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 <stdio.h>
  33 #include <string.h>
  34 #include <stdlib.h>
  35 #include <compat.h>
  36 
  37 static int isescaped(const char *, const char *, int);
  38 
  39 /* isescaped():
  40  *      Return true if the character in *p that belongs to a string
  41  *      that starts in *sp, is escaped by the escape character esc.
  42  */
  43 static int
  44 isescaped(const char *sp, const char *p, int esc)
  45 {
  46         const char     *cp;
  47         size_t          ne;
  48 
  49         /* No escape character */
  50         if (esc == '\0')
  51                 return 1;
  52 
  53         /* Count the number of escape characters that precede ours */
  54         for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
  55                 continue;
  56 
  57         /* Return true if odd number of escape characters */
  58         return (ne & 1) != 0;
  59 }
  60 
  61 
  62 /* fparseln():
  63  *      Read a line from a file parsing continuations ending in \
  64  *      and eliminating trailing newlines, or comments starting with
  65  *      the comment char.
  66  */
  67 char *
  68 fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
  69 {
  70         static const char dstr[3] = { '\\', '\\', '#' };
  71 
  72         size_t  s, len;
  73         char   *buf;
  74         char   *ptr, *cp;
  75         int     cnt;
  76         char    esc, con, nl, com;
  77 
  78         len = 0;
  79         buf = NULL;
  80         cnt = 1;
  81 
  82         if (str == NULL)
  83                 str = dstr;
  84 
  85         esc = str[0];
  86         con = str[1];
  87         com = str[2];
  88         /*
  89          * XXX: it would be cool to be able to specify the newline character,
  90          * but unfortunately, fgetln does not let us
  91          */
  92         nl  = '\n';
  93 
  94         while (cnt) {
  95                 cnt = 0;
  96 
  97                 if (lineno)
  98                         (*lineno)++;
  99 
 100                 if ((ptr = fgetln(fp, &s)) == NULL)
 101                         break;
 102 
 103                 if (s && com) {         /* Check and eliminate comments */
 104                         for (cp = ptr; cp < ptr + s; cp++)
 105                                 if (*cp == com && !isescaped(ptr, cp, esc)) {
 106                                         s = cp - ptr;
 107                                         cnt = s == 0 && buf == NULL;
 108                                         break;
 109                                 }
 110                 }
 111 
 112                 if (s && nl) {          /* Check and eliminate newlines */
 113                         cp = &ptr[s - 1];
 114 
 115                         if (*cp == nl)
 116                                 s--;    /* forget newline */
 117                 }
 118 
 119                 if (s && con) {         /* Check and eliminate continuations */
 120                         cp = &ptr[s - 1];
 121 
 122                         if (*cp == con && !isescaped(ptr, cp, esc)) {
 123                                 s--;    /* forget escape */
 124                                 cnt = 1;
 125                         }
 126                 }
 127 
 128                 if (s == 0 && buf != NULL)
 129                         continue;
 130 
 131                 if ((cp = realloc(buf, len + s + 1)) == NULL) {
 132                         free(buf);
 133                         return NULL;
 134                 }
 135                 buf = cp;
 136 
 137                 (void) memcpy(buf + len, ptr, s);
 138                 len += s;
 139                 buf[len] = '\0';
 140         }
 141 
 142         if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
 143             strchr(buf, esc) != NULL) {
 144                 ptr = cp = buf;
 145                 while (cp[0] != '\0') {
 146                         int skipesc;
 147 
 148                         while (cp[0] != '\0' && cp[0] != esc)
 149                                 *ptr++ = *cp++;
 150                         if (cp[0] == '\0' || cp[1] == '\0')
 151                                 break;
 152 
 153                         skipesc = 0;
 154                         if (cp[1] == com)
 155                                 skipesc += (flags & FPARSELN_UNESCCOMM);
 156                         if (cp[1] == con)
 157                                 skipesc += (flags & FPARSELN_UNESCCONT);
 158                         if (cp[1] == esc)
 159                                 skipesc += (flags & FPARSELN_UNESCESC);
 160                         if (cp[1] != com && cp[1] != con && cp[1] != esc)
 161                                 skipesc = (flags & FPARSELN_UNESCREST);
 162 
 163                         if (skipesc)
 164                                 cp++;
 165                         else
 166                                 *ptr++ = *cp++;
 167                         *ptr++ = *cp++;
 168                 }
 169                 *ptr = '\0';
 170                 len = strlen(buf);
 171         }
 172 
 173         if (size)
 174                 *size = len;
 175         return buf;
 176 }
 177 
 178 #ifdef TEST
 179 
 180 int
 181 main(int argc, char *argv[])
 182 {
 183         char   *ptr;
 184         size_t  size, line;
 185 
 186         line = 0;
 187         while ((ptr = fparseln(stdin, &size, &line, NULL,
 188             FPARSELN_UNESCALL)) != NULL)
 189                 printf("line %d (%d) |%s|\n", line, size, ptr);
 190         return 0;
 191 }
 192 
 193 /*
 194 
 195 # This is a test
 196 line 1
 197 line 2 \
 198 line 3 # Comment
 199 line 4 \# Not comment \\\\
 200 
 201 # And a comment \
 202 line 5 \\\
 203 line 6
 204 
 205 */
 206 
 207 #endif /* TEST */