]> git.ozlabs.org Git - ccan/blob - ccan/json_escape/json_escape.c
base64: fix for unsigned chars (e.g. ARM).
[ccan] / ccan / json_escape / json_escape.c
1 /* MIT (BSD) license - see LICENSE file for details */
2 #include <ccan/json_escape/json_escape.h>
3 #include <stdio.h>
4
5 struct json_escape *json_escape_string_(const tal_t *ctx,
6                                         const void *bytes, size_t len)
7 {
8         struct json_escape *esc;
9
10         esc = (void *)tal_arr_label(ctx, char, len + 1,
11                                     TAL_LABEL(struct json_escape, ""));
12         memcpy(esc->s, bytes, len);
13         esc->s[len] = '\0';
14         return esc;
15 }
16
17 bool json_escape_eq(const struct json_escape *a, const struct json_escape *b)
18 {
19         return streq(a->s, b->s);
20 }
21
22 bool json_escape_needed(const char *str, size_t len)
23 {
24         size_t i;
25         for (i = 0; i < len; i++) {
26                 if ((unsigned)str[i] < ' '
27                     || str[i] == 127
28                     || str[i] == '"'
29                     || str[i] == '\\')
30                         return true;
31         }
32         return false;
33 }
34
35 static struct json_escape *escape(const tal_t *ctx,
36                                   const char *str TAKES,
37                                   size_t len,
38                                   bool partial)
39 {
40         struct json_escape *esc;
41         size_t i, n;
42
43         /* Fast path: can steal, and nothing to escape. */
44         if (is_taken(str)
45             && tal_count(str) > len
46             && !json_escape_needed(str, len)) {
47                 taken(str);
48                 esc = (struct json_escape *)tal_steal(ctx, str);
49                 esc->s[len] = '\0';
50                 return esc;
51         }
52
53         /* Worst case: all \uXXXX */
54         esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
55
56         for (i = n = 0; i < len; i++, n++) {
57                 char escape = 0;
58                 switch (str[i]) {
59                 case '\n':
60                         escape = 'n';
61                         break;
62                 case '\b':
63                         escape = 'b';
64                         break;
65                 case '\f':
66                         escape = 'f';
67                         break;
68                 case '\t':
69                         escape = 't';
70                         break;
71                 case '\r':
72                         escape = 'r';
73                         break;
74                 case '\\':
75                         if (partial) {
76                                 /* Don't double-escape standard escapes. */
77                                 if (str[i+1] == 'n'
78                                     || str[i+1] == 'b'
79                                     || str[i+1] == 'f'
80                                     || str[i+1] == 't'
81                                     || str[i+1] == 'r'
82                                     || str[i+1] == '/'
83                                     || str[i+1] == '\\'
84                                     || str[i+1] == '"') {
85                                         escape = str[i+1];
86                                         i++;
87                                         break;
88                                 }
89                                 if (str[i+1] == 'u'
90                                     && cisxdigit(str[i+2])
91                                     && cisxdigit(str[i+3])
92                                     && cisxdigit(str[i+4])
93                                     && cisxdigit(str[i+5])) {
94                                             memcpy(esc->s + n, str + i, 6);
95                                             n += 5;
96                                             i += 5;
97                                             continue;
98                                 }
99                         } /* fall thru */
100                 case '"':
101                         escape = str[i];
102                         break;
103                 default:
104                         if ((unsigned)str[i] < ' ' || str[i] == 127) {
105                                 snprintf(esc->s + n, 7, "\\u%04X", str[i]);
106                                 n += 5;
107                                 continue;
108                         }
109                 }
110                 if (escape) {
111                         esc->s[n++] = '\\';
112                         esc->s[n] = escape;
113                 } else
114                         esc->s[n] = str[i];
115         }
116
117         esc->s[n] = '\0';
118         if (taken(str))
119                 tal_free(str);
120         return esc;
121 }
122
123 struct json_escape *json_partial_escape(const tal_t *ctx, const char *str TAKES)
124 {
125         return escape(ctx, str, strlen(str), true);
126 }
127
128 struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES)
129 {
130         return escape(ctx, str, strlen(str), false);
131 }
132
133 struct json_escape *json_escape_len(const tal_t *ctx, const char *str TAKES,
134                                     size_t len)
135 {
136         return escape(ctx, str, len, false);
137 }
138
139 /* By policy, we don't handle \u.  Use UTF-8. */
140 const char *json_escape_unescape(const tal_t *ctx, const struct json_escape *esc)
141 {
142         char *unesc = tal_arr(ctx, char, strlen(esc->s) + 1);
143         size_t i, n;
144
145         for (i = n = 0; esc->s[i]; i++, n++) {
146                 if (esc->s[i] != '\\') {
147                         unesc[n] = esc->s[i];
148                         continue;
149                 }
150
151                 i++;
152                 switch (esc->s[i]) {
153                 case 'n':
154                         unesc[n] = '\n';
155                         break;
156                 case 'b':
157                         unesc[n] = '\b';
158                         break;
159                 case 'f':
160                         unesc[n] = '\f';
161                         break;
162                 case 't':
163                         unesc[n] = '\t';
164                         break;
165                 case 'r':
166                         unesc[n] = '\r';
167                         break;
168                 case '/':
169                 case '\\':
170                 case '"':
171                         unesc[n] = esc->s[i];
172                         break;
173                 default:
174                         return tal_free(unesc);
175                 }
176         }
177
178         unesc[n] = '\0';
179         return unesc;
180 }