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