ARPDAS_QNX6 1.0
|
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