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