github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/nsenter/escape.c (about) 1 #include <stdlib.h> 2 #include <string.h> 3 4 #ifdef ESCAPE_TEST 5 # include <assert.h> 6 # define test_assert(arg) assert(arg) 7 #else 8 # define test_assert(arg) 9 #endif 10 11 #define DEL '\x7f' 12 13 /* 14 * Poor man version of itoa with base=16 and input number from 0 to 15, 15 * represented by a char. Converts it to a single hex digit ('0' to 'f'). 16 */ 17 static char hex(char i) 18 { 19 test_assert(i >= 0 && i < 16); 20 21 if (i >= 0 && i < 10) { 22 return '0' + i; 23 } 24 if (i >= 10 && i < 16) { 25 return 'a' + i - 10; 26 } 27 return '?'; 28 } 29 30 /* 31 * Given the character, tells how many _extra_ characters are needed 32 * to JSON-escape it. If 0 is returned, the character does not need to 33 * be escaped. 34 */ 35 static int need_escape(char c) 36 { 37 switch (c) { 38 case '\\': 39 case '"': 40 case '\b': 41 case '\n': 42 case '\r': 43 case '\t': 44 case '\f': 45 return 1; 46 case DEL: // -> \u007f 47 return 5; 48 default: 49 if (c > 0 && c < ' ') { 50 // ASCII decimal 01 to 31 -> \u00xx 51 return 5; 52 } 53 return 0; 54 } 55 } 56 57 /* 58 * Escape the string so it can be used as a JSON string (per RFC4627, 59 * section 2.5 minimal requirements, plus the DEL (0x7f) character). 60 * 61 * It is expected that the argument is a string allocated via malloc. 62 * In case no escaping is needed, the original string is returned as is; 63 * otherwise, the original string is free'd, and the newly allocated 64 * escaped string is returned. Thus, in any case, the value returned 65 * need to be free'd by the caller. 66 */ 67 char *escape_json_string(char *s) 68 { 69 int i, j, len; 70 char *c, *out; 71 72 /* 73 * First, check if escaping is at all needed -- if not, we can avoid 74 * malloc and return the argument as is. While at it, count how much 75 * extra space is required. 76 * 77 * XXX: the counting code must be in sync with the escaping code 78 * (checked by test_assert()s below). 79 */ 80 for (i = j = 0; s[i] != '\0'; i++) { 81 j += need_escape(s[i]); 82 } 83 if (j == 0) { 84 // nothing to escape 85 return s; 86 } 87 88 len = i + j + 1; 89 out = malloc(len); 90 if (!out) { 91 free(s); 92 // As malloc failed, strdup can fail, too, so in the worst case 93 // scenario NULL will be returned from here. 94 return strdup("escape_json_string: out of memory"); 95 } 96 for (c = s, j = 0; *c != '\0'; c++) { 97 switch (*c) { 98 case '"': 99 case '\\': 100 test_assert(need_escape(*c) == 1); 101 out[j++] = '\\'; 102 out[j++] = *c; 103 continue; 104 } 105 if ((*c < 0 || *c >= ' ') && (*c != DEL)) { 106 // no escape needed 107 test_assert(need_escape(*c) == 0); 108 out[j++] = *c; 109 continue; 110 } 111 out[j++] = '\\'; 112 switch (*c) { 113 case '\b': 114 out[j++] = 'b'; 115 break; 116 case '\n': 117 out[j++] = 'n'; 118 break; 119 case '\r': 120 out[j++] = 'r'; 121 break; 122 case '\t': 123 out[j++] = 't'; 124 break; 125 case '\f': 126 out[j++] = 'f'; 127 break; 128 default: 129 test_assert(need_escape(*c) == 5); 130 out[j++] = 'u'; 131 out[j++] = '0'; 132 out[j++] = '0'; 133 out[j++] = hex(*c >> 4); 134 out[j++] = hex(*c & 0x0f); 135 } 136 } 137 test_assert(j + 1 == len); 138 out[j] = '\0'; 139 140 free(s); 141 return out; 142 }