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  }