quote.con commit Merge branch 'fixes' (4ae22d9)
   1#include "cache.h"
   2#include "quote.h"
   3
   4/* Help to copy the thing properly quoted for the shell safety.
   5 * any single quote is replaced with '\'', any exclamation point
   6 * is replaced with '\!', and the whole thing is enclosed in a
   7 *
   8 * E.g.
   9 *  original     sq_quote     result
  10 *  name     ==> name      ==> 'name'
  11 *  a b      ==> a b       ==> 'a b'
  12 *  a'b      ==> a'\''b    ==> 'a'\''b'
  13 *  a!b      ==> a'\!'b    ==> 'a'\!'b'
  14 */
  15#undef EMIT
  16#define EMIT(x) ( (++len < n) && (*bp++ = (x)) )
  17
  18size_t sq_quote_buf(char *dst, size_t n, const char *src)
  19{
  20        char c;
  21        char *bp = dst;
  22        size_t len = 0;
  23
  24        EMIT('\'');
  25        while ((c = *src++)) {
  26                if (c == '\'' || c == '!') {
  27                        EMIT('\'');
  28                        EMIT('\\');
  29                        EMIT(c);
  30                        EMIT('\'');
  31                } else {
  32                        EMIT(c);
  33                }
  34        }
  35        EMIT('\'');
  36
  37        if ( n )
  38                *bp = 0;
  39
  40        return len;
  41}
  42
  43char *sq_quote(const char *src)
  44{
  45        char *buf;
  46        size_t cnt;
  47
  48        cnt = sq_quote_buf(NULL, 0, src) + 1;
  49        buf = xmalloc(cnt);
  50        sq_quote_buf(buf, cnt, src);
  51
  52        return buf;
  53}
  54
  55/*
  56 * C-style name quoting.
  57 *
  58 * Does one of three things:
  59 *
  60 * (1) if outbuf and outfp are both NULL, inspect the input name and
  61 *     counts the number of bytes that are needed to hold c_style
  62 *     quoted version of name, counting the double quotes around
  63 *     it but not terminating NUL, and returns it.  However, if name
  64 *     does not need c_style quoting, it returns 0.
  65 *
  66 * (2) if outbuf is not NULL, it must point at a buffer large enough
  67 *     to hold the c_style quoted version of name, enclosing double
  68 *     quotes, and terminating NUL.  Fills outbuf with c_style quoted
  69 *     version of name enclosed in double-quote pair.  Return value
  70 *     is undefined.
  71 *
  72 * (3) if outfp is not NULL, outputs c_style quoted version of name,
  73 *     but not enclosed in double-quote pair.  Return value is undefined.
  74 */
  75
  76int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
  77{
  78#undef EMIT
  79#define EMIT(c) \
  80        (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++))
  81
  82#define EMITQ() EMIT('\\')
  83
  84        const char *sp;
  85        int ch, count = 0, needquote = 0;
  86
  87        if (!no_dq)
  88                EMIT('"');
  89        for (sp = name; (ch = *sp++); ) {
  90
  91                if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
  92                    (ch == 0177)) {
  93                        needquote = 1;
  94                        switch (ch) {
  95                        case '\a': EMITQ(); ch = 'a'; break;
  96                        case '\b': EMITQ(); ch = 'b'; break;
  97                        case '\f': EMITQ(); ch = 'f'; break;
  98                        case '\n': EMITQ(); ch = 'n'; break;
  99                        case '\r': EMITQ(); ch = 'r'; break;
 100                        case '\t': EMITQ(); ch = 't'; break;
 101                        case '\v': EMITQ(); ch = 'v'; break;
 102
 103                        case '\\': /* fallthru */
 104                        case '"': EMITQ(); break;
 105                        case ' ':
 106                                break;
 107                        default:
 108                                /* octal */
 109                                EMITQ();
 110                                EMIT(((ch >> 6) & 03) + '0');
 111                                EMIT(((ch >> 3) & 07) + '0');
 112                                ch = (ch & 07) + '0';
 113                                break;
 114                        }
 115                }
 116                EMIT(ch);
 117        }
 118        if (!no_dq)
 119                EMIT('"');
 120        if (outbuf)
 121                *outbuf = 0;
 122
 123        return needquote ? count : 0;
 124}
 125
 126/*
 127 * C-style name unquoting.
 128 *
 129 * Quoted should point at the opening double quote.  Returns
 130 * an allocated memory that holds unquoted name, which the caller
 131 * should free when done.  Updates endp pointer to point at
 132 * one past the ending double quote if given.
 133 */
 134
 135char *unquote_c_style(const char *quoted, const char **endp)
 136{
 137        const char *sp;
 138        char *name = NULL, *outp = NULL;
 139        int count = 0, ch, ac;
 140
 141#undef EMIT
 142#define EMIT(c) (outp ? (*outp++ = (c)) : (count++))
 143
 144        if (*quoted++ != '"')
 145                return NULL;
 146
 147        while (1) {
 148                /* first pass counts and allocates, second pass fills */
 149                for (sp = quoted; (ch = *sp++) != '"'; ) {
 150                        if (ch == '\\') {
 151                                switch (ch = *sp++) {
 152                                case 'a': ch = '\a'; break;
 153                                case 'b': ch = '\b'; break;
 154                                case 'f': ch = '\f'; break;
 155                                case 'n': ch = '\n'; break;
 156                                case 'r': ch = '\r'; break;
 157                                case 't': ch = '\t'; break;
 158                                case 'v': ch = '\v'; break;
 159
 160                                case '\\': case '"':
 161                                        break; /* verbatim */
 162
 163                                case '0'...'7':
 164                                        /* octal */
 165                                        ac = ((ch - '0') << 6);
 166                                        if ((ch = *sp++) < '0' || '7' < ch)
 167                                                return NULL;
 168                                        ac |= ((ch - '0') << 3);
 169                                        if ((ch = *sp++) < '0' || '7' < ch)
 170                                                return NULL;
 171                                        ac |= (ch - '0');
 172                                        ch = ac;
 173                                        break;
 174                                default:
 175                                        return NULL; /* malformed */
 176                                }
 177                        }
 178                        EMIT(ch);
 179                }
 180
 181                if (name) {
 182                        *outp = 0;
 183                        if (endp)
 184                                *endp = sp;
 185                        return name;
 186                }
 187                outp = name = xmalloc(count + 1);
 188        }
 189}
 190
 191void write_name_quoted(const char *prefix, const char *name,
 192                       int quote, FILE *out)
 193{
 194        int needquote;
 195
 196        if (!quote) {
 197        no_quote:
 198                if (prefix && prefix[0])
 199                        fputs(prefix, out);
 200                fputs(name, out);
 201                return;
 202        }
 203
 204        needquote = 0;
 205        if (prefix && prefix[0])
 206                needquote = quote_c_style(prefix, NULL, NULL, 0);
 207        if (!needquote)
 208                needquote = quote_c_style(name, NULL, NULL, 0);
 209        if (needquote) {
 210                fputc('"', out);
 211                if (prefix && prefix[0])
 212                        quote_c_style(prefix, NULL, out, 1);
 213                quote_c_style(name, NULL, out, 1);
 214                fputc('"', out);
 215        }
 216        else
 217                goto no_quote;
 218}