1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
#ifndef UTIL_LINUX_CAREFULPUTC_H
#define UTIL_LINUX_CAREFULPUTC_H
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_WIDECHAR
#include <wctype.h>
#include <wchar.h>
#endif
#include <stdbool.h>
#include "cctype.h"
/*
* A puts() for use in write and wall (that sometimes are sgid tty).
* It avoids control and invalid characters.
* The locale of the recipient is nominally unknown,
* but it's a solid bet that it's compatible with the author's.
* Use soft_width=0 to disable wrapping.
*/
static inline int fputs_careful(const char * s, FILE *fp, const char ctrl, bool cr_lf, int soft_width)
{
int ret = 0, col = 0;
for (size_t slen = strlen(s); *s; ++s, --slen) {
if (*s == '\t')
col += (7 - (col % 8)) - 1;
else if (*s == '\r')
col = -1;
else if (*s == '\a')
--col;
if ((soft_width && col >= soft_width) || *s == '\n') {
if (soft_width) {
fprintf(fp, "%*s", soft_width - col, "");
col = 0;
}
ret = fputs(cr_lf ? "\r\n" : "\n", fp);
if (*s == '\n' || ret < 0)
goto wrote;
}
if (isprint(*s) || *s == '\a' || *s == '\t' || *s == '\r') {
ret = putc(*s, fp);
++col;
} else if (!c_isascii(*s)) {
#ifdef HAVE_WIDECHAR
wchar_t w;
size_t clen = mbtowc(&w, s, slen);
switch(clen) {
case (size_t)-2: // incomplete
case (size_t)-1: // EILSEQ
mbtowc(NULL, NULL, 0);
nonprint:
col += ret = fprintf(fp, "\\%3hho", *s);
break;
default:
if(!iswprint(w))
goto nonprint;
ret = fwrite(s, 1, clen, fp);
if (soft_width)
col += wcwidth(w);
s += clen - 1;
slen -= clen - 1;
break;
}
#else
col += ret = fprintf(fp, "\\%3hho", *s);
#endif
} else {
ret = fputs((char[]){ ctrl, *s ^ 0x40, '\0' }, fp);
col += 2;
}
wrote:
if (ret < 0)
return EOF;
}
return 0;
}
/*
* Requirements enumerated via testing (V8, Firefox, IE11):
*
* var charsToEscape = [];
* for (var i = 0; i < 65535; i += 1) {
* try {
* JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
* } catch (e) {
* charsToEscape.push(i);
* }
* }
*/
static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir)
{
const char *p;
fputc('"', out);
for (p = data; p && *p; p++) {
const unsigned char c = (unsigned char) *p;
/* From http://www.json.org
*
* The double-quote and backslashes would break out a string or
* init an escape sequence if not escaped.
*
* Note that single-quotes and forward slashes, while they're
* in the JSON spec, don't break double-quoted strings.
*/
if (c == '"' || c == '\\') {
fputc('\\', out);
fputc(c, out);
continue;
}
/* All non-control characters OK; do the case swap as required. */
if (c >= 0x20) {
fputc(dir == 1 ? toupper(c) :
dir == -1 ? tolower(c) : *p, out);
continue;
}
/* In addition, all chars under ' ' break Node's/V8/Chrome's, and
* Firefox's JSON.parse function
*/
switch (c) {
/* Handle short-hand cases to reduce output size. C
* has most of the same stuff here, so if there's an
* "Escape for C" function somewhere in the STL, we
* should probably be using it.
*/
case '\b':
fputs("\\b", out);
break;
case '\t':
fputs("\\t", out);
break;
case '\n':
fputs("\\n", out);
break;
case '\f':
fputs("\\f", out);
break;
case '\r':
fputs("\\r", out);
break;
default:
/* Other assorted control characters */
fprintf(out, "\\u00%02x", c);
break;
}
}
fputc('"', out);
}
static inline void fputs_quoted_case(const char *data, FILE *out, int dir)
{
const char *p;
fputc('"', out);
for (p = data; p && *p; p++) {
if ((unsigned char) *p == 0x22 || /* " */
(unsigned char) *p == 0x5c || /* \ */
(unsigned char) *p == 0x60 || /* ` */
(unsigned char) *p == 0x24 || /* $ */
!isprint((unsigned char) *p) ||
iscntrl((unsigned char) *p)) {
fprintf(out, "\\x%02x", (unsigned char) *p);
} else
fputc(dir == 1 ? toupper(*p) :
dir == -1 ? tolower(*p) :
*p, out);
}
fputc('"', out);
}
#define fputs_quoted(_d, _o) fputs_quoted_case(_d, _o, 0)
#define fputs_quoted_upper(_d, _o) fputs_quoted_case(_d, _o, 1)
#define fputs_quoted_lower(_d, _o) fputs_quoted_case(_d, _o, -1)
#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0)
#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
static inline void fputs_nonblank(const char *data, FILE *out)
{
const char *p;
for (p = data; p && *p; p++) {
if (isblank((unsigned char) *p) ||
(unsigned char) *p == 0x5c || /* \ */
!isprint((unsigned char) *p) ||
iscntrl((unsigned char) *p)) {
fprintf(out, "\\x%02x", (unsigned char) *p);
} else
fputc(*p, out);
}
}
#endif /* _CAREFULPUTC_H */
|