diff options
Diffstat (limited to 'lib/libutil/fparseln.c')
| -rw-r--r-- | lib/libutil/fparseln.c | 233 | 
1 files changed, 233 insertions, 0 deletions
| diff --git a/lib/libutil/fparseln.c b/lib/libutil/fparseln.c new file mode 100644 index 000000000000..ba7c4b45f291 --- /dev/null +++ b/lib/libutil/fparseln.c @@ -0,0 +1,233 @@ +/*	$NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $	*/ + +/* + * Copyright (c) 1997 Christos Zoulas.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + *    derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <libutil.h> + +static int isescaped __P((const char *, const char *, int)); + +/* isescaped(): + *	Return true if the character in *p that belongs to a string + *	that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(sp, p, esc) +	const char *sp, *p; +	int esc; +{ +	const char     *cp; +	size_t		ne; + +#if 0 +	_DIAGASSERT(sp != NULL); +	_DIAGASSERT(p != NULL); +#endif + +	/* No escape character */ +	if (esc == '\0') +		return 1; + +	/* Count the number of escape characters that precede ours */ +	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) +		continue; + +	/* Return true if odd number of escape characters */ +	return (ne & 1) != 0; +} + + +/* fparseln(): + *	Read a line from a file parsing continuations ending in \ + *	and eliminating trailing newlines, or comments starting with + *	the comment char. + */ +char * +fparseln(fp, size, lineno, str, flags) +	FILE		*fp; +	size_t		*size; +	size_t		*lineno; +	const char	 str[3]; +	int		 flags; +{ +	static const char dstr[3] = { '\\', '\\', '#' }; + +	size_t	s, len; +	char   *buf; +	char   *ptr, *cp; +	int	cnt; +	char	esc, con, nl, com; + +#if 0 +	_DIAGASSERT(fp != NULL); +#endif + +	len = 0; +	buf = NULL; +	cnt = 1; + +	if (str == NULL) +		str = dstr; + +	esc = str[0]; +	con = str[1]; +	com = str[2]; +	/* +	 * XXX: it would be cool to be able to specify the newline character, +	 * but unfortunately, fgetln does not let us +	 */ +	nl  = '\n'; + +	while (cnt) { +		cnt = 0; + +		if (lineno) +			(*lineno)++; + +		if ((ptr = fgetln(fp, &s)) == NULL) +			break; + +		if (s && com) {		/* Check and eliminate comments */ +			for (cp = ptr; cp < ptr + s; cp++) +				if (*cp == com && !isescaped(ptr, cp, esc)) { +					s = cp - ptr; +					cnt = s == 0 && buf == NULL; +					break; +				} +		} + +		if (s && nl) { 		/* Check and eliminate newlines */ +			cp = &ptr[s - 1]; + +			if (*cp == nl) +				s--;	/* forget newline */ +		} + +		if (s && con) {		/* Check and eliminate continuations */ +			cp = &ptr[s - 1]; + +			if (*cp == con && !isescaped(ptr, cp, esc)) { +				s--;	/* forget escape */ +				cnt = 1; +			} +		} + +		if (s == 0 && buf != NULL) +			continue; + +		if ((cp = realloc(buf, len + s + 1)) == NULL) { +			free(buf); +			return NULL; +		} +		buf = cp; + +		(void) memcpy(buf + len, ptr, s); +		len += s; +		buf[len] = '\0'; +	} + +	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && +	    strchr(buf, esc) != NULL) { +		ptr = cp = buf; +		while (cp[0] != '\0') { +			int skipesc; + +			while (cp[0] != '\0' && cp[0] != esc) +				*ptr++ = *cp++; +			if (cp[0] == '\0' || cp[1] == '\0') +				break; + +			skipesc = 0; +			if (cp[1] == com) +				skipesc += (flags & FPARSELN_UNESCCOMM); +			if (cp[1] == con) +				skipesc += (flags & FPARSELN_UNESCCONT); +			if (cp[1] == esc) +				skipesc += (flags & FPARSELN_UNESCESC); +			if (cp[1] != com && cp[1] != con && cp[1] != esc) +				skipesc = (flags & FPARSELN_UNESCREST); + +			if (skipesc) +				cp++; +			else +				*ptr++ = *cp++; +			*ptr++ = *cp++; +		} +		*ptr = '\0'; +		len = strlen(buf); +	} + +	if (size) +		*size = len; +	return buf; +} + +#ifdef TEST + +int main __P((int, char **)); + +int +main(argc, argv) +	int argc; +	char **argv; +{ +	char   *ptr; +	size_t	size, line; + +	line = 0; +	while ((ptr = fparseln(stdin, &size, &line, NULL, +	    FPARSELN_UNESCALL)) != NULL) +		printf("line %d (%d) |%s|\n", line, size, ptr); +	return 0; +} + +/* + +# This is a test +line 1 +line 2 \ +line 3 # Comment +line 4 \# Not comment \\\\ + +# And a comment \ +line 5 \\\ +line 6 + +*/ + +#endif /* TEST */ | 
