cook.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * $Id: cook.c,v 4.16 2008/01/23 00:36:04 bkorb Exp $
  3. * Time-stamp: "2007-11-16 22:49:11 bkorb"
  4. *
  5. * This file contains the routines that deal with processing quoted strings
  6. * into an internal format.
  7. *
  8. * This file is part of AutoOpts, a companion to AutoGen.
  9. * AutoOpts is free software.
  10. * AutoOpts is copyright (c) 1992-2008 by Bruce Korb - all rights reserved
  11. * AutoOpts is copyright (c) 1992-2008 by Bruce Korb - all rights reserved
  12. *
  13. * AutoOpts is available under any one of two licenses. The license
  14. * in use must be one of these two and the choice is under the control
  15. * of the user of the license.
  16. *
  17. * The GNU Lesser General Public License, version 3 or later
  18. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  19. *
  20. * The Modified Berkeley Software Distribution License
  21. * See the file "COPYING.mbsd"
  22. *
  23. * These files have the following md5sums:
  24. *
  25. * 239588c55c22c60ffe159946a760a33e pkg/libopts/COPYING.gplv3
  26. * fa82ca978890795162346e661b47161a pkg/libopts/COPYING.lgplv3
  27. * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
  28. */
  29. /* = = = START-STATIC-FORWARD = = = */
  30. /* static forward declarations maintained by mk-fwd */
  31. /* = = = END-STATIC-FORWARD = = = */
  32. /*=export_func ao_string_cook_escape_char
  33. * private:
  34. *
  35. * what: escape-process a string fragment
  36. * arg: + char const* + pzScan + points to character after the escape +
  37. * arg: + char* + pRes + Where to put the result byte +
  38. * arg: + unsigned int + nl_ch + replacement char if scanned char is \n +
  39. *
  40. * ret-type: unsigned int
  41. * ret-desc: The number of bytes consumed processing the escaped character.
  42. *
  43. * doc:
  44. *
  45. * This function converts "t" into "\t" and all your other favorite
  46. * escapes, including numeric ones: hex and ocatal, too.
  47. * The returned result tells the caller how far to advance the
  48. * scan pointer (passed in). The default is to just pass through the
  49. * escaped character and advance the scan by one.
  50. *
  51. * Some applications need to keep an escaped newline, others need to
  52. * suppress it. This is accomplished by supplying a '\n' replacement
  53. * character that is different from \n, if need be. For example, use
  54. * 0x7F and never emit a 0x7F.
  55. *
  56. * err: @code{NULL} is returned if the string is mal-formed.
  57. =*/
  58. unsigned int
  59. ao_string_cook_escape_char( char const* pzIn, char* pRes, u_int nl )
  60. {
  61. unsigned int res = 1;
  62. switch (*pRes = *pzIn++) {
  63. case NUL: /* NUL - end of input string */
  64. return 0;
  65. case '\r':
  66. if (*pzIn != '\n')
  67. return 1;
  68. res++;
  69. /* FALLTHROUGH */
  70. case '\n': /* NL - emit newline */
  71. *pRes = (char)nl;
  72. return res;
  73. case 'a': *pRes = '\a'; break;
  74. case 'b': *pRes = '\b'; break;
  75. case 'f': *pRes = '\f'; break;
  76. case 'n': *pRes = '\n'; break;
  77. case 'r': *pRes = '\r'; break;
  78. case 't': *pRes = '\t'; break;
  79. case 'v': *pRes = '\v'; break;
  80. case 'x':
  81. case 'X': /* HEX Escape */
  82. if (IS_HEX_DIGIT_CHAR(*pzIn)) {
  83. char z[4], *pz = z;
  84. do *(pz++) = *(pzIn++);
  85. while (IS_HEX_DIGIT_CHAR(*pzIn) && (pz < z + 2));
  86. *pz = NUL;
  87. *pRes = (unsigned char)strtoul(z, NULL, 16);
  88. res += pz - z;
  89. }
  90. break;
  91. case '0': case '1': case '2': case '3':
  92. case '4': case '5': case '6': case '7':
  93. {
  94. /*
  95. * IF the character copied was an octal digit,
  96. * THEN set the output character to an octal value
  97. */
  98. char z[4], *pz = z + 1;
  99. unsigned long val;
  100. z[0] = *pRes;
  101. while (IS_OCT_DIGIT_CHAR(*pzIn) && (pz < z + 3))
  102. *(pz++) = *(pzIn++);
  103. *pz = NUL;
  104. val = strtoul(z, NULL, 8);
  105. if (val > 0xFF)
  106. val = 0xFF;
  107. *pRes = (unsigned char)val;
  108. res = pz - z;
  109. break;
  110. }
  111. default: ;
  112. }
  113. return res;
  114. }
  115. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  116. *
  117. * A quoted string has been found.
  118. * Find the end of it and compress any escape sequences.
  119. */
  120. /*=export_func ao_string_cook
  121. * private:
  122. *
  123. * what: concatenate and escape-process strings
  124. * arg: + char* + pzScan + The *MODIFIABLE* input buffer +
  125. * arg: + int* + pLineCt + The (possibly NULL) pointer to a line count +
  126. *
  127. * ret-type: char*
  128. * ret-desc: The address of the text following the processed strings.
  129. * The return value is NULL if the strings are ill-formed.
  130. *
  131. * doc:
  132. *
  133. * A series of one or more quoted strings are concatenated together.
  134. * If they are quoted with double quotes (@code{"}), then backslash
  135. * escapes are processed per the C programming language. If they are
  136. * single quote strings, then the backslashes are honored only when they
  137. * precede another backslash or a single quote character.
  138. *
  139. * err: @code{NULL} is returned if the string(s) is/are mal-formed.
  140. =*/
  141. char*
  142. ao_string_cook( char* pzScan, int* pLineCt )
  143. {
  144. int l = 0;
  145. char q = *pzScan;
  146. /*
  147. * It is a quoted string. Process the escape sequence characters
  148. * (in the set "abfnrtv") and make sure we find a closing quote.
  149. */
  150. char* pzD = pzScan++;
  151. char* pzS = pzScan;
  152. if (pLineCt == NULL)
  153. pLineCt = &l;
  154. for (;;) {
  155. /*
  156. * IF the next character is the quote character, THEN we may end the
  157. * string. We end it unless the next non-blank character *after* the
  158. * string happens to also be a quote. If it is, then we will change
  159. * our quote character to the new quote character and continue
  160. * condensing text.
  161. */
  162. while (*pzS == q) {
  163. *pzD = NUL; /* This is probably the end of the line */
  164. pzS++;
  165. scan_for_quote:
  166. while (IS_WHITESPACE_CHAR(*pzS))
  167. if (*(pzS++) == '\n')
  168. (*pLineCt)++;
  169. /*
  170. * IF the next character is a quote character,
  171. * THEN we will concatenate the strings.
  172. */
  173. switch (*pzS) {
  174. case '"':
  175. case '\'':
  176. break;
  177. case '/':
  178. /*
  179. * Allow for a comment embedded in the concatenated string.
  180. */
  181. switch (pzS[1]) {
  182. default: return NULL;
  183. case '/':
  184. /*
  185. * Skip to end of line
  186. */
  187. pzS = strchr( pzS, '\n' );
  188. if (pzS == NULL)
  189. return NULL;
  190. (*pLineCt)++;
  191. break;
  192. case '*':
  193. {
  194. char* p = strstr( pzS+2, "*/" );
  195. /*
  196. * Skip to terminating star slash
  197. */
  198. if (p == NULL)
  199. return NULL;
  200. while (pzS < p) {
  201. if (*(pzS++) == '\n')
  202. (*pLineCt)++;
  203. }
  204. pzS = p + 2;
  205. }
  206. }
  207. goto scan_for_quote;
  208. default:
  209. /*
  210. * The next non-whitespace character is not a quote.
  211. * The series of quoted strings has come to an end.
  212. */
  213. return pzS;
  214. }
  215. q = *(pzS++); /* assign new quote character and advance scan */
  216. }
  217. /*
  218. * We are inside a quoted string. Copy text.
  219. */
  220. switch (*(pzD++) = *(pzS++)) {
  221. case NUL:
  222. return NULL;
  223. case '\n':
  224. (*pLineCt)++;
  225. break;
  226. case '\\':
  227. /*
  228. * IF we are escaping a new line,
  229. * THEN drop both the escape and the newline from
  230. * the result string.
  231. */
  232. if (*pzS == '\n') {
  233. pzS++;
  234. pzD--;
  235. (*pLineCt)++;
  236. }
  237. /*
  238. * ELSE IF the quote character is '"' or '`',
  239. * THEN we do the full escape character processing
  240. */
  241. else if (q != '\'') {
  242. int ct = ao_string_cook_escape_char( pzS, pzD-1, (u_int)'\n' );
  243. if (ct == 0)
  244. return NULL;
  245. pzS += ct;
  246. } /* if (q != '\'') */
  247. /*
  248. * OTHERWISE, we only process "\\", "\'" and "\#" sequences.
  249. * The latter only to easily hide preprocessing directives.
  250. */
  251. else switch (*pzS) {
  252. case '\\':
  253. case '\'':
  254. case '#':
  255. pzD[-1] = *pzS++;
  256. }
  257. } /* switch (*(pzD++) = *(pzS++)) */
  258. } /* for (;;) */
  259. }
  260. /*
  261. * Local Variables:
  262. * mode: C
  263. * c-file-style: "stroustrup"
  264. * indent-tabs-mode: nil
  265. * End:
  266. * end of autoopts/cook.c */