#ifndef lint static char Rcs_Id[] = "$Id: fields.c,v 1.7 1994/01/06 05:26:37 geoff Exp $"; #endif /* * $Log: fields.c,v $ * Revision 1.7 1994/01/06 05:26:37 geoff * Get rid of all references to System V string routines, for portability * (sigh). * * Revision 1.6 1994/01/05 20:13:43 geoff * Add the maxf parameter * * Revision 1.5 1994/01/04 02:40:21 geoff * Make the increments settable (field_line_inc and field_field_inc). * Add support for the FLD_NOSHRINK flag. * * Revision 1.4 1993/09/27 17:48:02 geoff * Fix some lint complaints and some parenthesization errors. * * Revision 1.3 1993/09/09 01:11:11 geoff * Add a return value to fieldwrite. Add support for backquotes and for * unstripped backslashes. * * Revision 1.2 1993/08/26 00:02:50 geoff * Fix a stupid null-pointer bug * * Revision 1.1 1993/08/25 21:32:05 geoff * Initial revision * */ #include #include "config.h" #include "fields.h" field_t * fieldread P ((FILE * file, char * delims, int flags, int maxf)); /* Read a line with fields from a file */ field_t * fieldmake P ((char * line, int allocated, char * delims, int flags, int maxf)); /* Make a field structure from a line */ static field_t * fieldparse P ((field_t * fieldp, char * line, char * delims, int flags, int maxf)); /* Parse the fields in a line */ static int fieldbackch P ((char * str, char ** out, int strip)); /* Process backslash sequences */ int fieldwrite P ((FILE * file, field_t * fieldp, int delim)); /* Write a line with fields to a file */ void fieldfree P ((field_t * fieldp)); /* Free a field returned by fieldread */ unsigned int field_field_inc = 20; /* Increment to increase # fields by */ unsigned int field_line_inc = 512; /* Incr to increase line length by */ #ifndef USG #define strchr index #endif /* USG */ extern void free (); extern char * malloc (); extern char * realloc (); extern char * strchr (); extern int strlen (); /* * Read one line of the given file into a buffer, break it up into * fields, and return them to the caller. The field_t structure * returned must eventually be freed with fieldfree. */ field_t * fieldread (file, delims, flags, maxf) FILE * file; /* File to read lines from */ char * delims; /* Characters to use for field delimiters */ int flags; /* Option flags; see fields.h */ int maxf; /* Maximum number of fields to parse */ { register char * linebuf; /* Buffer to hold the line read in */ int linemax; /* Maximum line buffer size */ int linesize; /* Current line buffer size */ linebuf = (char *) malloc (field_line_inc); if (linebuf == NULL) return NULL; linemax = field_line_inc; linesize = 0; /* * Read in the line. */ while (fgets (&linebuf[linesize], linemax - linesize, file) != NULL) { linesize += strlen (&linebuf[linesize]); if (linebuf[linesize - 1] == '\n') break; else { linemax += field_line_inc; linebuf = (char *) realloc (linebuf, linemax); if (linebuf == NULL) return NULL; } } if (linesize == 0) { free (linebuf); return NULL; } return fieldmake (linebuf, 1, delims, flags, maxf); } field_t * fieldmake (line, allocated, delims, flags, maxf) char * line; /* Line to make into a field structure */ int allocated; /* NZ if line allocated with malloc */ char * delims; /* Characters to use for field delimiters */ int flags; /* Option flags; see fields.h */ int maxf; /* Maximum number of fields to parse */ { register field_t * fieldp; /* Structure describing the fields */ int linesize; /* Current line buffer size */ fieldp = (field_t *) malloc (sizeof (field_t)); if (fieldp == NULL) return NULL; fieldp->nfields = 0; fieldp->linebuf = allocated ? line : NULL; fieldp->fields = NULL; fieldp->hadnl = 0; linesize = strlen (line); if (line[linesize - 1] == '\n') { line[--linesize] = '\0'; fieldp->hadnl = 1; } /* * Shrink the line buffer if necessary. */ if (allocated && (flags & FLD_NOSHRINK) == 0) { line = fieldp->linebuf = (char *) realloc (fieldp->linebuf, linesize + 1); if (fieldp->linebuf == NULL) { fieldfree (fieldp); return NULL; } } return fieldparse (fieldp, line, delims, flags, maxf); } static field_t * fieldparse (fieldp, line, delims, flags, maxf) register field_t * fieldp; /* Field structure to parse into */ register char * line; /* Line to be parsed */ char * delims; /* Characters to use for field delimiters */ int flags; /* Option flags; see fields.h */ int maxf; /* Maximum number of fields to parse */ { int fieldmax; /* Max size of fields array */ char * lineout; /* Where to store xlated char in line */ char quote; /* Quote character in use */ fieldp->nfields = 0; fieldmax = (maxf != 0 && maxf < field_field_inc) ? maxf + 2 : field_field_inc; fieldp->fields = (char **) malloc (fieldmax * sizeof (char *)); if (fieldp->fields == NULL) { fieldfree (fieldp); return NULL; } if ((flags & (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES)) == FLD_SHQUOTES) flags |= FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES; while (1) { if (flags & FLD_RUNS) { while (*line != '\0' && strchr (delims, *line) != NULL) line++; /* Skip runs of delimiters */ if (*line == '\0') break; } fieldp->fields[fieldp->nfields] = lineout = line; /* * Skip to the next delimiter. At the end of skipping, "line" will * point to either a delimiter or a null byte. */ if (flags & (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES | FLD_BACKSLASH)) { while (*line != '\0') { if (strchr (delims, *line) != NULL) break; else if (((flags & FLD_SNGLQUOTES) && *line == '\'') || ((flags & FLD_BACKQUOTES) && *line == '`') || ((flags & FLD_DBLQUOTES) && *line == '"')) { if ((flags & FLD_SHQUOTES) == 0 && line != fieldp->fields[fieldp->nfields]) quote = '\0'; else quote = *line; } else quote = '\0'; if (quote == '\0') { if (*line == '\\' && (flags & FLD_BACKSLASH)) { line++; if (*line == '\0') break; line += fieldbackch (line, &lineout, flags & FLD_STRIPQUOTES); } else *lineout++ = *line++; } else { /* Process quoted string */ if ((flags & FLD_STRIPQUOTES) == 0) *lineout++ = quote; ++line; while (*line != '\0') { if (*line == quote) { if ((flags & FLD_STRIPQUOTES) == 0) *lineout++ = quote; line++; /* Go on past quote */ if ((flags & FLD_SHQUOTES) == 0) { while (*line != '\0' && strchr (delims, *line) == NULL) line++; /* Skip to delimiter */ } break; } else if (*line == '\\') { if (flags & FLD_BACKSLASH) { line++; if (*line == '\0') break; else line += fieldbackch (line, &lineout, flags & FLD_STRIPQUOTES); } else { *lineout++ = '\\'; if (*++line == '\0') break; *lineout++ = *line; } } else *lineout++ = *line++; } } } } else { while (*line != '\0' && strchr (delims, *line) == NULL) line++; /* Skip to delimiter */ lineout = line; } fieldp->nfields++; if (*line++ == '\0') break; if (maxf != 0 && fieldp->nfields > maxf) break; *lineout = '\0'; if (fieldp->nfields >= fieldmax) { fieldmax += field_field_inc; fieldp->fields = (char **) realloc (fieldp->fields, fieldmax * sizeof (char *)); if (fieldp->fields == NULL) { fieldfree (fieldp); return NULL; } } } /* * Shrink the field pointers and return the field structure. */ if ((flags & FLD_NOSHRINK) == 0 && fieldp->nfields >= fieldmax) { fieldp->fields = (char **) realloc (fieldp->fields, (fieldp->nfields + 1) * sizeof (char *)); if (fieldp->fields == NULL) { fieldfree (fieldp); return NULL; } } fieldp->fields[fieldp->nfields] = NULL; return fieldp; } static int fieldbackch (str, out, strip) register char * str; /* First char of backslash sequence */ register char ** out; /* Where to store result */ int strip; /* NZ to convert the sequence */ { register int ch; /* Character being developed */ char * origstr; /* Original value of str */ if (!strip) { *(*out)++ = '\\'; if (*str != 'x' && *str != 'X' && (*str < '0' || *str > '7')) { *(*out)++ = *str; return *str != '\0'; } } switch (*str) { case '\0': *(*out)++ = '\0'; return 0; case 'a': *(*out)++ = '\007'; return 1; case 'b': *(*out)++ = '\b'; return 1; case 'f': *(*out)++ = '\f'; return 1; case 'n': *(*out)++ = '\n'; return 1; case 'r': *(*out)++ = '\r'; return 1; case 'v': *(*out)++ = '\v'; return 1; case 'X': case 'x': /* Hexadecimal sequence */ origstr = str++; ch = 0; if (*str >= '0' && *str <= '9') ch = *str++ - '0'; else if (*str >= 'a' && *str <= 'f') ch = *str++ - 'a' + 0xa; else if (*str >= 'A' && *str <= 'F') ch = *str++ - 'A' + 0xa; if (*str >= '0' && *str <= '9') ch = (ch << 4) | (*str++ - '0'); else if (*str >= 'a' && *str <= 'f') ch = (ch << 4) | (*str++ - 'a' + 0xa); else if (*str >= 'A' && *str <= 'F') ch = (ch << 4) | (*str++ - 'A' + 0xa); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* Octal sequence */ origstr = str; ch = *str++ - '0'; if (*str >= '0' && *str <= '7') ch = (ch << 3) | (*str++ - '0'); if (*str >= '0' && *str <= '7') ch = (ch << 3) | (*str++ - '0'); break; default: *(*out)++ = *str; return 1; } if (strip) { *(*out)++ = ch; return str - origstr; } else { for (ch = 0; origstr < str; ch++) *(*out)++ = *origstr++; return ch; } } int fieldwrite (file, fieldp, delim) FILE * file; /* File to write to */ register field_t * fieldp; /* Field structure to write */ int delim; /* Delimiter to place between fields */ { int error; /* NZ if an error occurs */ register int fieldno; /* Number of field being written */ error = 0; for (fieldno = 0; fieldno < fieldp->nfields; fieldno++) { if (fieldno != 0) error |= putc (delim, file) == EOF; error |= fputs (fieldp->fields[fieldno], file) == EOF; } if (fieldp->hadnl) error |= putc ('\n', file) == EOF; return error; } void fieldfree (fieldp) register field_t * fieldp; /* Field structure to free */ { if (fieldp == NULL) return; if (fieldp->linebuf != NULL) free ((char *) fieldp->linebuf); if (fieldp->fields != NULL) free ((char *) fieldp->fields); free ((char *) fieldp); }