fparseln.c revision 55227
1176732Sjeff/*	$NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $	*/
2176732Sjeff/* $FreeBSD: head/lib/libutil/fparseln.c 55227 1999-12-29 17:50:34Z peter $ */
3176732Sjeff
4176732Sjeff/*
5336039Sjamie * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
6336039Sjamie *
7336039Sjamie * Redistribution and use in source and binary forms, with or without
8176732Sjeff * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Christos Zoulas.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#if defined(LIBC_SCCS) && !defined(lint)
35__RCSID("$FreeBSD: head/lib/libutil/fparseln.c 55227 1999-12-29 17:50:34Z peter $");
36#endif
37
38#include <sys/types.h>
39#include <assert.h>
40#include <errno.h>
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <libutil.h>
45
46static int isescaped __P((const char *, const char *, int));
47
48/* isescaped():
49 *	Return true if the character in *p that belongs to a string
50 *	that starts in *sp, is escaped by the escape character esc.
51 */
52static int
53isescaped(sp, p, esc)
54	const char *sp, *p;
55	int esc;
56{
57	const char     *cp;
58	size_t		ne;
59
60#if 0
61	_DIAGASSERT(sp != NULL);
62	_DIAGASSERT(p != NULL);
63#endif
64
65	/* No escape character */
66	if (esc == '\0')
67		return 1;
68
69	/* Count the number of escape characters that precede ours */
70	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
71		continue;
72
73	/* Return true if odd number of escape characters */
74	return (ne & 1) != 0;
75}
76
77
78/* fparseln():
79 *	Read a line from a file parsing continuations ending in \
80 *	and eliminating trailing newlines, or comments starting with
81 *	the comment char.
82 */
83char *
84fparseln(fp, size, lineno, str, flags)
85	FILE		*fp;
86	size_t		*size;
87	size_t		*lineno;
88	const char	 str[3];
89	int		 flags;
90{
91	static const char dstr[3] = { '\\', '\\', '#' };
92
93	size_t	s, len;
94	char   *buf;
95	char   *ptr, *cp;
96	int	cnt;
97	char	esc, con, nl, com;
98
99#if 0
100	_DIAGASSERT(fp != NULL);
101#endif
102
103	len = 0;
104	buf = NULL;
105	cnt = 1;
106
107	if (str == NULL)
108		str = dstr;
109
110	esc = str[0];
111	con = str[1];
112	com = str[2];
113	/*
114	 * XXX: it would be cool to be able to specify the newline character,
115	 * but unfortunately, fgetln does not let us
116	 */
117	nl  = '\n';
118
119	while (cnt) {
120		cnt = 0;
121
122		if (lineno)
123			(*lineno)++;
124
125		if ((ptr = fgetln(fp, &s)) == NULL)
126			break;
127
128		if (s && com) {		/* Check and eliminate comments */
129			for (cp = ptr; cp < ptr + s; cp++)
130				if (*cp == com && !isescaped(ptr, cp, esc)) {
131					s = cp - ptr;
132					cnt = s == 0 && buf == NULL;
133					break;
134				}
135		}
136
137		if (s && nl) { 		/* Check and eliminate newlines */
138			cp = &ptr[s - 1];
139
140			if (*cp == nl)
141				s--;	/* forget newline */
142		}
143
144		if (s && con) {		/* Check and eliminate continuations */
145			cp = &ptr[s - 1];
146
147			if (*cp == con && !isescaped(ptr, cp, esc)) {
148				s--;	/* forget escape */
149				cnt = 1;
150			}
151		}
152
153		if (s == 0 && buf != NULL)
154			continue;
155
156		if ((cp = realloc(buf, len + s + 1)) == NULL) {
157			free(buf);
158			return NULL;
159		}
160		buf = cp;
161
162		(void) memcpy(buf + len, ptr, s);
163		len += s;
164		buf[len] = '\0';
165	}
166
167	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
168	    strchr(buf, esc) != NULL) {
169		ptr = cp = buf;
170		while (cp[0] != '\0') {
171			int skipesc;
172
173			while (cp[0] != '\0' && cp[0] != esc)
174				*ptr++ = *cp++;
175			if (cp[0] == '\0' || cp[1] == '\0')
176				break;
177
178			skipesc = 0;
179			if (cp[1] == com)
180				skipesc += (flags & FPARSELN_UNESCCOMM);
181			if (cp[1] == con)
182				skipesc += (flags & FPARSELN_UNESCCONT);
183			if (cp[1] == esc)
184				skipesc += (flags & FPARSELN_UNESCESC);
185			if (cp[1] != com && cp[1] != con && cp[1] != esc)
186				skipesc = (flags & FPARSELN_UNESCREST);
187
188			if (skipesc)
189				cp++;
190			else
191				*ptr++ = *cp++;
192			*ptr++ = *cp++;
193		}
194		*ptr = '\0';
195		len = strlen(buf);
196	}
197
198	if (size)
199		*size = len;
200	return buf;
201}
202
203#ifdef TEST
204
205int main __P((int, char **));
206
207int
208main(argc, argv)
209	int argc;
210	char **argv;
211{
212	char   *ptr;
213	size_t	size, line;
214
215	line = 0;
216	while ((ptr = fparseln(stdin, &size, &line, NULL,
217	    FPARSELN_UNESCALL)) != NULL)
218		printf("line %d (%d) |%s|\n", line, size, ptr);
219	return 0;
220}
221
222/*
223
224# This is a test
225line 1
226line 2 \
227line 3 # Comment
228line 4 \# Not comment \\\\
229
230# And a comment \
231line 5 \\\
232line 6
233
234*/
235
236#endif /* TEST */
237