cook.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * $Id: cook.c,v 4.12 2007/07/04 21:36:37 bkorb Exp $
  3. * Time-stamp: "2007-07-04 11:33:34 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-2007 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. * 239588c55c22c60ffe159946a760a33e pkg/libopts/COPYING.gplv3
  25. * fa82ca978890795162346e661b47161a pkg/libopts/COPYING.lgplv3
  26. * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
  27. */
  28. /* = = = START-STATIC-FORWARD = = = */
  29. /* static forward declarations maintained by :mkfwd */
  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': /* HEX Escape */
  80. if (isxdigit( (int)*pzIn )) {
  81. unsigned int val;
  82. unsigned char ch = *pzIn++;
  83. if ((ch >= 'A') && (ch <= 'F'))
  84. val = 10 + (ch - 'A');
  85. else if ((ch >= 'a') && (ch <= 'f'))
  86. val = 10 + (ch - 'a');
  87. else val = ch - '0';
  88. ch = *pzIn;
  89. if (! isxdigit( ch )) {
  90. *pRes = val;
  91. res = 2;
  92. break;
  93. }
  94. val <<= 4;
  95. if ((ch >= 'A') && (ch <= 'F'))
  96. val += 10 + (ch - 'A');
  97. else if ((ch >= 'a') && (ch <= 'f'))
  98. val += 10 + (ch - 'a');
  99. else val += ch - '0';
  100. res = 3;
  101. *pRes = val;
  102. }
  103. break;
  104. default:
  105. /*
  106. * IF the character copied was an octal digit,
  107. * THEN set the output character to an octal value
  108. */
  109. if (isdigit( (int)*pRes ) && (*pRes < '8')) {
  110. unsigned int val = *pRes - '0';
  111. unsigned char ch = *pzIn++;
  112. /*
  113. * IF the second character is *not* an octal digit,
  114. * THEN save the value and bail
  115. */
  116. if ((ch < '0') || (ch > '7')) {
  117. *pRes = val;
  118. break;
  119. }
  120. val = (val<<3) + (ch - '0');
  121. ch = *pzIn;
  122. res = 2;
  123. /*
  124. * IF the THIRD character is *not* an octal digit,
  125. * THEN save the value and bail
  126. */
  127. if ((ch < '0') || (ch > '7')) {
  128. *pRes = val;
  129. break;
  130. }
  131. /*
  132. * IF the new value would not be too large,
  133. * THEN add on the third and last character value
  134. */
  135. if ((val<<3) < 0xFF) {
  136. val = (val<<3) + (ch - '0');
  137. res = 3;
  138. }
  139. *pRes = val;
  140. break;
  141. }
  142. }
  143. return res;
  144. }
  145. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  146. *
  147. * A quoted string has been found.
  148. * Find the end of it and compress any escape sequences.
  149. */
  150. /*=export_func ao_string_cook
  151. * private:
  152. *
  153. * what: concatenate and escape-process strings
  154. * arg: + char* + pzScan + The *MODIFIABLE* input buffer +
  155. * arg: + int* + pLineCt + The (possibly NULL) pointer to a line count +
  156. *
  157. * ret-type: char*
  158. * ret-desc: The address of the text following the processed strings.
  159. * The return value is NULL if the strings are ill-formed.
  160. *
  161. * doc:
  162. *
  163. * A series of one or more quoted strings are concatenated together.
  164. * If they are quoted with double quotes (@code{"}), then backslash
  165. * escapes are processed per the C programming language. If they are
  166. * single quote strings, then the backslashes are honored only when they
  167. * precede another backslash or a single quote character.
  168. *
  169. * err: @code{NULL} is returned if the string(s) is/are mal-formed.
  170. =*/
  171. char*
  172. ao_string_cook( char* pzScan, int* pLineCt )
  173. {
  174. int l = 0;
  175. char q = *pzScan;
  176. /*
  177. * It is a quoted string. Process the escape sequence characters
  178. * (in the set "abfnrtv") and make sure we find a closing quote.
  179. */
  180. char* pzD = pzScan++;
  181. char* pzS = pzScan;
  182. if (pLineCt == NULL)
  183. pLineCt = &l;
  184. for (;;) {
  185. /*
  186. * IF the next character is the quote character, THEN we may end the
  187. * string. We end it unless the next non-blank character *after* the
  188. * string happens to also be a quote. If it is, then we will change
  189. * our quote character to the new quote character and continue
  190. * condensing text.
  191. */
  192. while (*pzS == q) {
  193. *pzD = NUL; /* This is probably the end of the line */
  194. pzS++;
  195. scan_for_quote:
  196. while (isspace((int)*pzS))
  197. if (*(pzS++) == '\n')
  198. (*pLineCt)++;
  199. /*
  200. * IF the next character is a quote character,
  201. * THEN we will concatenate the strings.
  202. */
  203. switch (*pzS) {
  204. case '"':
  205. case '\'':
  206. break;
  207. case '/':
  208. /*
  209. * Allow for a comment embedded in the concatenated string.
  210. */
  211. switch (pzS[1]) {
  212. default: return NULL;
  213. case '/':
  214. /*
  215. * Skip to end of line
  216. */
  217. pzS = strchr( pzS, '\n' );
  218. if (pzS == NULL)
  219. return NULL;
  220. (*pLineCt)++;
  221. break;
  222. case '*':
  223. {
  224. char* p = strstr( pzS+2, "*/" );
  225. /*
  226. * Skip to terminating star slash
  227. */
  228. if (p == NULL)
  229. return NULL;
  230. while (pzS < p) {
  231. if (*(pzS++) == '\n')
  232. (*pLineCt)++;
  233. }
  234. pzS = p + 2;
  235. }
  236. }
  237. goto scan_for_quote;
  238. default:
  239. /*
  240. * The next non-whitespace character is not a quote.
  241. * The series of quoted strings has come to an end.
  242. */
  243. return pzS;
  244. }
  245. q = *(pzS++); /* assign new quote character and advance scan */
  246. }
  247. /*
  248. * We are inside a quoted string. Copy text.
  249. */
  250. switch (*(pzD++) = *(pzS++)) {
  251. case NUL:
  252. return NULL;
  253. case '\n':
  254. (*pLineCt)++;
  255. break;
  256. case '\\':
  257. /*
  258. * IF we are escaping a new line,
  259. * THEN drop both the escape and the newline from
  260. * the result string.
  261. */
  262. if (*pzS == '\n') {
  263. pzS++;
  264. pzD--;
  265. (*pLineCt)++;
  266. }
  267. /*
  268. * ELSE IF the quote character is '"' or '`',
  269. * THEN we do the full escape character processing
  270. */
  271. else if (q != '\'') {
  272. int ct = ao_string_cook_escape_char( pzS, pzD-1, (u_int)'\n' );
  273. if (ct == 0)
  274. return NULL;
  275. pzS += ct;
  276. } /* if (q != '\'') */
  277. /*
  278. * OTHERWISE, we only process "\\", "\'" and "\#" sequences.
  279. * The latter only to easily hide preprocessing directives.
  280. */
  281. else switch (*pzS) {
  282. case '\\':
  283. case '\'':
  284. case '#':
  285. pzD[-1] = *pzS++;
  286. }
  287. } /* switch (*(pzD++) = *(pzS++)) */
  288. } /* for (;;) */
  289. }
  290. /*
  291. * Local Variables:
  292. * mode: C
  293. * c-file-style: "stroustrup"
  294. * indent-tabs-mode: nil
  295. * End:
  296. * end of autoopts/cook.c */