| #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 <stdio.h> |
| #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); |
| } |