xref: /f-stack/tools/libutil/fparseln.c (revision d4a07e70)
11eaf0ac3Slogwang /*	$NetBSD: fparseln.c,v 1.7 2007/03/08 19:57:53 drochner Exp $	*/
21eaf0ac3Slogwang 
322ce4affSfengbojiang /*-
422ce4affSfengbojiang  * SPDX-License-Identifier: BSD-4-Clause
522ce4affSfengbojiang  *
61eaf0ac3Slogwang  * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
71eaf0ac3Slogwang  *
81eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
91eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
101eaf0ac3Slogwang  * are met:
111eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
121eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer.
131eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
141eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
151eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
161eaf0ac3Slogwang  * 3. All advertising materials mentioning features or use of this software
171eaf0ac3Slogwang  *    must display the following acknowledgement:
181eaf0ac3Slogwang  *	This product includes software developed by Christos Zoulas.
191eaf0ac3Slogwang  * 4. The name of the author may not be used to endorse or promote products
201eaf0ac3Slogwang  *    derived from this software without specific prior written permission.
211eaf0ac3Slogwang  *
221eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
231eaf0ac3Slogwang  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
241eaf0ac3Slogwang  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
251eaf0ac3Slogwang  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
261eaf0ac3Slogwang  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
271eaf0ac3Slogwang  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
281eaf0ac3Slogwang  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
291eaf0ac3Slogwang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
301eaf0ac3Slogwang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
311eaf0ac3Slogwang  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
321eaf0ac3Slogwang  */
331eaf0ac3Slogwang 
341eaf0ac3Slogwang #include <sys/cdefs.h>
351eaf0ac3Slogwang __FBSDID("$FreeBSD$");
361eaf0ac3Slogwang 
371eaf0ac3Slogwang #include <sys/types.h>
381eaf0ac3Slogwang #include <assert.h>
391eaf0ac3Slogwang #include <errno.h>
401eaf0ac3Slogwang #include <stdio.h>
411eaf0ac3Slogwang #include <string.h>
421eaf0ac3Slogwang #include <stdlib.h>
431eaf0ac3Slogwang #include <libutil.h>
441eaf0ac3Slogwang 
451eaf0ac3Slogwang static int isescaped(const char *, const char *, int);
461eaf0ac3Slogwang 
471eaf0ac3Slogwang /* isescaped():
481eaf0ac3Slogwang  *	Return true if the character in *p that belongs to a string
491eaf0ac3Slogwang  *	that starts in *sp, is escaped by the escape character esc.
501eaf0ac3Slogwang  */
511eaf0ac3Slogwang static int
isescaped(const char * sp,const char * p,int esc)521eaf0ac3Slogwang isescaped(const char *sp, const char *p, int esc)
531eaf0ac3Slogwang {
541eaf0ac3Slogwang 	const char     *cp;
551eaf0ac3Slogwang 	size_t		ne;
561eaf0ac3Slogwang 
571eaf0ac3Slogwang #if 0
581eaf0ac3Slogwang 	_DIAGASSERT(sp != NULL);
591eaf0ac3Slogwang 	_DIAGASSERT(p != NULL);
601eaf0ac3Slogwang #endif
611eaf0ac3Slogwang 
621eaf0ac3Slogwang 	/* No escape character */
631eaf0ac3Slogwang 	if (esc == '\0')
641eaf0ac3Slogwang 		return 0;
651eaf0ac3Slogwang 
661eaf0ac3Slogwang 	/* Count the number of escape characters that precede ours */
671eaf0ac3Slogwang 	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
681eaf0ac3Slogwang 		continue;
691eaf0ac3Slogwang 
701eaf0ac3Slogwang 	/* Return true if odd number of escape characters */
711eaf0ac3Slogwang 	return (ne & 1) != 0;
721eaf0ac3Slogwang }
731eaf0ac3Slogwang 
741eaf0ac3Slogwang 
751eaf0ac3Slogwang /* fparseln():
761eaf0ac3Slogwang  *	Read a line from a file parsing continuations ending in \
771eaf0ac3Slogwang  *	and eliminating trailing newlines, or comments starting with
781eaf0ac3Slogwang  *	the comment char.
791eaf0ac3Slogwang  */
801eaf0ac3Slogwang char *
fparseln(FILE * fp,size_t * size,size_t * lineno,const char str[3],int flags)811eaf0ac3Slogwang fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
821eaf0ac3Slogwang {
831eaf0ac3Slogwang 	static const char dstr[3] = { '\\', '\\', '#' };
841eaf0ac3Slogwang 
851eaf0ac3Slogwang 	size_t	s, len;
861eaf0ac3Slogwang 	char   *buf;
871eaf0ac3Slogwang 	char   *ptr, *cp;
881eaf0ac3Slogwang 	int	cnt;
891eaf0ac3Slogwang 	char	esc, con, nl, com;
90*d4a07e70Sfengbojiang #ifdef FSTACK
91*d4a07e70Sfengbojiang 	#define MAXLINELEN 4096
92*d4a07e70Sfengbojiang 	char fbuf[MAXLINELEN];
93*d4a07e70Sfengbojiang #endif
941eaf0ac3Slogwang 
951eaf0ac3Slogwang #if 0
961eaf0ac3Slogwang 	_DIAGASSERT(fp != NULL);
971eaf0ac3Slogwang #endif
981eaf0ac3Slogwang 
991eaf0ac3Slogwang 	len = 0;
1001eaf0ac3Slogwang 	buf = NULL;
1011eaf0ac3Slogwang 	cnt = 1;
1021eaf0ac3Slogwang 
1031eaf0ac3Slogwang 	if (str == NULL)
1041eaf0ac3Slogwang 		str = dstr;
1051eaf0ac3Slogwang 
1061eaf0ac3Slogwang 	esc = str[0];
1071eaf0ac3Slogwang 	con = str[1];
1081eaf0ac3Slogwang 	com = str[2];
1091eaf0ac3Slogwang 	/*
1101eaf0ac3Slogwang 	 * XXX: it would be cool to be able to specify the newline character,
1111eaf0ac3Slogwang 	 * but unfortunately, fgetln does not let us
1121eaf0ac3Slogwang 	 */
1131eaf0ac3Slogwang 	nl  = '\n';
1141eaf0ac3Slogwang 
1151eaf0ac3Slogwang 	while (cnt) {
1161eaf0ac3Slogwang 		cnt = 0;
1171eaf0ac3Slogwang 
1181eaf0ac3Slogwang 		if (lineno)
1191eaf0ac3Slogwang 			(*lineno)++;
1201eaf0ac3Slogwang 
121*d4a07e70Sfengbojiang #ifndef FSTACK
1221eaf0ac3Slogwang 		if ((ptr = fgetln(fp, &s)) == NULL)
1231eaf0ac3Slogwang 			break;
124*d4a07e70Sfengbojiang #else
125*d4a07e70Sfengbojiang 		if (fgets(fbuf, MAXLINELEN, fp) == NULL)
126*d4a07e70Sfengbojiang 			break;
127*d4a07e70Sfengbojiang 		fbuf[strcspn(fbuf, "\n")] = '\0';
128*d4a07e70Sfengbojiang 		ptr = fbuf;
129*d4a07e70Sfengbojiang #endif
1301eaf0ac3Slogwang 
1311eaf0ac3Slogwang 		if (s && com) {		/* Check and eliminate comments */
1321eaf0ac3Slogwang 			for (cp = ptr; cp < ptr + s; cp++)
1331eaf0ac3Slogwang 				if (*cp == com && !isescaped(ptr, cp, esc)) {
1341eaf0ac3Slogwang 					s = cp - ptr;
1351eaf0ac3Slogwang 					cnt = s == 0 && buf == NULL;
1361eaf0ac3Slogwang 					break;
1371eaf0ac3Slogwang 				}
1381eaf0ac3Slogwang 		}
1391eaf0ac3Slogwang 
1401eaf0ac3Slogwang 		if (s && nl) { 		/* Check and eliminate newlines */
1411eaf0ac3Slogwang 			cp = &ptr[s - 1];
1421eaf0ac3Slogwang 
1431eaf0ac3Slogwang 			if (*cp == nl)
1441eaf0ac3Slogwang 				s--;	/* forget newline */
1451eaf0ac3Slogwang 		}
1461eaf0ac3Slogwang 
1471eaf0ac3Slogwang 		if (s && con) {		/* Check and eliminate continuations */
1481eaf0ac3Slogwang 			cp = &ptr[s - 1];
1491eaf0ac3Slogwang 
1501eaf0ac3Slogwang 			if (*cp == con && !isescaped(ptr, cp, esc)) {
1511eaf0ac3Slogwang 				s--;	/* forget continuation char */
1521eaf0ac3Slogwang 				cnt = 1;
1531eaf0ac3Slogwang 			}
1541eaf0ac3Slogwang 		}
1551eaf0ac3Slogwang 
1561eaf0ac3Slogwang 		if (s == 0) {
1571eaf0ac3Slogwang 			/*
1581eaf0ac3Slogwang 			 * nothing to add, skip realloc except in case
1591eaf0ac3Slogwang 			 * we need a minimal buf to return an empty line
1601eaf0ac3Slogwang 			 */
1611eaf0ac3Slogwang 			if (cnt || buf != NULL)
1621eaf0ac3Slogwang 				continue;
1631eaf0ac3Slogwang 		}
1641eaf0ac3Slogwang 
1651eaf0ac3Slogwang 		if ((cp = realloc(buf, len + s + 1)) == NULL) {
1661eaf0ac3Slogwang 			free(buf);
1671eaf0ac3Slogwang 			return NULL;
1681eaf0ac3Slogwang 		}
1691eaf0ac3Slogwang 		buf = cp;
1701eaf0ac3Slogwang 
1711eaf0ac3Slogwang 		(void) memcpy(buf + len, ptr, s);
1721eaf0ac3Slogwang 		len += s;
1731eaf0ac3Slogwang 		buf[len] = '\0';
1741eaf0ac3Slogwang 	}
1751eaf0ac3Slogwang 
1761eaf0ac3Slogwang 	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
1771eaf0ac3Slogwang 	    strchr(buf, esc) != NULL) {
1781eaf0ac3Slogwang 		ptr = cp = buf;
1791eaf0ac3Slogwang 		while (cp[0] != '\0') {
1801eaf0ac3Slogwang 			int skipesc;
1811eaf0ac3Slogwang 
1821eaf0ac3Slogwang 			while (cp[0] != '\0' && cp[0] != esc)
1831eaf0ac3Slogwang 				*ptr++ = *cp++;
1841eaf0ac3Slogwang 			if (cp[0] == '\0' || cp[1] == '\0')
1851eaf0ac3Slogwang 				break;
1861eaf0ac3Slogwang 
1871eaf0ac3Slogwang 			skipesc = 0;
1881eaf0ac3Slogwang 			if (cp[1] == com)
1891eaf0ac3Slogwang 				skipesc += (flags & FPARSELN_UNESCCOMM);
1901eaf0ac3Slogwang 			if (cp[1] == con)
1911eaf0ac3Slogwang 				skipesc += (flags & FPARSELN_UNESCCONT);
1921eaf0ac3Slogwang 			if (cp[1] == esc)
1931eaf0ac3Slogwang 				skipesc += (flags & FPARSELN_UNESCESC);
1941eaf0ac3Slogwang 			if (cp[1] != com && cp[1] != con && cp[1] != esc)
1951eaf0ac3Slogwang 				skipesc = (flags & FPARSELN_UNESCREST);
1961eaf0ac3Slogwang 
1971eaf0ac3Slogwang 			if (skipesc)
1981eaf0ac3Slogwang 				cp++;
1991eaf0ac3Slogwang 			else
2001eaf0ac3Slogwang 				*ptr++ = *cp++;
2011eaf0ac3Slogwang 			*ptr++ = *cp++;
2021eaf0ac3Slogwang 		}
2031eaf0ac3Slogwang 		*ptr = '\0';
2041eaf0ac3Slogwang 		len = strlen(buf);
2051eaf0ac3Slogwang 	}
2061eaf0ac3Slogwang 
2071eaf0ac3Slogwang 	if (size)
2081eaf0ac3Slogwang 		*size = len;
2091eaf0ac3Slogwang 	return buf;
2101eaf0ac3Slogwang }
2111eaf0ac3Slogwang 
2121eaf0ac3Slogwang #ifdef TEST
2131eaf0ac3Slogwang 
2141eaf0ac3Slogwang int
main(int argc,char * argv[])2151eaf0ac3Slogwang main(int argc, char *argv[])
2161eaf0ac3Slogwang {
2171eaf0ac3Slogwang 	char   *ptr;
2181eaf0ac3Slogwang 	size_t	size, line;
2191eaf0ac3Slogwang 
2201eaf0ac3Slogwang 	line = 0;
2211eaf0ac3Slogwang 	while ((ptr = fparseln(stdin, &size, &line, NULL,
2221eaf0ac3Slogwang 	    FPARSELN_UNESCALL)) != NULL)
2231eaf0ac3Slogwang 		printf("line %d (%d) |%s|\n", line, size, ptr);
2241eaf0ac3Slogwang 	return 0;
2251eaf0ac3Slogwang }
2261eaf0ac3Slogwang 
2271eaf0ac3Slogwang /*
2281eaf0ac3Slogwang 
2291eaf0ac3Slogwang # This is a test
2301eaf0ac3Slogwang line 1
2311eaf0ac3Slogwang line 2 \
2321eaf0ac3Slogwang line 3 # Comment
2331eaf0ac3Slogwang line 4 \# Not comment \\\\
2341eaf0ac3Slogwang 
2351eaf0ac3Slogwang # And a comment \
2361eaf0ac3Slogwang line 5 \\\
2371eaf0ac3Slogwang line 6
2381eaf0ac3Slogwang 
2391eaf0ac3Slogwang */
2401eaf0ac3Slogwang 
2411eaf0ac3Slogwang #endif /* TEST */
242