github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  }