123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- /*
- * $Id: cook.c,v 4.1 2005/02/14 17:03:54 bkorb Exp $
- * Time-stamp: "2005-02-14 07:58:28 bkorb"
- *
- * This file contains the routines that deal with processing quoted strings
- * into an internal format.
- */
- /*
- * Automated Options copyright 1992-2005 Bruce Korb
- *
- * Automated Options is free software.
- * You may redistribute it and/or modify it under the terms of the
- * GNU General Public License, as published by the Free Software
- * Foundation; either version 2, or (at your option) any later version.
- *
- * Automated Options is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Automated Options. See the file "COPYING". If not,
- * write to: The Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * As a special exception, Bruce Korb gives permission for additional
- * uses of the text contained in his release of AutoOpts.
- *
- * The exception is that, if you link the AutoOpts library with other
- * files to produce an executable, this does not by itself cause the
- * resulting executable to be covered by the GNU General Public License.
- * Your use of that executable is in no way restricted on account of
- * linking the AutoOpts library code into it.
- *
- * This exception does not however invalidate any other reasons why
- * the executable file might be covered by the GNU General Public License.
- *
- * This exception applies only to the code released by Bruce Korb under
- * the name AutoOpts. If you copy code from other sources under the
- * General Public License into a copy of AutoOpts, as the General Public
- * License permits, the exception does not apply to the code that you add
- * in this way. To avoid misleading anyone as to the status of such
- * modified files, you must delete this exception notice from them.
- *
- * If you write modifications of your own for AutoOpts, it is your choice
- * whether to permit this exception to apply to your modifications.
- * If you do not wish that, delete this exception notice.
- */
- /* = = = 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: + const char* + pzScan + points to character after the escape +
- * arg: + char* + pRes + Where to put the result byte +
- * arg: + char + 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(s) is/are mal-formed.
- =*/
- unsigned int
- ao_string_cook_escape_char( const char* pzIn, char* pRes, char 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 = 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( *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( *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(*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, '\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"
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- * end of autoopts/cook.c */
|