busybox/libbb/process_escape_sequence.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Utility routines.
   4 *
   5 * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
   6 * and Vladimir Oleynik <dzo@simtreas.ru>
   7 *
   8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   9 */
  10#include "libbb.h"
  11
  12#define WANT_HEX_ESCAPES 1
  13
  14/* Usual "this only works for ascii compatible encodings" disclaimer. */
  15#undef _tolower
  16#define _tolower(X) ((X)|((char) 0x20))
  17
  18char FAST_FUNC bb_process_escape_sequence(const char **ptr)
  19{
  20        const char *q;
  21        unsigned num_digits;
  22        unsigned n;
  23        unsigned base;
  24
  25        num_digits = n = 0;
  26        base = 8;
  27        q = *ptr;
  28
  29        if (WANT_HEX_ESCAPES && *q == 'x') {
  30                ++q;
  31                base = 16;
  32                ++num_digits;
  33        }
  34
  35        /* bash requires leading 0 in octal escapes:
  36         * \02 works, \2 does not (prints \ and 2).
  37         * We treat \2 as a valid octal escape sequence. */
  38        do {
  39                unsigned r;
  40                unsigned d = (unsigned char)(*q) - '0';
  41#if WANT_HEX_ESCAPES
  42                if (d >= 10) {
  43                        d = (unsigned char)_tolower(*q) - 'a';
  44                        //d += 10;
  45                        /* The above would map 'A'-'F' and 'a'-'f' to 10-15,
  46                         * however, some chars like '@' would map to 9 < base.
  47                         * Do not allow that, map invalid chars to N > base:
  48                         */
  49                        if ((int)d >= 0)
  50                                d += 10;
  51                }
  52#endif
  53                if (d >= base) {
  54                        if (WANT_HEX_ESCAPES && base == 16) {
  55                                --num_digits;
  56                                if (num_digits == 0) {
  57                                        /* \x<bad_char>: return '\',
  58                                         * leave ptr pointing to x */
  59                                        return '\\';
  60                                }
  61                        }
  62                        break;
  63                }
  64
  65                r = n * base + d;
  66                if (r > UCHAR_MAX) {
  67                        break;
  68                }
  69
  70                n = r;
  71                ++q;
  72        } while (++num_digits < 3);
  73
  74        if (num_digits == 0) {
  75                /* Not octal or hex escape sequence.
  76                 * Is it one-letter one? */
  77
  78                /* bash builtin "echo -e '\ec'" interprets \e as ESC,
  79                 * but coreutils "/bin/echo -e '\ec'" does not.
  80                 * Manpages tend to support coreutils way.
  81                 * Update: coreutils added support for \e on 28 Oct 2009. */
  82                static const char charmap[] ALIGN1 = {
  83                        'a',  'b', 'e', 'f',  'n',  'r',  't',  'v',  '\\', '\0',
  84                        '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\',
  85                };
  86                const char *p = charmap;
  87                do {
  88                        if (*p == *q) {
  89                                q++;
  90                                break;
  91                        }
  92                } while (*++p != '\0');
  93                /* p points to found escape char or NUL,
  94                 * advance it and find what it translates to.
  95                 * Note that \NUL and unrecognized sequence \z return '\'
  96                 * and leave ptr pointing to NUL or z. */
  97                n = p[sizeof(charmap) / 2];
  98        }
  99
 100        *ptr = q;
 101
 102        return (char) n;
 103}
 104
 105char* FAST_FUNC strcpy_and_process_escape_sequences(char *dst, const char *src)
 106{
 107        while (1) {
 108                char c, c1;
 109                c = c1 = *src++;
 110                if (c1 == '\\')
 111                        c1 = bb_process_escape_sequence(&src);
 112                *dst = c1;
 113                if (c == '\0')
 114                        return dst;
 115                dst++;
 116        }
 117}
 118