blob: 63cdc030a1ff12ce26e2d878976462a91f2ba505 [file] [log] [blame]
#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);
}