154820Speter/*	$NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $	*/
254820Speter
354820Speter/*
454820Speter * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
554820Speter *
654820Speter * Redistribution and use in source and binary forms, with or without
754820Speter * modification, are permitted provided that the following conditions
854820Speter * are met:
954820Speter * 1. Redistributions of source code must retain the above copyright
1054820Speter *    notice, this list of conditions and the following disclaimer.
1154820Speter * 2. Redistributions in binary form must reproduce the above copyright
1254820Speter *    notice, this list of conditions and the following disclaimer in the
1354820Speter *    documentation and/or other materials provided with the distribution.
1454820Speter * 3. All advertising materials mentioning features or use of this software
1554820Speter *    must display the following acknowledgement:
1654820Speter *	This product includes software developed by Christos Zoulas.
1754820Speter * 4. The name of the author may not be used to endorse or promote products
1854820Speter *    derived from this software without specific prior written permission.
1954820Speter *
2054820Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2154820Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2254820Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2354820Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2454820Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2554820Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2654820Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2754820Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2854820Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2954820Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3054820Speter */
3154820Speter
3254820Speter#include <sys/cdefs.h>
3384225Sdillon__FBSDID("$FreeBSD$");
3454820Speter
3555227Speter#include <sys/types.h>
3654820Speter#include <assert.h>
3754820Speter#include <errno.h>
3854820Speter#include <stdio.h>
3954820Speter#include <string.h>
4054820Speter#include <stdlib.h>
4155227Speter#include <libutil.h>
4254820Speter
4392917Sobrienstatic int isescaped(const char *, const char *, int);
4454820Speter
4554820Speter/* isescaped():
4654820Speter *	Return true if the character in *p that belongs to a string
4754820Speter *	that starts in *sp, is escaped by the escape character esc.
4854820Speter */
4954820Speterstatic int
50121193Smarkmisescaped(const char *sp, const char *p, int esc)
5154820Speter{
5254820Speter	const char     *cp;
5354820Speter	size_t		ne;
5454820Speter
5555227Speter#if 0
5654820Speter	_DIAGASSERT(sp != NULL);
5754820Speter	_DIAGASSERT(p != NULL);
5855227Speter#endif
5954820Speter
6054820Speter	/* No escape character */
6154820Speter	if (esc == '\0')
6254820Speter		return 1;
6354820Speter
6454820Speter	/* Count the number of escape characters that precede ours */
6554820Speter	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
6654820Speter		continue;
6754820Speter
6854820Speter	/* Return true if odd number of escape characters */
6954820Speter	return (ne & 1) != 0;
7054820Speter}
7154820Speter
7254820Speter
7354820Speter/* fparseln():
7454820Speter *	Read a line from a file parsing continuations ending in \
7554820Speter *	and eliminating trailing newlines, or comments starting with
7654820Speter *	the comment char.
7754820Speter */
7854820Speterchar *
79121193Smarkmfparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
8054820Speter{
8154820Speter	static const char dstr[3] = { '\\', '\\', '#' };
8254820Speter
8354820Speter	size_t	s, len;
8454820Speter	char   *buf;
8554820Speter	char   *ptr, *cp;
8654820Speter	int	cnt;
8754820Speter	char	esc, con, nl, com;
8854820Speter
8955227Speter#if 0
9054820Speter	_DIAGASSERT(fp != NULL);
9155227Speter#endif
9254820Speter
9354820Speter	len = 0;
9454820Speter	buf = NULL;
9554820Speter	cnt = 1;
9654820Speter
9754820Speter	if (str == NULL)
9854820Speter		str = dstr;
9954820Speter
10054820Speter	esc = str[0];
10154820Speter	con = str[1];
10254820Speter	com = str[2];
10354820Speter	/*
10454820Speter	 * XXX: it would be cool to be able to specify the newline character,
10554820Speter	 * but unfortunately, fgetln does not let us
10654820Speter	 */
10754820Speter	nl  = '\n';
10854820Speter
10954820Speter	while (cnt) {
11054820Speter		cnt = 0;
11154820Speter
11254820Speter		if (lineno)
11354820Speter			(*lineno)++;
11454820Speter
11554820Speter		if ((ptr = fgetln(fp, &s)) == NULL)
11654820Speter			break;
11754820Speter
11854820Speter		if (s && com) {		/* Check and eliminate comments */
11954820Speter			for (cp = ptr; cp < ptr + s; cp++)
12054820Speter				if (*cp == com && !isescaped(ptr, cp, esc)) {
12154820Speter					s = cp - ptr;
12254820Speter					cnt = s == 0 && buf == NULL;
12354820Speter					break;
12454820Speter				}
12554820Speter		}
12654820Speter
12754820Speter		if (s && nl) { 		/* Check and eliminate newlines */
12854820Speter			cp = &ptr[s - 1];
12954820Speter
13054820Speter			if (*cp == nl)
13154820Speter				s--;	/* forget newline */
13254820Speter		}
13354820Speter
13454820Speter		if (s && con) {		/* Check and eliminate continuations */
13554820Speter			cp = &ptr[s - 1];
13654820Speter
13754820Speter			if (*cp == con && !isescaped(ptr, cp, esc)) {
13854820Speter				s--;	/* forget escape */
13954820Speter				cnt = 1;
14054820Speter			}
14154820Speter		}
14254820Speter
14354820Speter		if (s == 0 && buf != NULL)
14454820Speter			continue;
14554820Speter
14654820Speter		if ((cp = realloc(buf, len + s + 1)) == NULL) {
14754820Speter			free(buf);
14854820Speter			return NULL;
14954820Speter		}
15054820Speter		buf = cp;
15154820Speter
15254820Speter		(void) memcpy(buf + len, ptr, s);
15354820Speter		len += s;
15454820Speter		buf[len] = '\0';
15554820Speter	}
15654820Speter
15754820Speter	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
15854820Speter	    strchr(buf, esc) != NULL) {
15954820Speter		ptr = cp = buf;
16054820Speter		while (cp[0] != '\0') {
16154820Speter			int skipesc;
16254820Speter
16354820Speter			while (cp[0] != '\0' && cp[0] != esc)
16454820Speter				*ptr++ = *cp++;
16554820Speter			if (cp[0] == '\0' || cp[1] == '\0')
16654820Speter				break;
16754820Speter
16854820Speter			skipesc = 0;
16954820Speter			if (cp[1] == com)
17054820Speter				skipesc += (flags & FPARSELN_UNESCCOMM);
17154820Speter			if (cp[1] == con)
17254820Speter				skipesc += (flags & FPARSELN_UNESCCONT);
17354820Speter			if (cp[1] == esc)
17454820Speter				skipesc += (flags & FPARSELN_UNESCESC);
17554820Speter			if (cp[1] != com && cp[1] != con && cp[1] != esc)
17654820Speter				skipesc = (flags & FPARSELN_UNESCREST);
17754820Speter
17854820Speter			if (skipesc)
17954820Speter				cp++;
18054820Speter			else
18154820Speter				*ptr++ = *cp++;
18254820Speter			*ptr++ = *cp++;
18354820Speter		}
18454820Speter		*ptr = '\0';
18554820Speter		len = strlen(buf);
18654820Speter	}
18754820Speter
18854820Speter	if (size)
18954820Speter		*size = len;
19054820Speter	return buf;
19154820Speter}
19254820Speter
19354820Speter#ifdef TEST
19454820Speter
19554820Speterint
196121193Smarkmmain(int argc, char *argv[])
19754820Speter{
19854820Speter	char   *ptr;
19954820Speter	size_t	size, line;
20054820Speter
20154820Speter	line = 0;
20254820Speter	while ((ptr = fparseln(stdin, &size, &line, NULL,
20354820Speter	    FPARSELN_UNESCALL)) != NULL)
20454820Speter		printf("line %d (%d) |%s|\n", line, size, ptr);
20554820Speter	return 0;
20654820Speter}
20754820Speter
20854820Speter/*
20954820Speter
21054820Speter# This is a test
21154820Speterline 1
21254820Speterline 2 \
21354820Speterline 3 # Comment
21454820Speterline 4 \# Not comment \\\\
21554820Speter
21654820Speter# And a comment \
21754820Speterline 5 \\\
21854820Speterline 6
21954820Speter
22054820Speter*/
22154820Speter
22254820Speter#endif /* TEST */
223