github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/libsnap-confine-private/string-utils.c (about) 1 /* 2 * Copyright (C) 2016-2017 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #include "string-utils.h" 19 20 #include <errno.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 #include <string.h> 24 25 #include "utils.h" 26 27 bool sc_streq(const char *a, const char *b) 28 { 29 if (!a || !b) { 30 return false; 31 } 32 33 size_t alen = strlen(a); 34 size_t blen = strlen(b); 35 36 if (alen != blen) { 37 return false; 38 } 39 40 return strncmp(a, b, alen) == 0; 41 } 42 43 bool sc_endswith(const char *str, const char *suffix) 44 { 45 if (!str || !suffix) { 46 return false; 47 } 48 49 size_t xlen = strlen(suffix); 50 size_t slen = strlen(str); 51 52 if (slen < xlen) { 53 return false; 54 } 55 56 return strncmp(str - xlen + slen, suffix, xlen) == 0; 57 } 58 59 bool sc_startswith(const char *str, const char *prefix) 60 { 61 if (!str || !prefix) { 62 return false; 63 } 64 65 size_t xlen = strlen(prefix); 66 size_t slen = strlen(str); 67 68 if (slen < xlen) { 69 return false; 70 } 71 72 return strncmp(str, prefix, xlen) == 0; 73 } 74 75 char *sc_strdup(const char *str) 76 { 77 size_t len; 78 char *copy; 79 if (str == NULL) { 80 die("cannot duplicate NULL string"); 81 } 82 len = strlen(str); 83 copy = malloc(len + 1); 84 if (copy == NULL) { 85 die("cannot allocate string copy (len: %zd)", len); 86 } 87 memcpy(copy, str, len + 1); 88 return copy; 89 } 90 91 int sc_must_snprintf(char *str, size_t size, const char *format, ...) 92 { 93 int n; 94 95 va_list va; 96 va_start(va, format); 97 n = vsnprintf(str, size, format, va); 98 va_end(va); 99 100 if (n < 0 || (size_t)n >= size) 101 die("cannot format string: %s", str); 102 103 return n; 104 } 105 106 size_t sc_string_append(char *dst, size_t dst_size, const char *str) 107 { 108 // Set errno in case we die. 109 errno = 0; 110 if (dst == NULL) { 111 die("cannot append string: buffer is NULL"); 112 } 113 if (str == NULL) { 114 die("cannot append string: string is NULL"); 115 } 116 size_t dst_len = strnlen(dst, dst_size); 117 if (dst_len == dst_size) { 118 die("cannot append string: dst is unterminated"); 119 } 120 121 size_t max_str_len = dst_size - dst_len; 122 size_t str_len = strnlen(str, max_str_len); 123 if (str_len == max_str_len) { 124 die("cannot append string: str is too long or unterminated"); 125 } 126 // Append the string 127 memcpy(dst + dst_len, str, str_len); 128 // Ensure we are terminated 129 dst[dst_len + str_len] = '\0'; 130 // return the new size 131 return strlen(dst); 132 } 133 134 size_t sc_string_append_char(char *dst, size_t dst_size, char c) 135 { 136 // Set errno in case we die. 137 errno = 0; 138 if (dst == NULL) { 139 die("cannot append character: buffer is NULL"); 140 } 141 size_t dst_len = strnlen(dst, dst_size); 142 if (dst_len == dst_size) { 143 die("cannot append character: dst is unterminated"); 144 } 145 size_t max_str_len = dst_size - dst_len; 146 if (max_str_len < 2) { 147 die("cannot append character: not enough space"); 148 } 149 if (c == 0) { 150 die("cannot append character: cannot append string terminator"); 151 } 152 // Append the character and terminate the string. 153 dst[dst_len + 0] = c; 154 dst[dst_len + 1] = '\0'; 155 // Return the new size 156 return dst_len + 1; 157 } 158 159 size_t sc_string_append_char_pair(char *dst, size_t dst_size, char c1, char c2) 160 { 161 // Set errno in case we die. 162 errno = 0; 163 if (dst == NULL) { 164 die("cannot append character pair: buffer is NULL"); 165 } 166 size_t dst_len = strnlen(dst, dst_size); 167 if (dst_len == dst_size) { 168 die("cannot append character pair: dst is unterminated"); 169 } 170 size_t max_str_len = dst_size - dst_len; 171 if (max_str_len < 3) { 172 die("cannot append character pair: not enough space"); 173 } 174 if (c1 == 0 || c2 == 0) { 175 die("cannot append character pair: cannot append string terminator"); 176 } 177 // Append the two characters and terminate the string. 178 dst[dst_len + 0] = c1; 179 dst[dst_len + 1] = c2; 180 dst[dst_len + 2] = '\0'; 181 // Return the new size 182 return dst_len + 2; 183 } 184 185 void sc_string_init(char *buf, size_t buf_size) 186 { 187 errno = 0; 188 if (buf == NULL) { 189 die("cannot initialize string, buffer is NULL"); 190 } 191 if (buf_size == 0) { 192 die("cannot initialize string, buffer is too small"); 193 } 194 buf[0] = '\0'; 195 } 196 197 void sc_string_quote(char *buf, size_t buf_size, const char *str) 198 { 199 if (str == NULL) { 200 die("cannot quote string: string is NULL"); 201 } 202 const char *hex = "0123456789abcdef"; 203 // NOTE: this also checks buf/buf_size sanity so that we don't have to. 204 sc_string_init(buf, buf_size); 205 sc_string_append_char(buf, buf_size, '"'); 206 for (unsigned char c; (c = *str) != 0; ++str) { 207 switch (c) { 208 // Pass ASCII letters and digits unmodified. 209 case '0' ... '9': 210 case 'A' ... 'Z': 211 case 'a' ... 'z': 212 // Pass most of the punctuation unmodified. 213 case ' ': 214 case '!': 215 case '#': 216 case '$': 217 case '%': 218 case '&': 219 case '(': 220 case ')': 221 case '*': 222 case '+': 223 case ',': 224 case '-': 225 case '.': 226 case '/': 227 case ':': 228 case ';': 229 case '<': 230 case '=': 231 case '>': 232 case '?': 233 case '@': 234 case '[': 235 case '\'': 236 case ']': 237 case '^': 238 case '_': 239 case '`': 240 case '{': 241 case '|': 242 case '}': 243 case '~': 244 sc_string_append_char(buf, buf_size, c); 245 break; 246 // Escape special whitespace characters. 247 case '\n': 248 sc_string_append_char_pair(buf, buf_size, '\\', 'n'); 249 break; 250 case '\r': 251 sc_string_append_char_pair(buf, buf_size, '\\', 'r'); 252 break; 253 case '\t': 254 sc_string_append_char_pair(buf, buf_size, '\\', 't'); 255 break; 256 case '\v': 257 sc_string_append_char_pair(buf, buf_size, '\\', 'v'); 258 break; 259 // Escape the escape character. 260 case '\\': 261 sc_string_append_char_pair(buf, buf_size, '\\', '\\'); 262 break; 263 // Escape double quote character. 264 case '"': 265 sc_string_append_char_pair(buf, buf_size, '\\', '"'); 266 break; 267 // Escape everything else as a generic hexadecimal escape string. 268 default: 269 sc_string_append_char_pair(buf, buf_size, '\\', 'x'); 270 sc_string_append_char_pair(buf, buf_size, hex[c >> 4], 271 hex[c & 15]); 272 break; 273 } 274 } 275 sc_string_append_char(buf, buf_size, '"'); 276 }