(root)/
util-linux-2.39/
lib/
jsonwrt.c
       1  /*
       2   * JSON output formatting functions.
       3   *
       4   * No copyright is claimed.  This code is in the public domain; do with
       5   * it what you wish.
       6   *
       7   * Written by Karel Zak <kzak@redhat.com>
       8   */
       9  #include <stdio.h>
      10  #include <inttypes.h>
      11  #include <ctype.h>
      12  #include <cctype.h>
      13  
      14  #include "c.h"
      15  #include "jsonwrt.h"
      16  
      17  /*
      18   * Requirements enumerated via testing (V8, Firefox, IE11):
      19   *
      20   * var charsToEscape = [];
      21   * for (var i = 0; i < 65535; i += 1) {
      22   *	try {
      23   *		JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
      24   *	} catch (e) {
      25   *		charsToEscape.push(i);
      26   *	}
      27   * }
      28   */
      29  static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
      30  {
      31  	const char *p;
      32  
      33  	fputc('"', out);
      34  	for (p = data; p && *p; p++) {
      35  
      36  		const unsigned int c = (unsigned int) *p;
      37  
      38  		/* From http://www.json.org
      39  		 *
      40  		 * The double-quote and backslashes would break out a string or
      41  		 * init an escape sequence if not escaped.
      42  		 *
      43  		 * Note that single-quotes and forward slashes, while they're
      44  		 * in the JSON spec, don't break double-quoted strings.
      45  		 */
      46  		if (c == '"' || c == '\\') {
      47  			fputc('\\', out);
      48  			fputc(c, out);
      49  			continue;
      50  		}
      51  
      52  		/* All non-control characters OK; do the case swap as required. */
      53  		if (c >= 0x20) {
      54  			/*
      55  			 * Don't use locale sensitive ctype.h functions for regular
      56  			 * ASCII chars, because for example with Turkish locale
      57  			 * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'.
      58  			 */
      59  			if (c <= 127)
      60  				fputc(dir ==  1 ? c_toupper(c) :
      61  				      dir == -1 ? c_tolower(c) : *p, out);
      62  			else
      63  				fputc(dir ==  1 ? toupper(c) :
      64  				      dir == -1 ? tolower(c) : *p, out);
      65  			continue;
      66  		}
      67  
      68  		/* In addition, all chars under ' ' break Node's/V8/Chrome's, and
      69  		 * Firefox's JSON.parse function
      70  		 */
      71  		switch (c) {
      72  			/* Handle short-hand cases to reduce output size.  C
      73  			 * has most of the same stuff here, so if there's an
      74  			 * "Escape for C" function somewhere in the STL, we
      75  			 * should probably be using it.
      76  			 */
      77  			case '\b':
      78  				fputs("\\b", out);
      79  				break;
      80  			case '\t':
      81  				fputs("\\t", out);
      82  				break;
      83  			case '\n':
      84  				fputs("\\n", out);
      85  				break;
      86  			case '\f':
      87  				fputs("\\f", out);
      88  				break;
      89  			case '\r':
      90  				fputs("\\r", out);
      91  				break;
      92  			default:
      93  				/* Other assorted control characters */
      94  				fprintf(out, "\\u00%02x", c);
      95  				break;
      96  		}
      97  	}
      98  	fputc('"', out);
      99  }
     100  
     101  #define fputs_quoted_json(_d, _o)       fputs_quoted_case_json(_d, _o, 0)
     102  #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
     103  #define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
     104  
     105  void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
     106  {
     107  	fmt->out = out;
     108  	fmt->indent = indent;
     109  	fmt->after_close = 0;
     110  }
     111  
     112  int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt)
     113  {
     114  	return fmt->out == NULL ? 0 : 1;
     115  }
     116  
     117  void ul_jsonwrt_indent(struct ul_jsonwrt *fmt)
     118  {
     119  	int i;
     120  
     121  	for (i = 0; i < fmt->indent; i++)
     122  		fputs("   ", fmt->out);
     123  }
     124  
     125  void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
     126  {
     127  	if (name) {
     128  		if (fmt->after_close)
     129  			fputs(",\n", fmt->out);
     130  		ul_jsonwrt_indent(fmt);
     131  		fputs_quoted_json_lower(name, fmt->out);
     132  	} else {
     133  		if (fmt->after_close)
     134  			fputs(",", fmt->out);
     135  		else
     136  			ul_jsonwrt_indent(fmt);
     137  	}
     138  
     139  	switch (type) {
     140  	case UL_JSON_OBJECT:
     141  		fputs(name ? ": {\n" : "{\n", fmt->out);
     142  		fmt->indent++;
     143  		break;
     144  	case UL_JSON_ARRAY:
     145  		fputs(name ? ": [\n" : "[\n", fmt->out);
     146  		fmt->indent++;
     147  		break;
     148  	case UL_JSON_VALUE:
     149  		fputs(name ? ": " : " ", fmt->out);
     150  		break;
     151  	}
     152  	fmt->after_close = 0;
     153  }
     154  
     155  void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
     156  {
     157  	if (fmt->indent == 1) {
     158  		fputs("\n}\n", fmt->out);
     159  		fmt->indent--;
     160  		fmt->after_close = 1;
     161  		return;
     162  	}
     163  	assert(fmt->indent > 0);
     164  
     165  	switch (type) {
     166  	case UL_JSON_OBJECT:
     167  		fmt->indent--;
     168  		fputc('\n', fmt->out);
     169  		ul_jsonwrt_indent(fmt);
     170  		fputs("}", fmt->out);
     171  		break;
     172  	case UL_JSON_ARRAY:
     173  		fmt->indent--;
     174  		fputc('\n', fmt->out);
     175  		ul_jsonwrt_indent(fmt);
     176  		fputs("]", fmt->out);
     177  		break;
     178  	case UL_JSON_VALUE:
     179  		break;
     180  	}
     181  
     182  	fmt->after_close = 1;
     183  }
     184  
     185  void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
     186  			const char *name, const char *data)
     187  {
     188  	ul_jsonwrt_value_open(fmt, name);
     189  	if (data && *data)
     190  		fputs(data, fmt->out);
     191  	else
     192  		fputs("null", fmt->out);
     193  	ul_jsonwrt_value_close(fmt);
     194  }
     195  
     196  void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
     197  			const char *name, const char *data)
     198  {
     199  	ul_jsonwrt_value_open(fmt, name);
     200  	if (data && *data)
     201  		fputs_quoted_json(data, fmt->out);
     202  	else
     203  		fputs("null", fmt->out);
     204  	ul_jsonwrt_value_close(fmt);
     205  }
     206  
     207  void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
     208  			const char *name, uint64_t data)
     209  {
     210  	ul_jsonwrt_value_open(fmt, name);
     211  	fprintf(fmt->out, "%"PRIu64, data);
     212  	ul_jsonwrt_value_close(fmt);
     213  }
     214  
     215  void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
     216  			const char *name, int data)
     217  {
     218  	ul_jsonwrt_value_open(fmt, name);
     219  	fputs(data ? "true" : "false", fmt->out);
     220  	ul_jsonwrt_value_close(fmt);
     221  }
     222  
     223  void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt,
     224  			const char *name)
     225  {
     226  	ul_jsonwrt_value_open(fmt, name);
     227  	fputs("null", fmt->out);
     228  	ul_jsonwrt_value_close(fmt);
     229  }