cook.c 8.8 KB

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