Mon, 24 Apr 2023 20:54:38 +0200
upgrade to ucx 3.0
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2016 Mike Becker. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
30 #include "highlighter.h"
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
37 #include <cx/string.h>
39 static void put_htmlescaped(CxBuffer *dest, char c) {
40 if (c == '>') {
41 cxBufferPutString(dest, ">");
42 } else if (c == '<') {
43 cxBufferPutString(dest, "<");
44 } else if (c) {
45 cxBufferPut(dest, c);
46 }
47 }
49 static void put_htmlescapedstr(CxBuffer *dest, cxstring s) {
50 for (int i = 0 ; i < s.length ; i++) {
51 put_htmlescaped(dest, s.ptr[i]);
52 }
53 }
55 static int check_keyword(cxstring word, const char** keywords) {
56 for (int i = 0 ; keywords[i] ; i++) {
57 if (cx_strcmp(word, cx_str(keywords[i])) == 0) {
58 return 1;
59 }
60 }
61 return 0;
62 }
64 static int check_capsonly(cxstring word) {
65 for (size_t i = 0 ; i < word.length ; i++) {
66 if (!isupper(word.ptr[i]) && !isdigit(word.ptr[i])
67 && word.ptr[i] != '_') {
68 return 0;
69 }
70 }
71 return 1;
72 }
74 /* Plaintext Highlighter */
76 void c2html_plain_highlighter(char const *src, CxBuffer *dest,
77 c2html_highlighter_data *hd) {
78 while (*src && *src != '\n') {
79 if (*src != '\r') {
80 put_htmlescaped(dest, *src);
81 }
82 src++;
83 }
84 cxBufferPut(dest, '\n');
85 }
87 /* C Highlighter */
89 static const char* ckeywords[] = {
90 "auto", "break", "case", "char", "const", "continue", "default", "do",
91 "double", "else", "enum", "extern", "float", "for", "goto", "if", "int",
92 "long", "register", "return", "short", "signed", "sizeof", "static",
93 "struct", "switch", "typedef", "union", "unsigned", "void", "volatile",
94 "while", NULL
95 };
97 void c2html_c_highlighter(char const *src, CxBuffer *dest,
98 c2html_highlighter_data *hd) {
99 /* reset buffers without clearing them */
100 hd->primary_buffer.size = hd->primary_buffer.pos = 0;
101 hd->secondary_buffer.size = hd->secondary_buffer.pos = 0;
103 /* alias the buffers for better handling */
104 CxBuffer *wbuf = &hd->primary_buffer;
105 CxBuffer *ifilebuf = &hd->secondary_buffer;
107 /* local information */
108 size_t sp = (size_t)-1;
109 int isstring = 0, iscomment = 0, isinclude = 0, parseinclude = 0;
110 char quote = '\0';
111 int isescaping = 0;
113 /* continue a multi line comment highlighting */
114 if (hd->multiline_comment) {
115 iscomment = 1;
116 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
117 }
119 char c;
120 do {
121 c = src[++sp];
122 if (c == '\r') continue;
124 /* comments */
125 if (!isstring && c == '/') {
126 if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') {
127 iscomment = 0;
128 hd->multiline_comment = 0;
129 cxBufferPutString(dest, "/</span>");
130 continue;
131 } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) {
132 iscomment = 1;
133 hd->multiline_comment = (src[sp+1] == '*');
134 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
135 }
136 }
138 if (iscomment) {
139 if (c == '\n') {
140 cxBufferPutString(dest, "</span>\n");
141 } else {
142 put_htmlescaped(dest, c);
143 }
144 } else if (isinclude) {
145 if (c == '<') {
146 cxBufferPutString(dest,
147 "<span class=\"c2html-stdinclude\"><");
148 } else if (c == '\"') {
149 if (parseinclude) {
150 cxBufferPutString(dest, "\">");
151 cxBufferWrite(ifilebuf->space, 1, ifilebuf->size, dest);
152 cxBufferPutString(dest, "\"</a>");
153 parseinclude = 0;
154 } else {
155 cxBufferPutString(dest,
156 "<a class=\"c2html-userinclude\" href=\"");
157 cxBufferPut(ifilebuf, '\"');
158 parseinclude = 1;
159 }
160 } else if (c == '>') {
161 cxBufferPutString(dest, "></span>");
162 } else {
163 if (parseinclude) {
164 cxBufferPut(ifilebuf, c);
165 }
166 put_htmlescaped(dest, c);
167 }
168 } else {
169 /* strings */
170 if (!isescaping && (c == '\'' || c == '\"')) {
171 if (isstring) {
172 put_htmlescaped(dest, c);
173 if (c == quote) {
174 isstring = 0;
175 cxBufferPutString(dest, "</span>");
176 } else {
177 put_htmlescaped(dest, c);
178 }
179 } else {
180 isstring = 1;
181 quote = c;
182 cxBufferPutString(dest, "<span class=\"c2html-string\">");
183 put_htmlescaped(dest, c);
184 }
185 } else {
186 if (isstring) {
187 put_htmlescaped(dest, c);
188 } else if (isalnum(c) || c == '_' || c == '#') {
189 /* buffer the current word */
190 cxBufferPut(wbuf, c);
191 } else {
192 /* write buffered word, if any */
193 if (wbuf->size > 0) {
194 cxstring word = cx_strn(wbuf->space, wbuf->size);
195 int closespan = 1;
196 cxstring typesuffix = CX_STR("_t");
197 if (check_keyword(word, ckeywords)) {
198 cxBufferPutString(dest,
199 "<span class=\"c2html-keyword\">");
200 } else if (cx_strsuffix(word, typesuffix)) {
201 cxBufferPutString(dest,
202 "<span class=\"c2html-type\">");
203 } else if (word.ptr[0] == '#') {
204 isinclude = !cx_strcmp(word, CX_STR("#include"));
205 cxBufferPutString(dest,
206 "<span class=\"c2html-directive\">");
207 } else if (check_capsonly(word)) {
208 cxBufferPutString(dest,
209 "<span class=\"c2html-macroconst\">");
210 } else {
211 closespan = 0;
212 }
213 put_htmlescapedstr(dest, word);
214 if (closespan) {
215 cxBufferPutString(dest, "</span>");
216 }
217 }
218 wbuf->pos = wbuf->size = 0; /* reset word buffer */
220 /* write current character */
221 put_htmlescaped(dest, c);
222 }
223 }
225 isescaping = !isescaping & (c == '\\');
226 }
227 } while (c && c != '\n');
228 }
230 /* Java Highlighter */
232 static const char* jkeywords[] = {
233 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto",
234 "package", "synchronized", "boolean", "do", "if", "private", "this",
235 "break", "double", "implements", "protected", "throw", "byte", "else",
236 "import", "public", "throws", "case", "enum", "instanceof", "return",
237 "transient", "catch", "extends", "int", "short", "try", "char", "final",
238 "interface", "static", "void", "class", "finally", "long", "strictfp",
239 "volatile", "const", "float", "native", "super", "while", NULL
240 };
242 void c2html_java_highlighter(char const *src, CxBuffer *dest,
243 c2html_highlighter_data *hd) {
244 /* reset buffers without clearing them */
245 hd->primary_buffer.size = hd->primary_buffer.pos = 0;
246 hd->secondary_buffer.size = hd->secondary_buffer.pos = 0;
248 /* alias the buffers for better handling */
249 CxBuffer *wbuf = &hd->primary_buffer;
251 /* local information */
252 size_t sp = (size_t)-1;
253 int isstring = 0, iscomment = 0, isimport = 0;
254 char quote = '\0';
255 int isescaping = 0;
257 if (hd->multiline_comment) {
258 iscomment = 1;
259 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
260 }
262 char c;
263 do {
264 c = src[++sp];
265 if (c == '\r') continue;
267 /* comments */
268 if (!isstring && c == '/') {
269 if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') {
270 iscomment = 0;
271 hd->multiline_comment = 0;
272 cxBufferPutString(dest, "/</span>");
273 continue;
274 } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) {
275 iscomment = 1;
276 hd->multiline_comment = (src[sp+1] == '*');
277 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
278 }
279 }
281 if (iscomment) {
282 if (c == '\n') {
283 cxBufferPutString(dest, "</span>\n");
284 } else {
285 put_htmlescaped(dest, c);
286 }
287 } else if (isimport) {
288 /* TODO: local imports */
289 } else {
290 /* strings */
291 if (!isescaping && (c == '\'' || c == '\"')) {
292 if (isstring) {
293 put_htmlescaped(dest, c);
294 if (c == quote) {
295 isstring = 0;
296 cxBufferPutString(dest, "</span>");
297 } else {
298 put_htmlescaped(dest, c);
299 }
300 } else {
301 isstring = 1;
302 quote = c;
303 cxBufferPutString(dest,
304 "<span class=\"c2html-string\">");
305 put_htmlescaped(dest, c);
306 }
307 } else {
308 if (isstring) {
309 put_htmlescaped(dest, c);
310 } else if (isalnum(c) || c == '_' || c == '@') {
311 /* buffer the current word */
312 cxBufferPut(wbuf, c);
313 } else {
314 /* write buffered word, if any */
315 if (wbuf->size > 0) {
316 cxstring word = cx_strn(wbuf->space, wbuf->size);
317 int closespan = 1;
318 if (check_keyword(word, jkeywords)) {
319 cxBufferPutString(dest,
320 "<span class=\"c2html-keyword\">");
321 } else if (isupper(word.ptr[0])) {
322 cxBufferPutString(dest,
323 "<span class=\"c2html-type\">");
324 } else if (word.ptr[0] == '@') {
325 cxBufferPutString(dest,
326 "<span class=\"c2html-directive\">");
327 } else if (check_capsonly(word)) {
328 cxBufferPutString(dest,
329 "<span class=\"c2html-macroconst\">");
330 } else {
331 closespan = 0;
332 }
333 put_htmlescapedstr(dest, word);
335 if (closespan) {
336 cxBufferPutString(dest, "</span>");
337 }
338 }
339 wbuf->pos = wbuf->size = 0; /* reset buffer */
341 /* write current character */
342 put_htmlescaped(dest, c);
343 }
344 }
346 isescaping = !isescaping & (c == '\\');
347 }
348 } while (c && c != '\n');
349 }