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