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