Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / lib / libutil / fparseln.c
1 /*      $NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $       */
2 /* $FreeBSD: src/lib/libutil/fparseln.c,v 1.2 1999/12/29 17:50:33 peter Exp $ */
3 /* $DragonFly: src/lib/libutil/fparseln.c,v 1.2 2003/06/17 04:26:51 dillon Exp $ */
4
5 /*
6  * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by Christos Zoulas.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * $FreeBSD: src/lib/libutil/fparseln.c,v 1.2 1999/12/29 17:50:33 peter Exp $
34  */
35
36 #include <sys/cdefs.h>
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
46 static 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  */
52 static int
53 isescaped(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  */
83 char *
84 fparseln(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
205 int main __P((int, char **));
206
207 int
208 main(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
225 line 1
226 line 2 \
227 line 3 # Comment
228 line 4 \# Not comment \\\\
229
230 # And a comment \
231 line 5 \\\
232 line 6
233
234 */
235
236 #endif /* TEST */