Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_pretty.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
26
27#include "nilorea/n_common.h"
28#include "nilorea/n_log.h"
29#include "nilorea/n_pretty.h"
30#include "nilorea/n_str.h"
31
32#include <stdio.h>
33#include <string.h>
34#include <unistd.h>
35
36static int failures = 0;
37
38/* Parse the -V LOG_LEVEL verbosity argument. */
39static void process_args(int argc, char** argv) {
40 int opt = 0;
41 while ((opt = getopt(argc, argv, "hvV:")) != EOF) {
42 switch (opt) {
43 case 'V':
44 if (!strncmp("LOG_NULL", optarg, 8))
46 else if (!strncmp("LOG_NOTICE", optarg, 10))
48 else if (!strncmp("LOG_INFO", optarg, 8))
50 else if (!strncmp("LOG_ERR", optarg, 7))
52 else if (!strncmp("LOG_DEBUG", optarg, 9))
54 else {
55 fprintf(stderr, "Unknown log level %s\n", optarg);
56 exit(1);
57 }
58 break;
59 case 'v':
60 fprintf(stderr, "ex_pretty\n");
61 exit(1);
62 default:
63 fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
64 exit(1);
65 }
66 }
67}
68
69static void expect_true(const char* label, int cond) {
70 if (!cond) {
71 n_log(LOG_ERR, "%s: condition false", label);
72 failures++;
73 }
74}
75
76static void expect_contains(const char* label, const N_STR* out, const char* needle) {
77 if (!out || !out->data || !strstr(out->data, needle)) {
78 n_log(LOG_ERR, "%s: output does not contain '%s' (got: %s)",
79 label, needle, (out && out->data) ? out->data : "NULL");
80 failures++;
81 }
82}
83
84int main(int argc, char** argv) {
86 process_args(argc, argv);
87
88 /* JSON */
89 {
90 N_STR* out = n_pretty_json("{\"a\":1,\"b\":[true,null],\"c\":\"x\"}");
91 expect_true("json valid returns output", out != NULL);
92 expect_contains("json output is multi-line", out, "\n");
93 expect_contains("json key survives", out, "\"a\":");
94 free_nstr(&out);
95
96 out = n_pretty_json("this is not json");
97 expect_true("json garbage returns NULL", out == NULL);
98 free_nstr(&out);
99
100 out = n_pretty_json("{\"a\":");
101 expect_true("json truncated returns NULL", out == NULL);
102 free_nstr(&out);
103 }
104
105 /* JSON, lenient: what the parsing printer refuses still has to become
106 readable, since a body cut at a size limit is the common case */
107 {
108 N_STR* out = n_pretty_json_lenient("{\"a\":1,\"b\":[true,null],\"c\":\"x\"}");
109 expect_true("lenient valid returns output", out != NULL);
110 expect_contains("lenient indents members", out, "\n \"a\": 1,\n");
111 expect_contains("lenient indents nested values", out, "\n true,\n");
112 expect_contains("lenient closes at depth", out, "\n}\n");
113 free_nstr(&out);
114
115 out = n_pretty_json_lenient("{\"a\":1,\"b\":{\"c\":\"trunca");
116 expect_true("lenient truncated returns output", out != NULL);
117 expect_contains("lenient keeps the cut string", out, "\"trunca");
118 expect_contains("lenient still broke the line", out, "\n");
119 free_nstr(&out);
120
121 /* punctuation inside a string must not break the line */
122 out = n_pretty_json_lenient("{\"a\":\"x,y{z}\"}");
123 expect_true("lenient string returns output", out != NULL);
124 expect_contains("lenient copies a string verbatim", out, "\"x,y{z}\"");
125 free_nstr(&out);
126
127 /* an empty container stays on its line rather than opening a level */
128 out = n_pretty_json_lenient("{\"a\":[],\"b\":{ }}");
129 expect_true("lenient empty container returns output", out != NULL);
130 expect_contains("lenient keeps [] together", out, "\"a\": [],");
131 expect_contains("lenient keeps {} together", out, "\"b\": {}");
132 free_nstr(&out);
133 }
134
135 /* XML / HTML */
136 {
137 N_STR* out = n_pretty_xml("<root><a x=\"1\"><b>hi</b></a></root>");
138 expect_true("xml valid returns output", out != NULL);
139 expect_contains("xml child is indented", out, "\n <a x=\"1\">\n");
140 expect_contains("xml grandchild is indented", out, "\n <b>\n");
141 expect_contains("xml text is indented", out, "\n hi\n");
142 expect_contains("xml close returns to depth", out, "\n</root>\n");
143 free_nstr(&out);
144
145 /* void element must not indent what follows; comments pass through */
146 out = n_pretty_xml("<div><br><!-- note --><span>x</span></div>");
147 expect_true("html returns output", out != NULL);
148 expect_contains("html br stays at depth", out, "\n <br>\n");
149 expect_contains("html comment kept", out, "\n <!-- note -->\n");
150 expect_contains("html span stays at depth", out, "\n <span>\n");
151 free_nstr(&out);
152
153 /* script content is raw text: the inner '<' is not a tag */
154 out = n_pretty_xml("<html><script>if(a<b){c();}</script></html>");
155 expect_true("html script returns output", out != NULL);
156 expect_contains("script content verbatim", out, "if(a<b){c();}");
157 expect_contains("script close tag kept", out, "</script>");
158 free_nstr(&out);
159
160 /* attribute values may contain '>' */
161 out = n_pretty_xml("<a title=\"1 > 0\">t</a>");
162 expect_true("xml quoted gt returns output", out != NULL);
163 expect_contains("xml quoted gt kept in tag", out, "<a title=\"1 > 0\">");
164 free_nstr(&out);
165
166 out = n_pretty_xml("plain text, no markup");
167 expect_true("xml non-markup returns NULL", out == NULL);
168 free_nstr(&out);
169 }
170
171 /* JavaScript */
172 {
173 N_STR* out = n_pretty_js("function f(a){if(a){return 1;}else{return 2;}}var x=f(1);");
174 expect_true("js valid returns output", out != NULL);
175 expect_contains("js opens a block", out, "{\n");
176 expect_contains("js indents body", out, "\n if(a){\n");
177 expect_contains("js indents nested body", out, "\n return 1;\n");
178 expect_contains("js statement gets its own line", out, "\nvar x=f(1);\n");
179 free_nstr(&out);
180
181 /* structural characters inside strings/regex must not break lines */
182 out = n_pretty_js("var s=\"x;y{z}\";var re=/a\\/b;{/g;var t='a;b';");
183 expect_true("js literals return output", out != NULL);
184 expect_contains("js double-quoted string intact", out, "\"x;y{z}\"");
185 expect_contains("js regex intact", out, "/a\\/b;{/g");
186 expect_contains("js single-quoted string intact", out, "'a;b'");
187 free_nstr(&out);
188
189 /* template literal spans lines and holds braces */
190 out = n_pretty_js("const q=`a{b}\n${x({})}`;done();");
191 expect_true("js template returns output", out != NULL);
192 expect_contains("js template intact", out, "`a{b}\n${x({})}`");
193 expect_contains("js statement after template", out, "done();");
194 free_nstr(&out);
195
196 /* a regex right after an expression keyword is a regex, not division */
197 out = n_pretty_js("function t(a){return /x;{/.test(a);}");
198 expect_true("js keyword regex returns output", out != NULL);
199 expect_contains("js regex after return intact", out, "return /x;{/.test(a);");
200 free_nstr(&out);
201
202 /* a '/' after an identifier or a closing paren is division */
203 out = n_pretty_js("var x=a/b;var y=f(1)/2;done();");
204 expect_true("js division returns output", out != NULL);
205 expect_contains("js division after identifier", out, "var x=a/b;\n");
206 expect_contains("js division after paren", out, "var y=f(1)/2;\n");
207 free_nstr(&out);
208
209 /* for(;;) semicolons at paren depth > 0 do not break the line */
210 out = n_pretty_js("for(i=0;i<3;i++){go(i);}");
211 expect_true("js for returns output", out != NULL);
212 expect_contains("js for header on one line", out, "for(i=0;i<3;i++){");
213 free_nstr(&out);
214
215 /* '} else {' style input: terminator glued to the closing brace */
216 out = n_pretty_js("a();};next();");
217 expect_true("js brace-semicolon returns output", out != NULL);
218 expect_contains("js terminator glued to brace", out, "\n};\n");
219 free_nstr(&out);
220 }
221
222 /* NULL / empty inputs */
223 expect_true("json NULL returns NULL", n_pretty_json(NULL) == NULL);
224 expect_true("lenient NULL returns NULL", n_pretty_json_lenient(NULL) == NULL);
225 expect_true("lenient empty returns NULL", n_pretty_json_lenient("") == NULL);
226 expect_true("xml NULL returns NULL", n_pretty_xml(NULL) == NULL);
227 expect_true("js NULL returns NULL", n_pretty_js(NULL) == NULL);
228 expect_true("xml empty returns NULL", n_pretty_xml("") == NULL);
229 expect_true("js empty returns NULL", n_pretty_js("") == NULL);
230
231 if (failures) {
232 n_log(LOG_ERR, "ex_pretty: %d failure(s)", failures);
233 return 1;
234 }
235 printf("ex_pretty: all tests passed\n");
236 return 0;
237}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static void expect_contains(const char *label, const N_STR *out, const char *needle)
Definition ex_diff.c:83
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_NULL
no log output
Definition n_log.h:70
#define LOG_INFO
informational
Definition n_log.h:106
N_STR * n_pretty_xml(const char *text)
Re-indent an XML or HTML document (one tag or text run per line).
Definition n_pretty.c:286
N_STR * n_pretty_js(const char *text)
Conservatively re-indent JavaScript (newlines at top-level braces and semicolons, two-space indent by...
Definition n_pretty.c:489
N_STR * n_pretty_json(const char *text)
Reformat a JSON document with cJSON (parse + print).
Definition n_pretty.c:89
N_STR * n_pretty_json_lenient(const char *text)
Re-indent a JSON document by punctuation alone, without parsing it.
Definition n_pretty.c:123
char * data
the string
Definition n_str.h:63
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
A box including a string and his lenght.
Definition n_str.h:61
Common headers and low-level functions & define.
Generic log system.
Lexical pretty-printers for JSON, XML/HTML and JavaScript text.
N_STR and string function declaration.