fparseln.c revision 55227
1176732Sjeff/* $NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $ */ 2176732Sjeff/* $FreeBSD: head/lib/libutil/fparseln.c 55227 1999-12-29 17:50:34Z peter $ */ 3176732Sjeff 4176732Sjeff/* 5336039Sjamie * Copyright (c) 1997 Christos Zoulas. All rights reserved. 6336039Sjamie * 7336039Sjamie * Redistribution and use in source and binary forms, with or without 8176732Sjeff * 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: head/lib/libutil/fparseln.c 55227 1999-12-29 17:50:34Z peter $"); 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 46static 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 */ 52static int 53isescaped(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 */ 83char * 84fparseln(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 205int main __P((int, char **)); 206 207int 208main(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 225line 1 226line 2 \ 227line 3 # Comment 228line 4 \# Not comment \\\\ 229 230# And a comment \ 231line 5 \\\ 232line 6 233 234*/ 235 236#endif /* TEST */ 237