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