github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/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 // Set errno in case we die. 78 errno = 0; 79 size_t len; 80 char *copy; 81 if (str == NULL) { 82 die("cannot duplicate NULL string"); 83 } 84 len = strlen(str); 85 copy = malloc(len + 1); 86 if (copy == NULL) { 87 die("cannot allocate string copy (len: %zd)", len); 88 } 89 memcpy(copy, str, len + 1); 90 return copy; 91 } 92 93 int sc_must_snprintf(char *str, size_t size, const char *format, ...) 94 { 95 // Set errno in case we die. 96 errno = 0; 97 int n; 98 99 va_list va; 100 va_start(va, format); 101 n = vsnprintf(str, size, format, va); 102 va_end(va); 103 104 if (n < 0 || (size_t)n >= size) 105 die("cannot format string: %s", str); 106 107 return n; 108 } 109 110 size_t sc_string_append(char *dst, size_t dst_size, const char *str) 111 { 112 // Set errno in case we die. 113 errno = 0; 114 if (dst == NULL) { 115 die("cannot append string: buffer is NULL"); 116 } 117 if (str == NULL) { 118 die("cannot append string: string is NULL"); 119 } 120 size_t dst_len = strnlen(dst, dst_size); 121 if (dst_len == dst_size) { 122 die("cannot append string: dst is unterminated"); 123 } 124 125 size_t max_str_len = dst_size - dst_len; 126 size_t str_len = strnlen(str, max_str_len); 127 if (str_len == max_str_len) { 128 die("cannot append string: str is too long or unterminated"); 129 } 130 // Append the string 131 memcpy(dst + dst_len, str, str_len); 132 // Ensure we are terminated 133 dst[dst_len + str_len] = '\0'; 134 // return the new size 135 return strlen(dst); 136 } 137 138 size_t sc_string_append_char(char *dst, size_t dst_size, char c) 139 { 140 // Set errno in case we die. 141 errno = 0; 142 if (dst == NULL) { 143 die("cannot append character: buffer is NULL"); 144 } 145 size_t dst_len = strnlen(dst, dst_size); 146 if (dst_len == dst_size) { 147 die("cannot append character: dst is unterminated"); 148 } 149 size_t max_str_len = dst_size - dst_len; 150 if (max_str_len < 2) { 151 die("cannot append character: not enough space"); 152 } 153 if (c == 0) { 154 die("cannot append character: cannot append string terminator"); 155 } 156 // Append the character and terminate the string. 157 dst[dst_len + 0] = c; 158 dst[dst_len + 1] = '\0'; 159 // Return the new size 160 return dst_len + 1; 161 } 162 163 size_t sc_string_append_char_pair(char *dst, size_t dst_size, char c1, char c2) 164 { 165 // Set errno in case we die. 166 errno = 0; 167 if (dst == NULL) { 168 die("cannot append character pair: buffer is NULL"); 169 } 170 size_t dst_len = strnlen(dst, dst_size); 171 if (dst_len == dst_size) { 172 die("cannot append character pair: dst is unterminated"); 173 } 174 size_t max_str_len = dst_size - dst_len; 175 if (max_str_len < 3) { 176 die("cannot append character pair: not enough space"); 177 } 178 if (c1 == 0 || c2 == 0) { 179 die("cannot append character pair: cannot append string terminator"); 180 } 181 // Append the two characters and terminate the string. 182 dst[dst_len + 0] = c1; 183 dst[dst_len + 1] = c2; 184 dst[dst_len + 2] = '\0'; 185 // Return the new size 186 return dst_len + 2; 187 } 188 189 void sc_string_init(char *buf, size_t buf_size) 190 { 191 errno = 0; 192 if (buf == NULL) { 193 die("cannot initialize string, buffer is NULL"); 194 } 195 if (buf_size == 0) { 196 die("cannot initialize string, buffer is too small"); 197 } 198 buf[0] = '\0'; 199 } 200 201 void sc_string_quote(char *buf, size_t buf_size, const char *str) 202 { 203 // Set errno in case we die. 204 errno = 0; 205 if (str == NULL) { 206 die("cannot quote string: string is NULL"); 207 } 208 const char *hex = "0123456789abcdef"; 209 // NOTE: this also checks buf/buf_size sanity so that we don't have to. 210 sc_string_init(buf, buf_size); 211 sc_string_append_char(buf, buf_size, '"'); 212 for (unsigned char c; (c = *str) != 0; ++str) { 213 switch (c) { 214 // Pass ASCII letters and digits unmodified. 215 case '0' ... '9': 216 case 'A' ... 'Z': 217 case 'a' ... 'z': 218 // Pass most of the punctuation unmodified. 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 case '_': 245 case '`': 246 case '{': 247 case '|': 248 case '}': 249 case '~': 250 sc_string_append_char(buf, buf_size, c); 251 break; 252 // Escape special whitespace characters. 253 case '\n': 254 sc_string_append_char_pair(buf, buf_size, '\\', 'n'); 255 break; 256 case '\r': 257 sc_string_append_char_pair(buf, buf_size, '\\', 'r'); 258 break; 259 case '\t': 260 sc_string_append_char_pair(buf, buf_size, '\\', 't'); 261 break; 262 case '\v': 263 sc_string_append_char_pair(buf, buf_size, '\\', 'v'); 264 break; 265 // Escape the escape character. 266 case '\\': 267 sc_string_append_char_pair(buf, buf_size, '\\', '\\'); 268 break; 269 // Escape double quote character. 270 case '"': 271 sc_string_append_char_pair(buf, buf_size, '\\', '"'); 272 break; 273 // Escape everything else as a generic hexadecimal escape string. 274 default: 275 sc_string_append_char_pair(buf, buf_size, '\\', 'x'); 276 sc_string_append_char_pair(buf, buf_size, hex[c >> 4], 277 hex[c & 15]); 278 break; 279 } 280 } 281 sc_string_append_char(buf, buf_size, '"'); 282 }