1270031Spfg/*	$NetBSD: fparseln.c,v 1.7 2007/03/08 19:57:53 drochner 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')
62270031Spfg		return 0;
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)) {
138270031Spfg				s--;	/* forget continuation char */
13954820Speter				cnt = 1;
14054820Speter			}
14154820Speter		}
14254820Speter
143270031Spfg		if (s == 0) {
144270031Spfg			/*
145270031Spfg			 * nothing to add, skip realloc except in case
146270031Spfg			 * we need a minimal buf to return an empty line
147270031Spfg			 */
148270031Spfg			if (cnt || buf != NULL)
149270031Spfg				continue;
150270031Spfg		}
15154820Speter
15254820Speter		if ((cp = realloc(buf, len + s + 1)) == NULL) {
15354820Speter			free(buf);
15454820Speter			return NULL;
15554820Speter		}
15654820Speter		buf = cp;
15754820Speter
15854820Speter		(void) memcpy(buf + len, ptr, s);
15954820Speter		len += s;
16054820Speter		buf[len] = '\0';
16154820Speter	}
16254820Speter
16354820Speter	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
16454820Speter	    strchr(buf, esc) != NULL) {
16554820Speter		ptr = cp = buf;
16654820Speter		while (cp[0] != '\0') {
16754820Speter			int skipesc;
16854820Speter
16954820Speter			while (cp[0] != '\0' && cp[0] != esc)
17054820Speter				*ptr++ = *cp++;
17154820Speter			if (cp[0] == '\0' || cp[1] == '\0')
17254820Speter				break;
17354820Speter
17454820Speter			skipesc = 0;
17554820Speter			if (cp[1] == com)
17654820Speter				skipesc += (flags & FPARSELN_UNESCCOMM);
17754820Speter			if (cp[1] == con)
17854820Speter				skipesc += (flags & FPARSELN_UNESCCONT);
17954820Speter			if (cp[1] == esc)
18054820Speter				skipesc += (flags & FPARSELN_UNESCESC);
18154820Speter			if (cp[1] != com && cp[1] != con && cp[1] != esc)
18254820Speter				skipesc = (flags & FPARSELN_UNESCREST);
18354820Speter
18454820Speter			if (skipesc)
18554820Speter				cp++;
18654820Speter			else
18754820Speter				*ptr++ = *cp++;
18854820Speter			*ptr++ = *cp++;
18954820Speter		}
19054820Speter		*ptr = '\0';
19154820Speter		len = strlen(buf);
19254820Speter	}
19354820Speter
19454820Speter	if (size)
19554820Speter		*size = len;
19654820Speter	return buf;
19754820Speter}
19854820Speter
19954820Speter#ifdef TEST
20054820Speter
20154820Speterint
202121193Smarkmmain(int argc, char *argv[])
20354820Speter{
20454820Speter	char   *ptr;
20554820Speter	size_t	size, line;
20654820Speter
20754820Speter	line = 0;
20854820Speter	while ((ptr = fparseln(stdin, &size, &line, NULL,
20954820Speter	    FPARSELN_UNESCALL)) != NULL)
21054820Speter		printf("line %d (%d) |%s|\n", line, size, ptr);
21154820Speter	return 0;
21254820Speter}
21354820Speter
21454820Speter/*
21554820Speter
21654820Speter# This is a test
21754820Speterline 1
21854820Speterline 2 \
21954820Speterline 3 # Comment
22054820Speterline 4 \# Not comment \\\\
22154820Speter
22254820Speter# And a comment \
22354820Speterline 5 \\\
22454820Speterline 6
22554820Speter
22654820Speter*/
22754820Speter
22854820Speter#endif /* TEST */
229