// ******************************************************************************** //
// **Creator:    Dazzle                Licence:      Diku Merc Rom Smaug Terms   ** //
// **Difficulty:    9                  Snippet:      File Handling               ** // 
// ******************************************************************************** //
// **                           Contact Information                              ** //
// **Yahoo:      ldevil.geo                 Msn:          ldevil@hotmail.com     ** //
// **Aim:        pocketweasle               Email:      sandstorm@arthmoor.com   ** //
// **Webpage:    http://sandstorm.arthmoor.com                                   ** //
// ******************************************************************************** //
// **Terms of Usage:                                                             ** //
// **Follow All the Licences listed above, also, if you have a snippet helpfile  ** //
// **Put my name in there, if not, leave my name in the source.                  ** //
// **Also, this code is given AS-IS, straight from my mud, so there will be some ** //
// **Effort required to make this work in your mud.                              ** //
// **Install at your own risk, if you have any comments or questions on this     ** //
// **Visit the website mentioned above, and enter the forum, post and bugs or    ** //
// **suggestions there.                                                          ** //
// ******************************************************************************** //


// *Okay, this is a GIANT expansion of my earlier FileOpen system released* //
// I have to give extra warning for this one, this will require intelligence to install
// I will provide all the required functions for this in this file just to save time
// However, you will have to make sure everything works yourself.
// This will also require allot of patch-work in all your file_opening functions
// so fread_string will require modifications, aswell as everything else.

// This code will now gzip and ungzip files, so FileOpen becomes the major lag of a system.
// Your mud will slow down greatly, however, you will use up allot less space, also, 
// If you use FileOpen_2 and FileClose_2, those files will not be compressed.
// FileOpen will check if the file is compressed, if it is, it ungzips it, if not, it just opens it.
// Rather simple process design.

// Slowing you down.  This system is Very much CPU intensive, especialy during bootup, but its 
// benefits are much worth the extra 30 seconds during copyover or on startup, you can save
// hundreds of mb in data, my mud without the system, was running close to 200mb, with the system
// we run at 70mb, thats with active pbase and lots of area's, it is a serious space-saver, and
// when you have limited space, its worth it.

// Benefits?  Saves on hard-drive space, allows you to find files that aren't closed, allows you
// to enhance your file_handling functions like fread_string, with the ability to list the name
// of the file when it bugs out, instead of just doing the blank thing it does.
// An example of this is:  nlogf( "Fread_string: EOF: %s", fp->file );  normaly it would just 
// do fread_string:EOF,
// With this system, you can enhance it any-way you want, and get the most out of data by
// Keeping track of all the information, logging it when necessary, it truly aids in the
// long run.

Now all of your functions like fread_string, fread_number, need to change from FILE * to FileData *
This makes the fp the FileData.

Inside of each function, you will want to add FILE *gfp = fp->fp;

And then change all referances to fp in said function to gfp, except the initial FileData *fp
So now all the getc(fp) would be getc(gfp) or whatever.

Now, You may want to go through all your file opening code, so the reading of notes, area's and such
and change all the FILE *'s to FileData *'s, you do not want to change FILE *'s that are used for
popens, they require FILE *'s and will cause bugs if you change them.

At the end of all this, your mud will keep track of all open files, and enable you to debug
possible file-corruptions, this will locate where files are opened, and not properly closed
it will also keep track of allot of data.

Replace all fopen's with FileOpen's

All fclose's with FileClose.

If you want files to not be compressed, use FileOpen_2 and FileClose_2


merc.h
typedef struct  file_info   		FileData;
struct file_info
{
	FileData *next;
	FileData *prev;
	FILE *fp;
	char *the_filename;

	// *Debugging Second-System* //
	char *mode;
	char *file;
	char *function;
	int   line;
	int   pos;
	int   size;
};

/* double-linked list handling macros -Thoric */
/* Updated by Scion 8/6/1999 */
#define Link(link, first, last, next, prev)                     	\
do                                                              	\
{                                                               	\
   if ( !(first) )								\
   {                                           				\
      (first) = (link);				                       	\
      (last) = (link);							    	\
   }											\
   else                                                      	\
      (last)->next = (link);			                       	\
   (link)->next = NULL;			                         	\
   if ((first) == (link))							\
      (link)->prev = NULL;							\
   else										\
      (link)->prev = (last);			                       	\
   (last) = (link);				                       	\
} while(0)

#define Unlink(link, first, last, next, prev)                   	\
do                                                              	\
{                                                               	\
	if ( !(link)->prev )							\
	{			                                    	\
         (first) = (link)->next;			                 	\
	   if ((first))							 	\
	      (first)->prev = NULL;						\
	} 										\
	else										\
	{                                                 		\
         (link)->prev->next = (link)->next;                 	\
	}										\
	if ( !(link)->next ) 							\
	{				                                    \
         (last) = (link)->prev;                 			\
	   if ((last))								\
	      (last)->next = NULL;						\
	} 										\
	else										\
	{                                                    		\
         (link)->next->prev = (link)->prev;                 	\
	}										\
} while(0)

#define Create(result, type, number)					\
do											\
{											\
    if (!((result) = (type *) calloc ((number), sizeof(type))))	\
    {											\
        perror("calloc failure");                                         \
	nlogf( "Calloc failure in function %s, File: %s:%d\n", __FUNCTION__, __FILE__, __LINE__ ); \
	abort();									\
    } \
} while(0)


#define Dispose(point)                         \
do                                             \
{                                              \
      if( (point) )                               \
      {                                           \
        free( (point) );                      \
      }	                                      \
      else \
      { \
	nlogf( "Disposing free pointer in function %s, File: %s:%d\n", __FUNCTION__, __FILE__, __LINE__ ); \
      } \
     (point) = NULL;                          \
  }                                           \
} while(0)


in the prototype section
void FileClose(FileData * fp);
void FileClose_2(FileData * fp);

FileData *__FileOpen(const char *file, const char *type, const char *the_filename, const char *function, int line);
FileData *__FileOpen_2(const char *file, const char *type, const char *the_filename, const char *function, int line);

#define FileOpen(the_file, type)  __FileOpen(the_file, type, __FILE__, __FUNCTION__, __LINE__)
#define FileOpen_2(the_file, type)  __FileOpen_2(the_file, type, __FILE__, __FUNCTION__, __LINE__)

#define MudCmd(name) void name(CharData *ch, char *argument)


interp.c/interp.h
Add do_fileio

This is a immortal command, so i'd drop it around L3 or higher.

FileHandler.c

/**************************************************************************************
 **************************************************************************************
 **                                                                                  **
 **      SSSSSSS  AAAA  NNNN  NN DDDDD    SSSSSSS TTTTTT OOOO  RRRRR  MMMM   MMMM    **
 **    SSSSSS    AA  AA NN NN NN DD  DD SSSSSS      TT  OO  OO RR  RR MM MM MM MM    **
 **      SSSSSS  AAAAAA NN NN NN DD  DD   SSSSSS    TT  OO  OO RRRRR  MM  MMM  MM    **
 **    SSSSSS    AA  AA NN  NNNN DDDDD  SSSSS       TT   OOOO  RR  RR MM       MM    **
 **                                                                                  **
 **************************************************************************************
 **************************************************************************************
 **        Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,           **
 **        Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe.       **
 **                                                                                  **
 **        Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael             **
 **        Chastain, Michael Quan, and Mitchell Tse.                                 **
 **************************************************************************************
 **************************************************************************************
 **        ROM 2.4 is copyright 1993-1998 Russ Taylor                                **
 **        ROM has been brought to you by the ROM consortium                         **
 **            Russ Taylor (rtaylor@hypercube.org)                                   **
 **            Gabrielle Taylor (gtaylor@hypercube.org)                              **
 **            Brian Moore (zump@rom.org)                                            **
 **        By using this code, you have agreed to follow the terms of the            **
 **        ROM license, in the file Rom24/doc/rom.license                            **
 **************************************************************************************/
// *Insert all your standard includes here.* //
#include "merc.h"

int FilesOpen;

FileData *first_file;
FileData *last_file;

FileData *new_filedata()
{
	FileData *file_data;
        Create( file_data, FileData, 1 );
        file_data->mode         = NULL;
        file_data->fp           = NULL;
        file_data->the_filename = NULL;
        file_data->file         = NULL;
        file_data->function     = NULL;
        file_data->line         = 0;
	return file_data;
}

void free_filedata( FileData *file_data)
{
         Dispose( file_data->the_filename );
         Dispose( file_data->mode );
         Dispose( file_data->function );
         Dispose( file_data->file );
	 // *Emergancy!* //
	 if(file_data->fp)
		fclose(file_data->fp);
         Dispose( file_data );
}

void unlink_file_data( void )
{
   FileData *file_data;

   while( ( file_data = last_file ) != NULL )
   {
      fprintf( stdout, "Found open file_data: %s  %s   %d", file_data->file, file_data->mode, file_data->line );
      Unlink( file_data, first_file, last_file, next, prev );
      free_filedata(file_data);
   }
}

// * Gzip the pfile  * //
void gzip_pfile( char *argument )
{
   char buf[LBUF * 2], bufik[100];
   FILE *fp;
   snprintf( bufik, 100, "gzip -fq %s", argument );

   fp = popen( bufik, "r" );
   fgetf( buf, LBUF * 2, fp );
   // nlogf("%s", buf);
   pclose( fp );
   return;
}

// * ungzip the playerfile.* //
void ungzip_pfile( char *argument )
{
   char buf[LBUF * 2], bufik[100];
   FILE *fp;
   snprintf( bufik, 100, "gzip -dfq %s", argument );
   fp = popen( bufik, "r" );
   fgetf( buf, LBUF * 2, fp );
   // nlogf("%s", buf);

   pclose( fp );
   return;
}

// *Remove the .gz from a string.* //
char *smash_gz( const char *str )
{
   static char strnew[LBUF];
   int i = 0;

   strnew[0] = '\0';

   for( ; *str != '\0'; str++ )
   {
      if( *str == '.' )
      {
         str++;
         if( *str == 'g' )
         {
            strnew[i] = '\0';
            return strnew;
         }
         else
         {
            str--;
            strnew[i] = *str;
         }
      }
      else
         strnew[i] = *str;

      i++;
   }
   strnew[i] = '\0'; // Make the last char null!
   return strnew;
}

// *Detail how big the file is, good to know :)* //
void stat_pfile( const char *strsave, char *name )
{
   struct stat bufty;
   // *Grab file-size* //
   if( stat( strsave, &bufty ) == -1 || !S_ISREG( bufty.st_mode ) )
   {
      nlogf( "Decompressing %s:", name );
   }
   else
   {
      nlogf( "Decompressing %s: %ldkb", name, bufty.st_size / 1024 );
   }
}

FileData *__FileOpen( const char *file, const char *type, const char *the_filename, const char *function, int line )
{
   if( NullString( file ) )
      return NULL;
   if( NullString( type ) )
      return NULL;

   // Check too long!
   if( strlen( file ) < 250 && strlen( type ) < 250 )
   {
      char file_name[SBUF];
      FileData *file_data;
      snprintf( file_name, SBUF, "%s.gz", file );
      if( !IsExactName( file_name, ( char * )file ) )
      {
         if( exists_file( file_name ) )
         {
            ungzip_pfile( file_name );
         }
      }

      file_data = new_filedata();
      file_data->mode = StrDup( type );
      file_data->fp = fopen( file, type );
      file_data->the_filename = StrDup( file );
      file_data->file = StrDup( the_filename );
      file_data->function = StrDup( function );
      file_data->line = line;
      Link( file_data, first_file, last_file, next, prev );
      FilesOpen++;
      if( file_data->fp == NULL )
      {
         Unlink( file_data, first_file, last_file, next, prev );
 	 free_filedata(file_data);
         FilesOpen--;
         return NULL;
      }
      else
         return file_data;
   }

   return NULL;
}

FileData *__FileOpen_2( const char *file, const char *type, const char *the_filename, const char *function, int line )
{
   if( NullString( file ) )
      return NULL;
   if( NullString( type ) )
      return NULL;

   // Check too long!
   if( strlen( file ) < 250 && strlen( type ) < 250 )
   {
      FileData *file_data;
      file_data = new_filedata();
      file_data->mode = StrDup( type );
      file_data->the_filename = StrDup( file );
      file_data->fp = fopen( file, type );
      file_data->file = StrDup( the_filename );
      file_data->function = StrDup( function );
      file_data->line = line;

      Link( file_data, first_file, last_file, next, prev );
      FilesOpen++;
      if( file_data->fp == NULL )
      {
         Unlink( file_data, first_file, last_file, next, prev );
	 free_filedata(file_data);
         FilesOpen--;
         return NULL;
      }
      else
         return file_data;
   }

   return NULL;
}

/*
 * this function closes the stream specified, and then gzips the saved file
 */
void FileClose( FileData * fp )
{
   if( fp )
   {
      char path[500];

      snprintf( path, 500, "%s", fp->the_filename );

      if( fp->fp )
         fclose( fp->fp );
      else
         nlogf( "FileClose: Filedata->fp is NULL!" );
      fp->fp = NULL;

      Unlink( fp, first_file, last_file, next, prev );
      free_filedata(fp);

      gzip_pfile( path );

      FilesOpen--;
   }
   else
   {
      nlogf( "FileClose: Null File_Data!\n" );
   }
   // *Nullify the FP, just for safety* //
   fp = NULL;

   // *Uh Oh? FilesOpen is below 0.. This = Evil * //
   if( FilesOpen < 0 )
   {
      nlogf( "FilesOpen is below 0, this means something closed a null FP!" );
      FilesOpen = 0; // Just for-fun ;)
   }

   return;
}

/*
 * this function closes the stream specified, and attempts to re-open
 * fpReserve, if it's not already open.
 */
void FileClose_2( FileData * fp )
{
   if( fp )
   {
      if( fp->fp )
         fclose( fp->fp );
      else
         nlogf( "FileClose_2: Filedata->fp is NULL!" );

      fp->fp = NULL;
      Unlink( fp, first_file, last_file, next, prev );
      free_filedata(fp);
      FilesOpen--;
   }
   else
   {
      nlogf( "FileClose_2: Null File_Data!\n" );
   }
   // *bye bye file_data* //
   // *Nullify the FP, just for safety* //
   fp = NULL;

   // *Uh Oh? FilesOpen is below 0.. This = Evil * //
   if( FilesOpen < 0 )
   {
      nlogf( "FilesOpen is below 0, this means something closed a null FP!" );
      FilesOpen = 0; // Just for-fun ;)
   }

   return;
}

// *Find out if a file exists.. This is from afkmud, not sure who wrote it though :P* //
bool exists_file( const char *name )
{
   struct stat fst;

   /*
    * Stands to reason that if there ain't a name to look at, it damn well don't exist! 
    */
   if( !name || name[0] == '\0' || !StrCmp( name, "" ) )
      return false;

   if( stat( name, &fst ) != -1 )
      return true;
   else
      return false;
}

MudCmd( do_fileio )
{
   BUFFER *output;
   FileData *file_data;
   int count = 0;

   output = NewBuf(  );

   // *Egads!* //
   BufPrintf( output, "%-25s   ::   %-15s   ::   %-15s   ::   %-15s   ::   %-15s\n\r", "Filename", "File Mode", "Where",
              "Function", "Line" );

   // *Parse the loop* //
   for( file_data = first_file; file_data; file_data = file_data->next )
   {
      BufPrintf( output, "%-25s   ::   %-15s   ::   %-15s   ::   %-15s   ::   %-15d\n\r", file_data->the_filename,
                 file_data->mode, file_data->file, file_data->function, file_data->line );
      count++;
   }

   BufPrintf( output, "\n\r" );
   BufPrintf( output, "There are %d open files.\n\r", count );

   // *Fix this here.* //
   FilesOpen = count;
   page_to_char( buf_string( output ), ch );
   free_buf( output );
}



// *If you do not have this, add it* //
void BufPrintf( BUFFER * buffer, char *fmt, ... )
{
   char buf[LBUF];
   va_list args;

   buf[0] = '\0';

   va_start( args, fmt );
   vsnprintf( buf, LBUF, fmt, args );
   va_end( args );

   AddBuf( buffer, buf );
   return;
}



// *That should be it, this is a big system, so any problems, comments, questions, please* //
// *connect to http://sandstorm.arthmoor.com/mercuryboard/  setup an account, and post questions *//
// *comments, bug reports, whatever, i respond to all my snippets there.* //