50#define PRETTY_OUT_PADDING 256
52#define PRETTY_INDENT_STEP 2
54#define PRETTY_TAG_NAME_MAX 31
58 if (n == 0)
return TRUE;
69 for (
int it = 0; it < depth; it++) {
78 if (!o->
buf)
return NULL;
93 while (*p && isspace((
unsigned char)*p)) p++;
94 if (*p !=
'{' && *p !=
'[')
return NULL;
95 cJSON* json = cJSON_Parse(text);
96 if (!json)
return NULL;
97 char* printed = cJSON_Print(json);
99 if (!printed)
return NULL;
101 if (!out) free(printed);
104 n_log(
LOG_ERR,
"n_pretty_json: built without cJSON support (HAVE_CJSON)");
111 "area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
112 "link",
"meta",
"param",
"source",
"track",
"wbr", NULL};
119 for (
size_t it = 0; list[it]; it++) {
120 if (strcmp(name, list[it]) == 0)
return TRUE;
126static void _tag_name(
const char* p,
char* name,
size_t name_size) {
130 while (*p && out + 1 < name_size &&
131 (isalnum((
unsigned char)*p) || *p ==
':' || *p ==
'-' || *p ==
'_')) {
132 name[out++] = (char)tolower((
unsigned char)*p);
145 if (*p == quote) quote = 0;
146 }
else if (*p ==
'"' || *p ==
'\'') {
148 }
else if (*p ==
'>') {
157static const char*
_stristr(
const char* haystack,
const char* needle) {
158 size_t nlen = strlen(needle);
159 if (nlen == 0)
return haystack;
160 for (
const char* p = haystack; *p; p++) {
161 if (strncasecmp(p, needle, nlen) == 0)
return p;
169 if (!
_out_mem(o, s, n))
return FALSE;
178 while (a < b && isspace((
unsigned char)s[a])) a++;
179 while (b > a && isspace((
unsigned char)s[b - 1])) b--;
180 if (b <= a)
return TRUE;
186 const char* p = text;
187 while (*p && isspace((
unsigned char)*p)) p++;
188 if (*p !=
'<')
return NULL;
197 const char* e = strchr(p,
'<');
198 size_t n = e ? (size_t)(e - p) : strlen(p);
205 if (p[1] ==
'!' && p[2] ==
'-' && p[3] ==
'-') {
206 const char* e = strstr(p + 4,
"-->");
207 size_t n = e ? (size_t)(e + 3 - p) : strlen(p);
212 if (strncasecmp(p,
"<![CDATA[", 9) == 0) {
213 const char* e = strstr(p + 9,
"]]>");
214 size_t n = e ? (size_t)(e + 3 - p) : strlen(p);
219 if (p[1] ==
'?' || p[1] ==
'!') {
222 size_t n = e ? (size_t)(e + 1 - p) : strlen(p);
230 size_t n = e ? (size_t)(e + 1 - p) : strlen(p);
231 int is_close = (p[1] ==
'/');
232 int self_close = (e && n >= 2 && p[n - 2] ==
'/');
235 if (is_close && depth > 0) depth--;
238 if (!ok || is_close || self_close || !e)
continue;
244 snprintf(closer,
sizeof(closer),
"</%s", name);
245 const char* end =
_stristr(p, closer);
246 size_t rn = end ? (size_t)(end - p) : strlen(p);
262 if (c == 0)
return TRUE;
263 return strchr(
"(,=:[!&|?;{}<>+-*%~^", c) != NULL;
269 static const char* kw[] = {
"await",
"case",
"delete",
"do",
"else",
"in",
270 "instanceof",
"new",
"of",
"return",
"throw",
271 "typeof",
"void",
"yield", NULL};
272 for (
size_t it = 0; kw[it]; it++) {
273 if (strlen(kw[it]) == n && strncmp(s, kw[it], n) == 0)
return TRUE;
300 if (!
_out_ch(&js->
o,
' '))
return FALSE;
326 if (s[it] ==
'\\' && s[it + 1]) {
330 if (s[it] == quote)
return it + 1;
331 if (s[it] ==
'\n')
break;
342 if (s[it] ==
'\\' && s[it + 1]) {
346 if (s[it] ==
'`')
return it + 1;
347 if (s[it] ==
'$' && s[it + 1] ==
'{') {
350 while (s[it] && braces > 0) {
351 if (s[it] ==
'{') braces++;
352 if (s[it] ==
'}') braces--;
368 if (s[it] ==
'\\' && s[it + 1]) {
372 if (s[it] ==
'\n')
return it;
374 if (s[it] ==
']') in_class = 0;
375 }
else if (s[it] ==
'[') {
377 }
else if (s[it] ==
'/') {
379 while (isalpha((
unsigned char)s[it])) it++;
389 if (!text[0])
return NULL;
391 JS_STATE js = {{NULL, 0, 0}, 0, 1, 0, 0, 0};
393 const char* p = text;
398 if (isspace((
unsigned char)c)) {
403 if (c ==
'/' && p[1] ==
'/') {
404 const char* e = strchr(p,
'\n');
405 size_t n = e ? (size_t)(e - p) : strlen(p);
410 if (c ==
'/' && p[1] ==
'*') {
411 const char* e = strstr(p + 2,
"*/");
412 size_t n = e ? (size_t)(e + 2 - p) : strlen(p);
418 if (c ==
'"' || c ==
'\'') {
443 if (isalpha((
unsigned char)c) || c ==
'_' || c ==
'$') {
446 while (p[n] && (isalnum((
unsigned char)p[n]) || p[n] ==
'_' || p[n] ==
'$')) n++;
453 if (c ==
'{' && paren == 0) {
461 if (c ==
'}' && paren == 0) {
469 while (*p && isspace((
unsigned char)*p) && *p !=
'\n') p++;
470 if (*p ==
';' || *p ==
',') {
478 if (c ==
';' && paren == 0) {
485 if (c ==
'(' || c ==
'[') paren++;
486 if ((c ==
')' || c ==
']') && paren > 0) paren--;
492 if (!ok || !js.
o.
buf) {
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
N_STR * n_pretty_xml(const char *text)
Re-indent an XML or HTML document (one tag or text run per line).
N_STR * n_pretty_js(const char *text)
Conservatively re-indent JavaScript (newlines at top-level braces and semicolons, two-space indent by...
N_STR * n_pretty_json(const char *text)
Reformat a JSON document with cJSON (parse + print).
size_t NSTRBYTE
N_STR base unit.
N_STR * char_to_nstr_nocopy(char *src)
Convert a char into a N_STR, direct use of linked source pointer.
int write_and_fit_ex(char **dest, NSTRBYTE *size, NSTRBYTE *written, const char *src, NSTRBYTE src_size, NSTRBYTE additional_padding)
Append src_size bytes from src to *dest, updating size/written, growing the buffer if needed.
A box including a string and his lenght.
Common headers and low-level functions & define.
static int _emit_trimmed(PRETTY_OUT *o, int depth, const char *s, size_t n)
emit s trimmed of leading/trailing whitespace as one indented line; empty-after-trim runs are skipped
static size_t _js_string_len(const char *s)
length of the string literal starting at s (quote to closing quote, escapes honored,...
static size_t _js_regex_len(const char *s)
length of the regex literal starting at s (slash to closing slash, escapes and character classes hono...
static size_t _js_template_len(const char *s)
length of the template literal starting at s (backtick to backtick, escapes honored,...
static int _js_token(JS_STATE *js, const char *s, size_t n)
emit a raw token of n bytes with leading indent/space handling
#define PRETTY_INDENT_STEP
spaces per indentation level
int indent
current indentation depth
int kw_regex
1 when the last token was a keyword that can precede a regex literal
static int _js_newline(JS_STATE *js)
terminate the current line unless already at a line start
static const char * PRETTY_RAWTEXT_ELEMENTS[]
HTML elements whose content is raw text (no nested markup)
int at_line_start
1 when nothing has been emitted on the current line yet
static const char * _find_tag_end(const char *p)
find the '>' closing a tag starting at p, honoring quoted attribute values; returns NULL when the tag...
int pending_space
1 when collapsed whitespace separates the previous and next token
NSTRBYTE written
meaningful bytes, excluding the terminating '\0'
static N_STR * _out_finish(PRETTY_OUT *o)
hand the buffer over to a new N_STR (no copy); the PRETTY_OUT is emptied
static int _emit_line(PRETTY_OUT *o, int depth, const char *s, size_t n)
emit one indented line holding n raw bytes of s
static int _out_mem(PRETTY_OUT *o, const char *s, size_t n)
append n bytes of s to the output, TRUE on success
char last_sig
last significant code character emitted (0 = none yet)
static int _js_regex_possible(char c)
whether a '/' may start a regex literal after last significant char c (start of input,...
PRETTY_OUT o
output buffer
static const char * PRETTY_VOID_ELEMENTS[]
HTML elements that never have content or a closing tag.
static const char * _stristr(const char *haystack, const char *needle)
case-insensitive search for needle in haystack
static int _js_regex_keyword(const char *s, size_t n)
whether the n bytes at s are a keyword after which an expression (and therefore a regex literal) can ...
#define PRETTY_TAG_NAME_MAX
longest element name kept for void/raw-text lookups
static int _js_lead(JS_STATE *js)
emit the indent or the collapsed space before a token, as appropriate
static int _out_indent(PRETTY_OUT *o, int depth)
append depth * PRETTY_INDENT_STEP spaces, TRUE on success
static int _out_ch(PRETTY_OUT *o, char c)
append a single character to the output, TRUE on success
char * buf
buffer, reallocated on growth
static int _name_in_list(const char *name, const char **list)
whether lowercase element name is in a NULL-terminated list
NSTRBYTE size
total allocation in bytes
#define PRETTY_OUT_PADDING
growth headroom so consecutive short appends share one realloc
static void _tag_name(const char *p, char *name, size_t name_size)
extract the lowercased element name from a tag starting at p ("<name" or "</name")
JS emitter state threaded through the helpers.
growing output buffer fed by write_and_fit_ex
Lexical pretty-printers for JSON, XML/HTML and JavaScript text.