/* * This module provides acccess to zero-terminated subfields within an * indexed file. When you open the file you include a list of field * names that are associated with consecutive substrings with each * record. This file implicitly includes access_db.h. * * The first record of the file has special meaning, it defines the * field lengths and names. All fixed fields (index keys) are at the * beginning of the record and consist of all blanks plus a nul-byte * separator. The first field not containing blanks is a comma-separated * list of the field names in the same order they will occur in the * record. Additional fields names after those that correspond to the * fixed fields are variable length fields. * * Author: David Jones * Date: 30-DEC-1995 * Revised: 18-JAN-1996 Added delete_rec function */ #include #include #include #include "subaccess.h" /*****************************************************************************/ /* * Define routines to access an indexed file by zero-terminated subfields * within the records. Caller is responsible for padding fixed fields. */ #include "access_db.h" /*****************************************************************************/ /* Open indexed file and initialize context for accessing it by field. * Return value is 1 on success and 0 on error. */ int subfield_open ( char *db_name, /* file name */ char *mode, int rec_limit, /* max record size. */ subfield_ctx ctx ) { int length, i, j, status, fixed_count, max_fixed, item_limit; size_t size_read; char *rec, *field_names; /* * open file and zero structure. */ if ( rec_limit <= 0 ) return 20; /* Bad parameter */ ctx->db = ifopen ( db_name, mode ); if ( !ctx->db ) return 0; /* open error */ ctx->item_count = 0; ctx->rec_limit = ctx->rec_size = 0; ctx->hdr_data[0] = '\0'; ctx->fld_names = ctx->outrec = (char *) 0; ctx->key = (char *) 0; /* * Read first record (sequentially). */ rec = ctx->rec = (char *) malloc ( 1+(sizeof(char) * rec_limit) ); if ( !ctx->rec ) { subfield_close ( ctx ); return 0; } ctx->rec_limit = rec_limit; rec[rec_limit] = '\0'; status = ifread_rec ( rec, (size_t) rec_limit, &size_read, ctx->db, 0, "", 0 ); /* * Locate the field list within the first record. */ field_names = ""; max_fixed = 0; for ( item_limit = fixed_count = i = 0; i < size_read; i++ ) { length = strlen ( &rec[i] ); if ( !length ) break; /* null string illegal */ if ( length > max_fixed ) max_fixed = length; if ( length == strspn(&rec[i]," ") ) { fixed_count++; i += length; /* skip remainder */ } else { /* * Substring is comma-separated list of names. */ field_names = ctx->fld_names = malloc ( length+1 ); if ( !ctx->fld_names ) { subfield_close ( ctx ); return 0; } strcpy ( ctx->fld_names, &rec[i] ); for ( item_limit = 1, j = 0; field_names[j]; j++ ) if ( field_names[j] == ',' ) item_limit++; i += length + 1; break; } } if ( item_limit <= 0 || fixed_count > item_limit ) { /* Invalid field definition */ subfield_close ( ctx ); return 0; } /* * Copy header data. */ if ( i < size_read ) { for ( j = 0; (j < 31) && i < size_read && rec[i]; j++ ) ctx->hdr_data[j] = rec[i++]; ctx->hdr_data[j] = '\0'; } /* * Allocate work areas. */ ctx->key = malloc ( max_fixed + 1 ); if ( !ctx->key ) { subfield_close ( ctx ); return 0; } ctx->fld = (struct subfield_item *) malloc (sizeof(struct subfield_item) * item_limit ); if ( !ctx->fld ) { subfield_close ( ctx ); return 0; } /* * Initialize the field definitions. */ length++; ctx->item_count = 0; for ( i = j = 0; j < length; j++ ) { if ( field_names[j] == ',' || !field_names[j] ) { field_names[j] = '\0'; /* terminate string */ ctx->fld[ctx->item_count].size = 0; ctx->fld[ctx->item_count].name = &ctx->fld_names[i]; ctx->fld[ctx->item_count].value = ""; ctx->item_count++; i = j + 1; } } if ( item_limit != ctx->item_count ) { printf("Consistent check failed on field count.\n"); return 0; } for ( i = j = 0; i < fixed_count; i++ ) { length = strlen ( &rec[j] ); ctx->fld[i].size = length; j += length + 1; } return 1; } /*****************************************************************************/ /* Close indexed file and cleanup. structure. */ int subfield_close ( subfield_ctx ctx ) { if ( ctx->db ) { /* * File open, close and free allocated subelements. */ ifclose ( ctx->db ); ctx->db = (IFILE *) 0; if ( ctx->item_count > 0 ) free ( ctx->fld ); if ( ctx->rec_limit > 0 ) free ( ctx->rec ); if ( ctx->fld_names ) free ( ctx->fld_names ); if ( ctx->key ) free ( ctx->key ); if ( ctx->outrec ) free ( ctx->outrec ); } return 1; } /*****************************************************************************/ /* * specify a key_size of zero for sequential read */ int subfield_read ( subfield_ctx ctx, char *key, int key_num, int key_size ) { int status, i, k, j; size_t size_read; char *rec; /* * Pad key to full size. */ if ( key_size > 0 ) { if ( key_num > ctx->item_count ) return 20; if ( key_size > ctx->fld[key_num].size ) return 20; /* too large */ strncpy ( ctx->key, key, key_size ); i = key_size; key_size = ctx->fld[key_num].size; while ( i < key_size ) ctx->key[i++] = ' '; ctx->key[i] = '\0'; key = ctx->key; } /* * Read record. */ ctx->rec_size = 0; rec = ctx->rec; status = ifread_rec ( rec, (size_t) ctx->rec_limit, &size_read, ctx->db, key_num, key, key_size ); if ( (status&1) == 1 ) { /* * Parse record into items. */ ctx->rec_size = size_read; for ( k = i = j = 0; j < size_read; j++ ) { if ( !rec[j] ) { ctx->fld[k++].value = &rec[i]; if ( k >= ctx->item_count ) break; /* got all */ i = j + 1; } } /* * Trim spaces from fixed fields. */ for ( i = 0; i < ctx->item_count; i++ ) { if ( ctx->fld[i].size == 0 ) break; for ( j = ctx->fld[i].size -1; j >= 0 && ctx->fld[i].value[j] == ' '; --j ); ctx->fld[i].value[j+1] = '\0'; } } else status = 0; return status; } /*****************************************************************************/ /* * Lookup field value in current record and return pointer to it. * Return null if name unknown. */ char *subfield_value ( subfield_ctx ctx, char *name ) { int i; for ( i = 0; i < ctx->item_count; i++ ) { if ( 0 == strcmp ( ctx->fld[i].name, name ) ) return ctx->fld[i].value; } return (char *) 0; /* unknown name */ } /*****************************************************************************/ /* * Record is built into contiguous area using field items. */ int subfield_set ( subfield_ctx ctx, char *name, char *value ) { int i; for ( i = 0; i < ctx->item_count; i++ ) { if ( 0 == strcmp ( ctx->fld[i].name, name ) ) { ctx->fld[i].value = value; return 1; } } return 0; /* unknown name */ } static int build_outrec ( subfield_ctx ctx, int *out_length ) { int i, j, vlen, length, status; if ( !ctx->outrec ) { ctx->outrec = malloc ( ctx->rec_limit ); if ( !ctx->outrec ) return 0; /* allocation error */ } for ( *out_length = length = i = 0; i < ctx->item_count; i++ ) { vlen = strlen ( ctx->fld[i].value ) + 1; if ( ctx->fld[i].size > 0 ) { /* fixed field, padd with spaces. */ if ( ctx->fld[i].value[0] == ' ' ) return 20; strncpy ( &ctx->outrec[length], ctx->fld[i].value, ctx->fld[i].size ); j = length + vlen - 1; length += ctx->fld[i].size + 1; while ( j < length ) ctx->outrec[j++] = ' '; ctx->outrec[j-1] = '\0'; } else if ( vlen + length > ctx->rec_limit ) { return 0; } else { strcpy ( &ctx->outrec[length], ctx->fld[i].value ); length += vlen; } } *out_length = length; return 1; } /*****************************************************************************/ /* Add/update file record. */ int subfield_write ( subfield_ctx ctx ) { int status, length; status = build_outrec ( ctx, &length ); if ( (status&1) == 1 ) status = ifwrite_rec ( ctx->outrec, (size_t) length, ctx->db ); return status; } int subfield_update ( subfield_ctx ctx ) { int status, length; status = build_outrec ( ctx, &length ); if ( (status&1) == 1 ) status = ifupdate_rec ( ctx->outrec, (size_t) length, ctx->db ); return status; } /*****************************************************************************/ /* delete file record. */ int subfield_delete_rec ( subfield_ctx ctx ) { int status, length; status = ifdelete_rec ( ctx->db ); return status; } /*****************************************************************************/ /* Speical update to the header record. Call this routine invalidates * the current read data. The header record is read again and parsed and * the old optional data, if present is saved. */ int subfield_update_header ( subfield_ctx ctx, int (*update_cb)(subfield_ctx) ) { int status, i, j, length, hdr_off; size_t size_read; char *field_names; /* * Read the header. */ for ( i = 0; i < ctx->fld[0].size; i++ ) ctx->key[i] = ' '; status = ifread_rec ( ctx->rec, (size_t) ctx->rec_limit, &size_read, ctx->db, 0, ctx->key, ctx->fld[0].size ); if ( (status&1) == 0 ) return status; /* * Locate the field list within the first record. */ field_names = (char *) 0; for ( hdr_off = i = 0; i < size_read; i++ ) { length = strlen ( &ctx->rec[i] ); if ( !length ) break; /* null string illegal */ if ( length == strspn(&ctx->rec[i]," ") ) { i += length; /* skip remainder */ } else { /* * Skip the field name list. */ i += length; hdr_off = i + 1; /* start of header. */ break; } } if ( hdr_off ) { /* Copy the old data */ length = strlen ( &ctx->rec[hdr_off] ); if ( length > 31 ) length = 31; strncpy ( ctx->hdr_data, &ctx->rec[hdr_off], length ); ctx->hdr_data[length] = '\0'; } else { /* * Caculate place where header should go. */ hdr_off = size_read; if ( ctx->rec[hdr_off-1] ) { /* terminator missing on field list */ ctx->rec[hdr_off++] = '\0'; } ctx->hdr_data[0] = '\0'; } /* * Call user routine to update the header data while record is locked. */ update_cb ( ctx ); /* * rewrite record. */ length = strlen ( ctx->hdr_data ); strcpy ( &ctx->rec[hdr_off], ctx->hdr_data ); status = ifupdate_rec ( ctx->rec, hdr_off + length + 1, ctx->db ); return status; }