1 /* $NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $ */ 2 /* $FreeBSD$ */ 3 4 /* 5 * Copyright (c) 1997 Christos Zoulas. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Christos Zoulas. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 #if defined(LIBC_SCCS) && !defined(lint) 35 __RCSID("$FreeBSD$"); 36 #endif 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 */ 237