Start 1.33.0 development cycle
[oweals/busybox.git] / libbb / percent_decode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5 //kbuild:lib-y += percent_decode.o
6
7 #include "libbb.h"
8
9 static unsigned hex_to_bin(unsigned char c)
10 {
11         unsigned v;
12
13         v = c - '0';
14         if (v <= 9)
15                 return v;
16         /* c | 0x20: letters to lower case, non-letters
17          * to (potentially different) non-letters */
18         v = (unsigned)(c | 0x20) - 'a';
19         if (v <= 5)
20                 return v + 10;
21         return ~0;
22 /* For testing:
23 void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
24 int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
25 t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
26 */
27 }
28
29 char* FAST_FUNC percent_decode_in_place(char *str, int strict)
30 {
31         /* note that decoded string is always shorter than original */
32         char *src = str;
33         char *dst = str;
34         char c;
35
36         while ((c = *src++) != '\0') {
37                 unsigned v;
38
39                 if (!strict && c == '+') {
40                         *dst++ = ' ';
41                         continue;
42                 }
43                 if (c != '%') {
44                         *dst++ = c;
45                         continue;
46                 }
47                 v = hex_to_bin(src[0]);
48                 if (v > 15) {
49  bad_hex:
50                         if (strict)
51                                 return NULL;
52                         *dst++ = '%';
53                         continue;
54                 }
55                 v = (v * 16) | hex_to_bin(src[1]);
56                 if (v > 255)
57                         goto bad_hex;
58                 if (strict && (v == '/' || v == '\0')) {
59                         /* caller takes it as indication of invalid
60                          * (dangerous wrt exploits) chars */
61                         return str + 1;
62                 }
63                 *dst++ = v;
64                 src += 2;
65         }
66         *dst = '\0';
67         return str;
68 }