V9/libc/stdio/getflds.c

Compare this file to the similar file:
Show the results in this format:

/*
 *	getflds - break a line into fields
 *
 *	General strategy: ptrs is a pointer to an array of
 *	character pointers.  This array is used to hold the
 *	results of each call to getflds.  Expandptrs() uses
 *	realloc to grow this array as needed.  Each pointer in
 *	the array points into the buffer addressed by str,
 *	so all the strings returned by getflds are really
 *	contiguous.  Expandstr() uses realloc to grow this buffer
 *	as needed; after it has done so it must relocate all
 *	the pointers into the buffer because it may have moved.
 *
 *	Both expandstrs and expandptrs return nonzero for failure.
 *
 *	Ptrs and str are initially null.  In the (unlikely)
 *	event that an attempt to grow one of them fails, the
 *	corresponding pointer is set back to null.
 */

#include <stdio.h>
#include <setjmp.h>

/* how many elements to expand str and ptrs at a time */
#define STRCHUNK 512
#define PTRCHUNK 128

char *malloc(), *realloc();
static char **ptrs = NULL, *str = NULL;
static unsigned ptrsize = 0, strsize = 0;

static
expandptrs()
{
	ptrsize += PTRCHUNK;
	ptrs = (char **) (ptrs? realloc (ptrs, ptrsize * sizeof (char *)):
				malloc (ptrsize * sizeof (char *)));
	if (ptrs == NULL) {
		ptrsize = 0;
		return 1;
	}
	return 0;
}

static
expandstr()
{
	register char *newstr;
	register unsigned n;

	strsize += STRCHUNK;
	newstr = str? realloc (str, strsize): malloc (strsize);
	if (newstr == NULL) {
		str = NULL;
		strsize = 0;
		return 1;
	}

	/* relocate the ptrs array */
	for (n = 0; n < ptrsize; n++)
		ptrs[n] = newstr + (ptrs[n] - str);

	str = newstr;
	return 0;
}

char **
getflds (f)
	register FILE *f;
{
	register int c;
	register unsigned nptr = 0, nstr = 0;

	/* try to read the first character of the line */
	c = getc (f);

	/* a line beginning with # is a comment */
	while (c == '#') {
		do c = getc (f);
		while (c != '\n' && c != EOF);
		if (c == '\n')
			c = getc (f);
	}

	/* EOF at the beginning of the line is real */
	if (c == EOF)
		return NULL;

	/* one iteration per field */
	do {
		/* 0 outside a quoted string, otherwise the type of quote */
		register quote = 0;

		/* skip leading white space */
		while (c == ' ' || c == '\t')
			c = getc (f);

		/* maybe it wan't a field after all */
		if (c == '\n' || c == EOF)
			break;

		/* make room for the field pointer and plant it */
		if (nptr >= ptrsize)
			if (expandptrs())
				return NULL;
		ptrs[nptr++] = str + nstr;

		/* one iteration per character or escape sequence */
		while ((c != ' ' && c != '\t' || quote) && c != '\n' && c != EOF) {

			/* check for escape sequence */
			if (c == '\\') {
				c = getc (f);
				switch (c) {

				/* C escapes for newline, etc. */
				case 'b': c = '\b'; break;
				case 'f': c = '\f'; break;
				case 'n': c = '\n'; break;
				case 'r': c = '\r'; break;
				case 't': c = '\t'; break;
				case 'v': c = '\v'; break;

				/* these characters stand for themselves */
				case '\'': case '"': case '#':
				case ' ': case '\t': case '\\':
					break;

				/* backslash, newline is ignored */
				case '\n':
					c = getc (f);
					continue;

				/* backslash, octal digits */
				case '0': case '1': case '2': case '3':
				case '4': case '5': case '6': case '7':
					{
						register r = c - '0';
						register n = 2;
						do {
							c = getc (f);
							if (c < '0' || c > '7') {
								ungetc (c, f);
								break;
							}
							r = (r<<3) + c - '0';
						} while (--n > 0);
						c = r;
					}
					break;

				/* else treat the backslash as an ordinary char */
				default:
					ungetc (c, f);
					c = '\\';
					break;
				}

			/* not a backslash, check for a quote */
			} else if (c == '\'' || c == '"') {

				/* beginning of a quoted string */
				if (!quote) {
					quote = c;
					c = getc (f);
					continue;

				/* end of a quoted string */
				} else if (c == quote) {
					quote = 0;
					c = getc (f);
					continue;
				}

				/*
				 * otherwise we're inside a quoted string
				 * and we've seen the "other" quote, which
				 * is now just an ordinary character.
				 */
			}

			/* we have a character; put it in the string */
			if (nstr >= strsize)
				if (expandstr())
					return NULL;
			str[nstr++] = c;
			c = getc (f);
		}

		/* end of field; plant a null char at the end of the string */
		if (nstr >= strsize)
			if (expandstr())
				return NULL;
		str[nstr++] = '\0';

	} while (c != '\n' && c != EOF);

	/* end of line; put a null pointer at the end of the array */
	if (nptr >= ptrsize)
		if (expandptrs())
			return NULL;
	ptrs[nptr++] = NULL;

	return ptrs;
}