fparseln.c 5.2 KB

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