Sync usage with man page.
[netbsd-mini2440.git] / dist / ntp / libopts / cook.c
blob9b4f0caf23e2b355240fabc8cc315bdc34f4e791
1 /* $NetBSD$ */
4 /*
5 * Id: cook.c,v 4.10 2007/02/04 17:44:12 bkorb Exp
6 * Time-stamp: "2006-09-24 15:21:02 bkorb"
8 * This file contains the routines that deal with processing quoted strings
9 * into an internal format.
13 * Automated Options copyright 1992-2007 Bruce Korb
15 * Automated Options is free software.
16 * You may redistribute it and/or modify it under the terms of the
17 * GNU General Public License, as published by the Free Software
18 * Foundation; either version 2, or (at your option) any later version.
20 * Automated Options is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with Automated Options. See the file "COPYING". If not,
27 * write to: The Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor,
29 * Boston, MA 02110-1301, USA.
31 * As a special exception, Bruce Korb gives permission for additional
32 * uses of the text contained in his release of AutoOpts.
34 * The exception is that, if you link the AutoOpts library with other
35 * files to produce an executable, this does not by itself cause the
36 * resulting executable to be covered by the GNU General Public License.
37 * Your use of that executable is in no way restricted on account of
38 * linking the AutoOpts library code into it.
40 * This exception does not however invalidate any other reasons why
41 * the executable file might be covered by the GNU General Public License.
43 * This exception applies only to the code released by Bruce Korb under
44 * the name AutoOpts. If you copy code from other sources under the
45 * General Public License into a copy of AutoOpts, as the General Public
46 * License permits, the exception does not apply to the code that you add
47 * in this way. To avoid misleading anyone as to the status of such
48 * modified files, you must delete this exception notice from them.
50 * If you write modifications of your own for AutoOpts, it is your choice
51 * whether to permit this exception to apply to your modifications.
52 * If you do not wish that, delete this exception notice.
55 /* = = = START-STATIC-FORWARD = = = */
56 /* static forward declarations maintained by :mkfwd */
57 /* = = = END-STATIC-FORWARD = = = */
59 /*=export_func ao_string_cook_escape_char
60 * private:
62 * what: escape-process a string fragment
63 * arg: + char const* + pzScan + points to character after the escape +
64 * arg: + char* + pRes + Where to put the result byte +
65 * arg: + unsigned int + nl_ch + replacement char if scanned char is \n +
67 * ret-type: unsigned int
68 * ret-desc: The number of bytes consumed processing the escaped character.
70 * doc:
72 * This function converts "t" into "\t" and all your other favorite
73 * escapes, including numeric ones: hex and ocatal, too.
74 * The returned result tells the caller how far to advance the
75 * scan pointer (passed in). The default is to just pass through the
76 * escaped character and advance the scan by one.
78 * Some applications need to keep an escaped newline, others need to
79 * suppress it. This is accomplished by supplying a '\n' replacement
80 * character that is different from \n, if need be. For example, use
81 * 0x7F and never emit a 0x7F.
83 * err: @code{NULL} is returned if the string is mal-formed.
84 =*/
85 unsigned int
86 ao_string_cook_escape_char( char const* pzIn, char* pRes, u_int nl )
88 unsigned int res = 1;
90 switch (*pRes = *pzIn++) {
91 case NUL: /* NUL - end of input string */
92 return 0;
93 case '\r':
94 if (*pzIn != '\n')
95 return 1;
96 res++;
97 /* FALLTHROUGH */
98 case '\n': /* NL - emit newline */
99 *pRes = (char)nl;
100 return res;
102 case 'a': *pRes = '\a'; break;
103 case 'b': *pRes = '\b'; break;
104 case 'f': *pRes = '\f'; break;
105 case 'n': *pRes = '\n'; break;
106 case 'r': *pRes = '\r'; break;
107 case 't': *pRes = '\t'; break;
108 case 'v': *pRes = '\v'; break;
110 case 'x': /* HEX Escape */
111 if (isxdigit( (int)*pzIn )) {
112 unsigned int val;
113 unsigned char ch = *pzIn++;
115 if ((ch >= 'A') && (ch <= 'F'))
116 val = 10 + (ch - 'A');
117 else if ((ch >= 'a') && (ch <= 'f'))
118 val = 10 + (ch - 'a');
119 else val = ch - '0';
121 ch = *pzIn;
123 if (! isxdigit( ch )) {
124 *pRes = val;
125 res = 2;
126 break;
128 val <<= 4;
129 if ((ch >= 'A') && (ch <= 'F'))
130 val += 10 + (ch - 'A');
131 else if ((ch >= 'a') && (ch <= 'f'))
132 val += 10 + (ch - 'a');
133 else val += ch - '0';
135 res = 3;
136 *pRes = val;
138 break;
140 default:
142 * IF the character copied was an octal digit,
143 * THEN set the output character to an octal value
145 if (isdigit( (int)*pRes ) && (*pRes < '8')) {
146 unsigned int val = *pRes - '0';
147 unsigned char ch = *pzIn++;
150 * IF the second character is *not* an octal digit,
151 * THEN save the value and bail
153 if ((ch < '0') || (ch > '7')) {
154 *pRes = val;
155 break;
158 val = (val<<3) + (ch - '0');
159 ch = *pzIn;
160 res = 2;
163 * IF the THIRD character is *not* an octal digit,
164 * THEN save the value and bail
166 if ((ch < '0') || (ch > '7')) {
167 *pRes = val;
168 break;
172 * IF the new value would not be too large,
173 * THEN add on the third and last character value
175 if ((val<<3) < 0xFF) {
176 val = (val<<3) + (ch - '0');
177 res = 3;
180 *pRes = val;
181 break;
185 return res;
189 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
191 * A quoted string has been found.
192 * Find the end of it and compress any escape sequences.
194 /*=export_func ao_string_cook
195 * private:
197 * what: concatenate and escape-process strings
198 * arg: + char* + pzScan + The *MODIFIABLE* input buffer +
199 * arg: + int* + pLineCt + The (possibly NULL) pointer to a line count +
201 * ret-type: char*
202 * ret-desc: The address of the text following the processed strings.
203 * The return value is NULL if the strings are ill-formed.
205 * doc:
207 * A series of one or more quoted strings are concatenated together.
208 * If they are quoted with double quotes (@code{"}), then backslash
209 * escapes are processed per the C programming language. If they are
210 * single quote strings, then the backslashes are honored only when they
211 * precede another backslash or a single quote character.
213 * err: @code{NULL} is returned if the string(s) is/are mal-formed.
215 char*
216 ao_string_cook( char* pzScan, int* pLineCt )
218 int l = 0;
219 char q = *pzScan;
222 * It is a quoted string. Process the escape sequence characters
223 * (in the set "abfnrtv") and make sure we find a closing quote.
225 char* pzD = pzScan++;
226 char* pzS = pzScan;
228 if (pLineCt == NULL)
229 pLineCt = &l;
231 for (;;) {
233 * IF the next character is the quote character, THEN we may end the
234 * string. We end it unless the next non-blank character *after* the
235 * string happens to also be a quote. If it is, then we will change
236 * our quote character to the new quote character and continue
237 * condensing text.
239 while (*pzS == q) {
240 *pzD = NUL; /* This is probably the end of the line */
241 pzS++;
243 scan_for_quote:
244 while (isspace((int)*pzS))
245 if (*(pzS++) == '\n')
246 (*pLineCt)++;
249 * IF the next character is a quote character,
250 * THEN we will concatenate the strings.
252 switch (*pzS) {
253 case '"':
254 case '\'':
255 break;
257 case '/':
259 * Allow for a comment embedded in the concatenated string.
261 switch (pzS[1]) {
262 default: return NULL;
263 case '/':
265 * Skip to end of line
267 pzS = strchr( pzS, '\n' );
268 if (pzS == NULL)
269 return NULL;
270 (*pLineCt)++;
271 break;
273 case '*':
275 char* p = strstr( pzS+2, "*/" );
277 * Skip to terminating star slash
279 if (p == NULL)
280 return NULL;
281 while (pzS < p) {
282 if (*(pzS++) == '\n')
283 (*pLineCt)++;
286 pzS = p + 2;
289 goto scan_for_quote;
291 default:
293 * The next non-whitespace character is not a quote.
294 * The series of quoted strings has come to an end.
296 return pzS;
299 q = *(pzS++); /* assign new quote character and advance scan */
303 * We are inside a quoted string. Copy text.
305 switch (*(pzD++) = *(pzS++)) {
306 case NUL:
307 return NULL;
309 case '\n':
310 (*pLineCt)++;
311 break;
313 case '\\':
315 * IF we are escaping a new line,
316 * THEN drop both the escape and the newline from
317 * the result string.
319 if (*pzS == '\n') {
320 pzS++;
321 pzD--;
322 (*pLineCt)++;
326 * ELSE IF the quote character is '"' or '`',
327 * THEN we do the full escape character processing
329 else if (q != '\'') {
330 int ct = ao_string_cook_escape_char( pzS, pzD-1, (u_int)'\n' );
331 if (ct == 0)
332 return NULL;
334 pzS += ct;
335 } /* if (q != '\'') */
338 * OTHERWISE, we only process "\\", "\'" and "\#" sequences.
339 * The latter only to easily hide preprocessing directives.
341 else switch (*pzS) {
342 case '\\':
343 case '\'':
344 case '#':
345 pzD[-1] = *pzS++;
347 } /* switch (*(pzD++) = *(pzS++)) */
348 } /* for (;;) */
351 * Local Variables:
352 * mode: C
353 * c-file-style: "stroustrup"
354 * indent-tabs-mode: nil
355 * End:
356 * end of autoopts/cook.c */