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