ARPDAS_QNX6 1.0
mlf.c
Go to the documentation of this file.
00001 /* config string is of the form
00002   \w+/(\d\d+/(\d\d/(\d\d\.dat)?)?)?
00003   Or perhaps more precisely:
00004   <base>/
00005   <base>/dd+/
00006   <base>/dd+/dd/
00007   <base>/dd+/dd/dd.dat
00008 
00009   Todo:
00010     Separate out a type to hold a file as an n-tuple
00011     mlf_ntup_t *mlfn;
00012     mlf_ntup_t *mlf_convert_fname( mlf_def_t *mlf, const char *fname );
00013     mlf_set_ntup( mlf_def_t *mlf, mlf_ntup_t *mlfn );
00014     mlf_compare( mlf_def_t *mlf, mlf_ntup_t *mlfn );
00015 */
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <limits.h>
00019 #include <ctype.h>
00020 #include <sys/stat.h>
00021 #include "mlf.h"
00022 #include "nortlib.h"
00023 
00024 static char *mlf_strtok( char *buf, char *delset, char *delp ) {
00025   static char *bufp;
00026   int n;
00027   char *rbuf;
00028   
00029   if ( buf != NULL ) bufp = buf;
00030   n = strcspn( bufp, delset );
00031   *delp = bufp[n];
00032   bufp[n] = '\0';
00033   rbuf = bufp;
00034   bufp += n;
00035   if ( *delp ) bufp++;
00036   return rbuf;
00037 }
00038 
00039 /* Parses fname into an n-tuple.
00040   If fname == NULL or is empty, a valid zeroed n-tuple is
00041   returned. The n_tuple has n_levels+1 elements. The 0-th
00042   points to the BASE directory.
00043 */
00044 mlf_ntup_t *mlf_convert_fname( mlf_def_t *mlf, const char *fbase, const char *fname ) {
00045   mlf_ntup_t *mlfn;
00046   char *cfg;
00047   char *num, del;
00048   const char *s;
00049   int level;
00050 
00051   mlfn = new_memory( sizeof( mlf_ntup_t ) );
00052   mlfn->ntup = new_memory( (mlf->n_levels+1) * sizeof(int) );
00053   for ( level = 0; level <= mlf->n_levels; level++ )
00054     mlfn->ntup[level] = -1;
00055   mlfn->mlf = mlf;
00056   mlfn->base = fbase;
00057   mlfn->suffix = NULL;
00058   if ( fname == NULL || *fname == '\0' ) return mlfn;
00059 
00060   cfg = nl_strdup( fname );
00061   mlfn->base = mlf_strtok( cfg, "/", &del );
00062   for ( s = mlfn->base; *s; s++ )
00063     if ( ! isalnum(*s) && *s != '_' )
00064       nl_error( 3,
00065         "mlf_convert_fname: illegal char '%c' in base '%s'",
00066         *s, mlfn->base );
00067 
00068   for ( level = 0; level <= mlf->n_levels; level++ ) {
00069     num = mlf_strtok( NULL, level < mlf->n_levels ? "/" : "/.", &del );
00070     if ( num != NULL && *num != '\0' ) {
00071       char *end;
00072 
00073       if ( del == '/' && level == mlf->n_levels )
00074         nl_error( 3,
00075           "Too many directory levels specified", level );
00076       mlfn->ntup[level] = strtoul( num, &end, 10 );
00077       if ( *end != '\0' )
00078         nl_error( 3,
00079           "mlf_convert_fname: Subdir '%s' at level %d not numeric",
00080           num, level );
00081     }
00082   }
00083   if ( del == '.' ) {
00084     mlfn->suffix = mlf_strtok( NULL, "/", &del );
00085     for ( s = mlfn->suffix; *s; s++ )
00086       if ( ! isalnum(*s) && *s != '_' )
00087         nl_error( 3, "mlf_convert_fname: Illegal char in suffix" );
00088   }
00089 
00090   return mlfn;
00091 }
00092 
00093 void mlf_free_mlfn( mlf_ntup_t *mlfn ) {
00094   free( mlfn->ntup );
00095   free( mlfn );
00096 }
00097 
00098 /* Set the file path from an array of ints.
00099    Called from mlf_set_ntup() which parses a filename and
00100    mlf_set_index() which parses a long int.
00101  */
00102 static void mlf_set_ixs( mlf_def_t *mlf, int *ixs ) {
00103   mlf->index = 0;
00104   { int end, i;
00105   
00106     for ( i = 0; i <= mlf->n_levels; i++ ) {
00107       if (  mlf->flags & MLF_INITIALIZE ||
00108             mlf->flvl[i].index != ixs[i] ) {
00109         mlf->flvl[i].index = ixs[i];
00110         mlf->flags |= MLF_INITIALIZE;
00111         if ( mlf->flags & MLF_WRITING ) {
00112           struct stat buf;
00113           *(mlf->flvl[i].s) = '\0';
00114           if ( stat( mlf->fpath, &buf ) || ! S_ISDIR(buf.st_mode) ) {
00115             if ( mkdir( mlf->fpath, 0775 ) != 0 )
00116               nl_error( 3, "Unable to create directory %s", mlf->fpath );
00117           }
00118         }
00119         if ( ixs[i] < 0 ) {
00120           for ( ; i <= mlf->n_levels; i++ ) {
00121             mlf->flvl[i].index = -1;
00122             mlf->index *= mlf->n_files;
00123           }
00124           mlf->flags |= MLF_INC_FIRST;
00125           return;
00126         }
00127       }
00128       end = sprintf( mlf->flvl[i].s, "/%02d", mlf->flvl[i].index );
00129       if ( i < mlf->n_levels )
00130         mlf->flvl[i+1].s = mlf->flvl[i].s + end;
00131       if ( mlf->flvl[i].index < 0 )
00132         nl_error( 4, "Assertion failed: mlf->flvl[i].index < 0" );
00133       mlf->index =
00134         mlf->index * mlf->n_files + mlf->flvl[i].index;
00135     }
00136   }
00137   mlf->index++;
00138   mlf->flags &= ~(MLF_INC_FIRST | MLF_INITIALIZE);
00139   if ( mlf->flags & MLF_WRITING )
00140     mlf->flags |= MLF_INC_FIRST;
00141 }
00142 
00143 void mlf_set_index( mlf_def_t * mlf, unsigned long index ) {
00144   int ixs[MLF_MAX_LEVELS+1], i, nf = mlf->n_files;
00145   unsigned long ix = index > 0 ? index - 1 : 0;
00146   for ( i = mlf->n_levels; i > 0; i-- ) {
00147     ixs[i] = ix % nf;
00148     ix = ix/nf;
00149   }
00150   ixs[0] = ix;
00151   mlf_set_ixs( mlf, ixs );
00152   mlf->flags &= ~MLF_INC_FIRST;
00153 }
00154 /*
00155 =Name mlf_set_index(): Set next file by index
00156 =Subject Multi-level File Routines
00157 =Synopsis
00158 
00159   #include "mlf.h"
00160   void mlf_set_index( mlf_def_t * mlf, unsigned long index );
00161 
00162 =Description
00163   
00164   mlf_set_index() defines the next file to be read or written by
00165   index. Given the definitions in the mlf_def_t structure, there
00166   is a natural one-to-one mapping between the non-negative
00167   integers and the multi-level paths generated by these routines.
00168   If for some reason you know the index number of a file (because
00169   perhaps it was stored in telemetry in that form), you can
00170   retrieve the file by calling mlf_set_index() followed by
00171   =mlf_next_file=().
00172   
00173 =Returns
00174 
00175   Nothing.
00176 
00177 =SeeAlso
00178 
00179   =Multi-level File Routines=.
00180 
00181 =End  
00182 */
00183 
00184 /* mlf_set_ntup() copies an n-tuple into the mlf_def_t.
00185    If writing, makes sure the directories exist.
00186  */
00187 void mlf_set_ntup( mlf_def_t *mlf, mlf_ntup_t *mlfn ) {
00188   if ( mlfn->mlf != mlf )
00189     nl_error( 4, "mlf_set_ntup: Invalid n-tuple" );
00190   if ( mlfn->suffix != NULL )
00191     mlf->fsuffix = mlfn->suffix;
00192   if ( mlfn->base != NULL ) {
00193     int end = sprintf( mlf->fpath, "%s", mlfn->base );
00194     mlf->flvl[0].s = mlf->fpath + end;
00195   }
00196   mlf_set_ixs( mlf, mlfn->ntup );
00197 }
00198 
00199 mlf_def_t *mlf_init( int n_levels, int n_files, int writing,
00200     const char *fbase, const char *fsuffix, const char *config ) {
00201   mlf_def_t *mlf;
00202   mlf_ntup_t *mlfn;
00203 
00204   if ( n_levels < 1 || n_levels > MLF_MAX_LEVELS ) {
00205     nl_error( 3, "mlf_init: n_levels must be >= 1 and <= %d",
00206         MLF_MAX_LEVELS );
00207     return NULL;
00208   }
00209   if ( n_files < 2 ) {
00210     nl_error( 3, "mlf_init: n_files must be >= 2" );
00211     return NULL;
00212   }
00213   n_levels--;
00214   mlf = new_memory( sizeof(mlf_def_t) );
00215   if ( mlf )
00216     mlf->flvl = new_memory( (n_levels+1) * sizeof(mlf_elt_t) );
00217   if ( mlf == NULL || mlf->flvl == NULL ) return NULL;
00218   mlf->n_levels = n_levels;
00219   mlf->n_files = n_files;
00220   mlf->flags = ( writing ? MLF_WRITING : 0 ) | MLF_INITIALIZE;
00221   { int end = sprintf( mlf->fpath, "%s", "DATA" );
00222     mlf->flvl[0].s = mlf->fpath+end;
00223   }
00224   mlf->fsuffix = (fsuffix == NULL) ? "log" : nl_strdup(fsuffix);
00225   mlfn = mlf_convert_fname( mlf, fbase, config );
00226   mlf_set_ntup( mlf, mlfn );
00227   mlf_free_mlfn( mlfn );
00228   return mlf;
00229 }
00230 /*
00231 =Name mlf_init(): Initialize multi-level file operations
00232 =Subject Multi-level File Routines
00233 =Synopsis
00234 
00235   #include "mlf.h"
00236   mlf_def_t *mlf_init( int n_levels, int n_files, int writing,
00237       char *fbase, char *fsuffix, char *config );
00238 
00239 =Description
00240   
00241   The multi-level file routines are designed for efficiently
00242   storing a large number of sequential files. Most hierarchical
00243   file systems become seriously inefficient as the number of
00244   files in a single directory becomes large. This is due in large
00245   part to the fact that any search for a specific filename
00246   requires a linear search through the directory, and hence
00247   sequentially accessing each file in a directory is an order n^2
00248   operation.
00249   
00250   The MLF routines address this issue by using multiple directory
00251   levels to store sequential files. The number of levels and the
00252   number of entries at each level is configurable, depending on
00253   the total expected number of files.
00254 
00255   mlf_init() establishes the parameters for subsequent
00256   multi-level file operations. n_levels specifies how many levels
00257   of directories should be used. n_files specifies the number of
00258   files per directory. the writing argument should be non-zero
00259   for write operations, zero for read operations. fbase is the
00260   name of the first level directory. fsuffix is a suffix that is
00261   appended to each file. config is an optional string defining
00262   the first file to access. The base and suffix in the config
00263   string takes precedence over the fbase and fsuffix parameters.
00264   
00265   Generated file names are of the form:
00266 
00267   $fbase/\d\d+(/\d\d)* /\d\d\.$fsuffix
00268   
00269 =Returns
00270 
00271   mlf_init() returns a pointer to a dynamically allocated
00272   structure which holds the definitions. Most errors are syntax
00273   errors and are fatal, although they can be made non-fatal
00274   by manipulating =nl_response=.
00275 
00276 =SeeAlso
00277 
00278   =Multi-level File Routines=.
00279 
00280 =End  
00281 */
00282 
00283 /* returns < 0 if current file position preceeds mlfn,
00284    0 if equal, >0 if later
00285 */
00286 int mlf_compare( mlf_def_t *mlf, mlf_ntup_t *mlfn ) {
00287   int i, diff = 0;
00288   for ( i = 0; diff == 0 && i <= mlf->n_levels; i++ )
00289     diff = mlf->flvl[i].index - mlfn->ntup[i];
00290   if ( diff == 0 && ( mlf->flags & MLF_INC_FIRST ) == 0 )
00291     diff = -1;
00292   return diff;
00293 }
00294 
00295 static next_file( mlf_def_t *mlf, int level ) {
00296   if ( mlf->flags & MLF_INC_FIRST ) {
00297     int lvlidx;
00298     if ( level == mlf->n_levels ) mlf->index++;
00299     lvlidx = ++mlf->flvl[level].index;
00300     if ( level > 0 &&
00301          (( lvlidx == 0 && mlf->flvl[level-1].index < 0 ) ||
00302           ( lvlidx >= mlf->n_files ))) {
00303       mlf->flvl[level].index = 0;
00304       next_file( mlf, level-1 );
00305     }
00306   } else mlf->flags |= MLF_INC_FIRST;
00307   if ( level < mlf->n_levels ) {
00308     int n;
00309     
00310     n = sprintf( mlf->flvl[level].s, "/%02d", mlf->flvl[level].index );
00311     mlf->flvl[level+1].s = mlf->flvl[level].s + n;
00312     if ( mlf->flags & MLF_WRITING && mkdir( mlf->fpath, 0775 ) != 0 )
00313       nl_error( 2, "Unable to create directory %s", mlf->fpath );
00314   } else {
00315     sprintf( mlf->flvl[level].s, "/%02d.%s", mlf->flvl[level].index,
00316         mlf->fsuffix );
00317   }
00318 }
00319 
00320 FILE *mlf_next_file( mlf_def_t *mlf ) {
00321   FILE *fp;
00322   
00323   next_file( mlf, mlf->n_levels );
00324   fp = fopen( mlf->fpath, (mlf->flags & MLF_WRITING) ? "w" : "r" );
00325   if ( fp == 0 && nl_response > 0 )
00326     nl_error( 1, "Unable to open file '%s'", mlf->fpath );
00327   return fp;
00328 }
00329 
00330 int mlf_next_fd( mlf_def_t *mlf ) {
00331   int fd;
00332   next_file( mlf, mlf->n_levels );
00333   fd = open( mlf->fpath, (mlf->flags & MLF_WRITING) ? O_WRONLY : O_RDONLY );
00334   if ( fd == -1 && nl_response > 0 )
00335     nl_error( 1, "Unable to open file '%s'", mlf->fpath );
00336   return fd;
00337 }
00338 /*
00339 =Name mlf_next_file(): Open the next mlf file
00340 =Subject Multi-level File Routines
00341 =Synopsis
00342 
00343   #include "mlf.h"
00344   FILE *mlf_next_file( mlf_def_t *mlf );
00345 
00346 =Description
00347 
00348   Given an mlf definition, mlf_next_file() opens the next file in
00349   the sequence. After calling mlf_next_file(), the path of the
00350   open file is held in mlf->fpath, and the index is in
00351   mlf->index. (See =mlf_set_index=())
00352   
00353 =Returns
00354 
00355   A FILE pointer to the newly opened file or NULL if the file
00356   could not be opened.
00357   
00358 =SeeAlso
00359 
00360   =Multi-level File Routines=.
00361 
00362 =End  
00363 */
00364 
00365 /*
00366 =Name mlf_next_dir(): Open the next mlf directory
00367 =Subject Multi-level File Routines
00368 =Synopsis
00369 
00370   #include "mlf.h"
00371   int mlf_next_dir( mlf_def_t *mlf );
00372 
00373 =Description
00374 
00375   mlf_next_dir() is used instead of mlf_next_file() for
00376   applications that want sequentially numbered directories
00377   that will contain multiple files as opposed to
00378   sequentially numbered files.
00379 
00380   Given an mlf definition, mlf_next_dir() checks to make
00381   sure the next directory exists. If writing, the next
00382   directory will be created if it doesn't already exist.
00383   After calling mlf_next_dir(), the path of the
00384   new directory is held in mlf->fpath, and the index is in
00385   mlf->index. (See =mlf_set_index=())
00386   
00387 =Returns
00388 
00389   Returns 1 if the directory exists. If writing and the
00390   directory cannot be created, an error will be issued
00391   unless =nl_response= is zero.
00392   
00393 =SeeAlso
00394 
00395   =Multi-level File Routines=.
00396 
00397 =End  
00398 */
00399 
00400 /* Returns 1 if the directory exists. If we're writing,
00401    then we'll try to create it if it doesn't exist.
00402 */   
00403 int mlf_next_dir( mlf_def_t *mlf ) {
00404   struct stat buf;
00405   next_file( mlf, mlf->n_levels );
00406   if ( stat( mlf->fpath, &buf ) || ! S_ISDIR(buf.st_mode) ) {
00407     if ( mlf->flags & MLF_WRITING ) {
00408       if ( mkdir( mlf->fpath, 0775 ) == 0 ) return 1;
00409       if ( nl_response )
00410         nl_error( 2, "Unable to create directory %s", mlf->fpath );
00411     }
00412     return 0;
00413   }
00414   return 1;
00415 }
00416 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines