1 #ifndef UTIL_LINUX_CAREFULPUTC_H
2 #define UTIL_LINUX_CAREFULPUTC_H
3 
4 /*
5  * A putc() for use in write and wall (that sometimes are sgid tty).
6  * It avoids control characters in our locale, and also ASCII control
7  * characters.   Note that the locale of the recipient is unknown.
8 */
9 #include <stdio.h>
10 #include <string.h>
11 #include <ctype.h>
12 
13 #include "cctype.h"
14 
fputc_careful(int c,FILE * fp,const char fail)15 static inline int fputc_careful(int c, FILE *fp, const char fail)
16 {
17 	int ret;
18 
19 	if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
20 		ret = putc(c, fp);
21 	else if (!c_isascii(c))
22 		ret = fprintf(fp, "\\%3o", (unsigned char)c);
23 	else {
24 		ret = putc(fail, fp);
25 		if (ret != EOF)
26 			ret = putc(c ^ 0x40, fp);
27 	}
28 	return (ret < 0) ? EOF : 0;
29 }
30 
31 /*
32  * Requirements enumerated via testing (V8, Firefox, IE11):
33  *
34  * var charsToEscape = [];
35  * for (var i = 0; i < 65535; i += 1) {
36  *	try {
37  *		JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
38  *	} catch (e) {
39  *		charsToEscape.push(i);
40  *	}
41  * }
42  */
fputs_quoted_case_json(const char * data,FILE * out,int dir)43 static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir)
44 {
45 	const char *p;
46 
47 	fputc('"', out);
48 	for (p = data; p && *p; p++) {
49 
50 		const unsigned char c = (unsigned char) *p;
51 
52 		/* From http://www.json.org
53 		 *
54 		 * The double-quote and backslashes would break out a string or
55 		 * init an escape sequence if not escaped.
56 		 *
57 		 * Note that single-quotes and forward slashes, while they're
58 		 * in the JSON spec, don't break double-quoted strings.
59 		 */
60 		if (c == '"' || c == '\\') {
61 			fputc('\\', out);
62 			fputc(c, out);
63 			continue;
64 		}
65 
66 		/* All non-control characters OK; do the case swap as required. */
67 		if (c >= 0x20) {
68 			fputc(dir ==  1 ? toupper(c) :
69 			      dir == -1 ? tolower(c) : *p, out);
70 			continue;
71 		}
72 
73 		/* In addition, all chars under ' ' break Node's/V8/Chrome's, and
74 		 * Firefox's JSON.parse function
75 		 */
76 		switch (c) {
77 			/* Handle short-hand cases to reduce output size.  C
78 			 * has most of the same stuff here, so if there's an
79 			 * "Escape for C" function somewhere in the STL, we
80 			 * should probably be using it.
81 			 */
82 			case '\b':
83 				fputs("\\b", out);
84 				break;
85 			case '\t':
86 				fputs("\\t", out);
87 				break;
88 			case '\n':
89 				fputs("\\n", out);
90 				break;
91 			case '\f':
92 				fputs("\\f", out);
93 				break;
94 			case '\r':
95 				fputs("\\r", out);
96 				break;
97 			default:
98 				/* Other assorted control characters */
99 				fprintf(out, "\\u00%02x", c);
100 				break;
101 		}
102 	}
103 	fputc('"', out);
104 }
105 
106 
fputs_quoted_case(const char * data,FILE * out,int dir)107 static inline void fputs_quoted_case(const char *data, FILE *out, int dir)
108 {
109 	const char *p;
110 
111 	fputc('"', out);
112 	for (p = data; p && *p; p++) {
113 		if ((unsigned char) *p == 0x22 ||		/* " */
114 		    (unsigned char) *p == 0x5c ||		/* \ */
115 		    (unsigned char) *p == 0x60 ||		/* ` */
116 		    (unsigned char) *p == 0x24 ||		/* $ */
117 		    !isprint((unsigned char) *p) ||
118 		    iscntrl((unsigned char) *p)) {
119 
120 			fprintf(out, "\\x%02x", (unsigned char) *p);
121 		} else
122 			fputc(dir ==  1 ? toupper(*p) :
123 			      dir == -1 ? tolower(*p) :
124 			      *p, out);
125 	}
126 	fputc('"', out);
127 }
128 
129 #define fputs_quoted(_d, _o)		fputs_quoted_case(_d, _o, 0)
130 #define fputs_quoted_upper(_d, _o)	fputs_quoted_case(_d, _o, 1)
131 #define fputs_quoted_lower(_d, _o)	fputs_quoted_case(_d, _o, -1)
132 
133 #define fputs_quoted_json(_d, _o)       fputs_quoted_case_json(_d, _o, 0)
134 #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
135 #define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
136 
fputs_nonblank(const char * data,FILE * out)137 static inline void fputs_nonblank(const char *data, FILE *out)
138 {
139 	const char *p;
140 
141 	for (p = data; p && *p; p++) {
142 		if (isblank((unsigned char) *p) ||
143 		    (unsigned char) *p == 0x5c ||		/* \ */
144 		    !isprint((unsigned char) *p) ||
145 		    iscntrl((unsigned char) *p)) {
146 
147 			fprintf(out, "\\x%02x", (unsigned char) *p);
148 
149 		} else
150 			fputc(*p, out);
151 	}
152 }
153 
154 
155 #endif  /*  _CAREFULPUTC_H  */
156