123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /*
- * $Id: cook.c,v 4.12 2007/07/04 21:36:37 bkorb Exp $
- * Time-stamp: "2007-07-04 11:33:34 bkorb"
- *
- * This file contains the routines that deal with processing quoted strings
- * into an internal format.
- *
- * This file is part of AutoOpts, a companion to AutoGen.
- * AutoOpts is free software.
- * AutoOpts is copyright (c) 1992-2007 by Bruce Korb - all rights reserved
- *
- * AutoOpts is available under any one of two licenses. The license
- * in use must be one of these two and the choice is under the control
- * of the user of the license.
- *
- * The GNU Lesser General Public License, version 3 or later
- * See the files "COPYING.lgplv3" and "COPYING.gplv3"
- *
- * The Modified Berkeley Software Distribution License
- * See the file "COPYING.mbsd"
- *
- * These files have the following md5sums:
- *
- * 239588c55c22c60ffe159946a760a33e pkg/libopts/COPYING.gplv3
- * fa82ca978890795162346e661b47161a pkg/libopts/COPYING.lgplv3
- * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
- */
- /* = = = START-STATIC-FORWARD = = = */
- /* static forward declarations maintained by :mkfwd */
- /* = = = END-STATIC-FORWARD = = = */
- /*=export_func ao_string_cook_escape_char
- * private:
- *
- * what: escape-process a string fragment
- * arg: + char const* + pzScan + points to character after the escape +
- * arg: + char* + pRes + Where to put the result byte +
- * arg: + unsigned int + nl_ch + replacement char if scanned char is \n +
- *
- * ret-type: unsigned int
- * ret-desc: The number of bytes consumed processing the escaped character.
- *
- * doc:
- *
- * This function converts "t" into "\t" and all your other favorite
- * escapes, including numeric ones: hex and ocatal, too.
- * The returned result tells the caller how far to advance the
- * scan pointer (passed in). The default is to just pass through the
- * escaped character and advance the scan by one.
- *
- * Some applications need to keep an escaped newline, others need to
- * suppress it. This is accomplished by supplying a '\n' replacement
- * character that is different from \n, if need be. For example, use
- * 0x7F and never emit a 0x7F.
- *
- * err: @code{NULL} is returned if the string is mal-formed.
- =*/
- unsigned int
- ao_string_cook_escape_char( char const* pzIn, char* pRes, u_int nl )
- {
- unsigned int res = 1;
- switch (*pRes = *pzIn++) {
- case NUL: /* NUL - end of input string */
- return 0;
- case '\r':
- if (*pzIn != '\n')
- return 1;
- res++;
- /* FALLTHROUGH */
- case '\n': /* NL - emit newline */
- *pRes = (char)nl;
- return res;
- case 'a': *pRes = '\a'; break;
- case 'b': *pRes = '\b'; break;
- case 'f': *pRes = '\f'; break;
- case 'n': *pRes = '\n'; break;
- case 'r': *pRes = '\r'; break;
- case 't': *pRes = '\t'; break;
- case 'v': *pRes = '\v'; break;
- case 'x': /* HEX Escape */
- if (isxdigit( (int)*pzIn )) {
- unsigned int val;
- unsigned char ch = *pzIn++;
- if ((ch >= 'A') && (ch <= 'F'))
- val = 10 + (ch - 'A');
- else if ((ch >= 'a') && (ch <= 'f'))
- val = 10 + (ch - 'a');
- else val = ch - '0';
- ch = *pzIn;
- if (! isxdigit( ch )) {
- *pRes = val;
- res = 2;
- break;
- }
- val <<= 4;
- if ((ch >= 'A') && (ch <= 'F'))
- val += 10 + (ch - 'A');
- else if ((ch >= 'a') && (ch <= 'f'))
- val += 10 + (ch - 'a');
- else val += ch - '0';
- res = 3;
- *pRes = val;
- }
- break;
- default:
- /*
- * IF the character copied was an octal digit,
- * THEN set the output character to an octal value
- */
- if (isdigit( (int)*pRes ) && (*pRes < '8')) {
- unsigned int val = *pRes - '0';
- unsigned char ch = *pzIn++;
- /*
- * IF the second character is *not* an octal digit,
- * THEN save the value and bail
- */
- if ((ch < '0') || (ch > '7')) {
- *pRes = val;
- break;
- }
- val = (val<<3) + (ch - '0');
- ch = *pzIn;
- res = 2;
- /*
- * IF the THIRD character is *not* an octal digit,
- * THEN save the value and bail
- */
- if ((ch < '0') || (ch > '7')) {
- *pRes = val;
- break;
- }
- /*
- * IF the new value would not be too large,
- * THEN add on the third and last character value
- */
- if ((val<<3) < 0xFF) {
- val = (val<<3) + (ch - '0');
- res = 3;
- }
- *pRes = val;
- break;
- }
- }
- return res;
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * A quoted string has been found.
- * Find the end of it and compress any escape sequences.
- */
- /*=export_func ao_string_cook
- * private:
- *
- * what: concatenate and escape-process strings
- * arg: + char* + pzScan + The *MODIFIABLE* input buffer +
- * arg: + int* + pLineCt + The (possibly NULL) pointer to a line count +
- *
- * ret-type: char*
- * ret-desc: The address of the text following the processed strings.
- * The return value is NULL if the strings are ill-formed.
- *
- * doc:
- *
- * A series of one or more quoted strings are concatenated together.
- * If they are quoted with double quotes (@code{"}), then backslash
- * escapes are processed per the C programming language. If they are
- * single quote strings, then the backslashes are honored only when they
- * precede another backslash or a single quote character.
- *
- * err: @code{NULL} is returned if the string(s) is/are mal-formed.
- =*/
- char*
- ao_string_cook( char* pzScan, int* pLineCt )
- {
- int l = 0;
- char q = *pzScan;
- /*
- * It is a quoted string. Process the escape sequence characters
- * (in the set "abfnrtv") and make sure we find a closing quote.
- */
- char* pzD = pzScan++;
- char* pzS = pzScan;
- if (pLineCt == NULL)
- pLineCt = &l;
- for (;;) {
- /*
- * IF the next character is the quote character, THEN we may end the
- * string. We end it unless the next non-blank character *after* the
- * string happens to also be a quote. If it is, then we will change
- * our quote character to the new quote character and continue
- * condensing text.
- */
- while (*pzS == q) {
- *pzD = NUL; /* This is probably the end of the line */
- pzS++;
- scan_for_quote:
- while (isspace((int)*pzS))
- if (*(pzS++) == '\n')
- (*pLineCt)++;
- /*
- * IF the next character is a quote character,
- * THEN we will concatenate the strings.
- */
- switch (*pzS) {
- case '"':
- case '\'':
- break;
- case '/':
- /*
- * Allow for a comment embedded in the concatenated string.
- */
- switch (pzS[1]) {
- default: return NULL;
- case '/':
- /*
- * Skip to end of line
- */
- pzS = strchr( pzS, '\n' );
- if (pzS == NULL)
- return NULL;
- (*pLineCt)++;
- break;
- case '*':
- {
- char* p = strstr( pzS+2, "*/" );
- /*
- * Skip to terminating star slash
- */
- if (p == NULL)
- return NULL;
- while (pzS < p) {
- if (*(pzS++) == '\n')
- (*pLineCt)++;
- }
- pzS = p + 2;
- }
- }
- goto scan_for_quote;
- default:
- /*
- * The next non-whitespace character is not a quote.
- * The series of quoted strings has come to an end.
- */
- return pzS;
- }
- q = *(pzS++); /* assign new quote character and advance scan */
- }
- /*
- * We are inside a quoted string. Copy text.
- */
- switch (*(pzD++) = *(pzS++)) {
- case NUL:
- return NULL;
- case '\n':
- (*pLineCt)++;
- break;
- case '\\':
- /*
- * IF we are escaping a new line,
- * THEN drop both the escape and the newline from
- * the result string.
- */
- if (*pzS == '\n') {
- pzS++;
- pzD--;
- (*pLineCt)++;
- }
- /*
- * ELSE IF the quote character is '"' or '`',
- * THEN we do the full escape character processing
- */
- else if (q != '\'') {
- int ct = ao_string_cook_escape_char( pzS, pzD-1, (u_int)'\n' );
- if (ct == 0)
- return NULL;
- pzS += ct;
- } /* if (q != '\'') */
- /*
- * OTHERWISE, we only process "\\", "\'" and "\#" sequences.
- * The latter only to easily hide preprocessing directives.
- */
- else switch (*pzS) {
- case '\\':
- case '\'':
- case '#':
- pzD[-1] = *pzS++;
- }
- } /* switch (*(pzD++) = *(pzS++)) */
- } /* for (;;) */
- }
- /*
- * Local Variables:
- * mode: C
- * c-file-style: "stroustrup"
- * indent-tabs-mode: nil
- * End:
- * end of autoopts/cook.c */
|