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