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