Graphviz 15.1.1~dev.20260731.2204
Loading...
Searching...
No Matches
htmllex.c
Go to the documentation of this file.
1
3/*************************************************************************
4 * Copyright (c) 2011 AT&T Intellectual Property
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v2.0
7 * which accompanies this distribution, and is available at
8 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
9 *
10 * Contributors: Details at https://graphviz.org
11 *************************************************************************/
12
13#include "config.h"
14
15#include <assert.h>
16#include <common/render.h>
17#include <common/htmltable.h>
18#include "htmlparse.h"
19#include <common/htmllex.h>
20#include <cdt/cdt.h>
21#include <limits.h>
22#include <stdatomic.h>
23#include <stdbool.h>
24#include <stddef.h>
25#include <stdint.h>
26#include <util/alloc.h>
27#include <util/gv_ctype.h>
28#include <util/startswith.h>
29#include <util/strcasecmp.h>
30#include <util/strview.h>
31#include <util/tokenize.h>
32#include <util/unused.h>
33
34#ifdef HAVE_EXPAT
35#include <expat.h>
36#endif
37
38#ifndef XML_STATUS_ERROR
39#define XML_STATUS_ERROR 0
40#endif
41
42static unsigned long htmllineno_ctx(htmllexstate_t *ctx);
43
44/* error_context:
45 * Print the last 2 "token"s seen.
46 */
48{
49 agerr(AGPREV, "... %.*s%.*s ...\n", (int)ctx->prevtok.size,
50 ctx->prevtok.data, (int)ctx->currtok.size, ctx->currtok.data);
51}
52
53/* htmlerror:
54 * yyerror - called by yacc output
55 */
56void htmlerror(htmlscan_t *scanner, const char *msg)
57{
58 htmllexstate_t *ctx = &scanner->lexer;
59 if (ctx->error)
60 return;
61 ctx->error = 1;
62 agerrorf("%s in line %lu \n", msg, htmllineno(scanner));
63 error_context(&scanner->lexer);
64}
65
66#ifdef HAVE_EXPAT
67/* lexerror:
68 * called by lexer when unknown <..> is found.
69 */
70static void lexerror(htmllexstate_t *ctx, const char *name)
71{
72 ctx->tok = T_error;
73 ctx->error = 1;
74 agerrorf("Unknown HTML element <%s> on line %lu \n", name, htmllineno_ctx(ctx));
75}
76
78static int icmp(const void *name, const void *item) {
79 const char *const *const j = item;
80 return strcasecmp(name, *j);
81}
82
83static int bgcolorfn(htmldata_t *p, const char *v) {
84 p->bgcolor = strdup(v);
85 return 0;
86}
87
88static int pencolorfn(htmldata_t *p, const char *v) {
89 p->pencolor = strdup(v);
90 return 0;
91}
92
93static int hreffn(htmldata_t *p, const char *v) {
94 p->href = strdup(v);
95 return 0;
96}
97
98static int sidesfn(htmldata_t *p, const char *v) {
99 unsigned short flags = 0;
100 char c;
101
102 while ((c = *v++)) {
103 switch (gv_tolower(c)) {
104 case 'l' :
106 break;
107 case 't' :
108 flags |= BORDER_TOP;
109 break;
110 case 'r' :
112 break;
113 case 'b' :
115 break;
116 default :
117 agwarningf("Unrecognized character '%c' (%d) in sides attribute\n", c, c);
118 break;
119 }
120 }
121 if (flags != BORDER_MASK)
122 p->flags |= flags;
123 return 0;
124}
125
126static int titlefn(htmldata_t *p, const char *v) {
127 p->title = strdup(v);
128 return 0;
129}
130
131static int portfn(htmldata_t *p, const char *v) {
132 p->port = strdup(v);
133 return 0;
134}
135
136#define DELIM " ,"
137
138static int stylefn(htmldata_t *p, const char *v) {
139 int rv = 0;
140 for (tok_t t = tok(v, DELIM); !tok_end(&t); tok_next(&t)) {
141 strview_t tk = tok_get(&t);
142 if (strview_case_str_eq(tk, "ROUNDED")) p->style.rounded = true;
143 else if (strview_case_str_eq(tk, "RADIAL")) p->style.radial = true;
144 else if (strview_case_str_eq(tk,"SOLID")) {
145 p->style.dotted = false;
146 p->style.dashed = false;
147 } else if (strview_case_str_eq(tk,"INVISIBLE") ||
148 strview_case_str_eq(tk,"INVIS")) p->style.invisible = true;
149 else if (strview_case_str_eq(tk,"DOTTED")) p->style.dotted = true;
150 else if (strview_case_str_eq(tk,"DASHED")) p->style.dashed = true;
151 else {
152 agwarningf("Illegal value %.*s for STYLE - ignored\n", (int)tk.size,
153 tk.data);
154 rv = 1;
155 }
156 }
157 return rv;
158}
159
160static int targetfn(htmldata_t *p, const char *v) {
161 p->target = strdup(v);
162 return 0;
163}
164
165static int idfn(htmldata_t *p, const char *v) {
166 p->id = strdup(v);
167 return 0;
168}
169
170
171/* doInt:
172 * Scan v for integral value. Check that
173 * the value is >= min and <= max. Return value in ul.
174 * String s is name of value.
175 * Return 0 if okay; 1 otherwise.
176 */
177static int doInt(const char *v, char *s, int min, int max, long *ul) {
178 int rv = 0;
179 char *ep;
180 long b = strtol(v, &ep, 10);
181
182 if (ep == v) {
183 agwarningf("Improper %s value %s - ignored", s, v);
184 rv = 1;
185 } else if (b > max) {
186 agwarningf("%s value %s > %d - too large - ignored", s, v, max);
187 rv = 1;
188 } else if (b < min) {
189 agwarningf("%s value %s < %d - too small - ignored", s, v, min);
190 rv = 1;
191 } else
192 *ul = b;
193 return rv;
194}
195
196
197static int gradientanglefn(htmldata_t *p, const char *v) {
198 long u;
199
200 if (doInt(v, "GRADIENTANGLE", 0, 360, &u))
201 return 1;
202 p->gradientangle = (unsigned short) u;
203 return 0;
204}
205
206
207static int borderfn(htmldata_t *p, const char *v) {
208 long u;
209
210 if (doInt(v, "BORDER", 0, UCHAR_MAX, &u))
211 return 1;
212 p->border = (unsigned char) u;
213 p->flags |= BORDER_SET;
214 return 0;
215}
216
217static int cellpaddingfn(htmldata_t *p, const char *v) {
218 long u;
219
220 if (doInt(v, "CELLPADDING", 0, UCHAR_MAX, &u))
221 return 1;
222 p->pad = (unsigned char) u;
223 p->flags |= PAD_SET;
224 return 0;
225}
226
227static int cellspacingfn(htmldata_t *p, const char *v) {
228 long u;
229
230 if (doInt(v, "CELLSPACING", SCHAR_MIN, SCHAR_MAX, &u))
231 return 1;
232 p->space = (signed char) u;
233 p->flags |= SPACE_SET;
234 return 0;
235}
236
237static int cellborderfn(htmldata_t *data, const char *v) {
238 long u;
239 htmltbl_t *const p = (htmltbl_t *)((uintptr_t)data - offsetof(htmltbl_t, data));
240
241 if (doInt(v, "CELLBORDER", 0, INT8_MAX, &u))
242 return 1;
243 p->cellborder = (int8_t)u;
244 return 0;
245}
246
247static int columnsfn(htmldata_t *data, const char *v) {
248 htmltbl_t *const p = (htmltbl_t *)((uintptr_t)data - offsetof(htmltbl_t, data));
249 if (*v != '*') {
250 agwarningf("Unknown value %s for COLUMNS - ignored\n", v);
251 return 1;
252 }
253 p->vrule = true;
254 return 0;
255}
256
257static int rowsfn(htmldata_t *data, const char *v) {
258 htmltbl_t *const p = (htmltbl_t *)((uintptr_t)data - offsetof(htmltbl_t, data));
259 if (*v != '*') {
260 agwarningf("Unknown value %s for ROWS - ignored\n", v);
261 return 1;
262 }
263 p->hrule = true;
264 return 0;
265}
266
267static int fixedsizefn(htmldata_t *p, const char *v) {
268 int rv = 0;
269 if (!strcasecmp(v, "TRUE"))
270 p->flags |= FIXED_FLAG;
271 else if (strcasecmp(v, "FALSE")) {
272 agwarningf("Illegal value %s for FIXEDSIZE - ignored\n", v);
273 rv = 1;
274 }
275 return rv;
276}
277
278static int valignfn(htmldata_t *p, const char *v) {
279 int rv = 0;
280 if (!strcasecmp(v, "BOTTOM"))
281 p->flags |= VALIGN_BOTTOM;
282 else if (!strcasecmp(v, "TOP"))
283 p->flags |= VALIGN_TOP;
284 else if (strcasecmp(v, "MIDDLE")) {
285 agwarningf("Illegal value %s for VALIGN - ignored\n", v);
286 rv = 1;
287 }
288 return rv;
289}
290
291static int halignfn(htmldata_t *p, const char *v) {
292 int rv = 0;
293 if (!strcasecmp(v, "LEFT"))
294 p->flags |= HALIGN_LEFT;
295 else if (!strcasecmp(v, "RIGHT"))
296 p->flags |= HALIGN_RIGHT;
297 else if (strcasecmp(v, "CENTER")) {
298 agwarningf("Illegal value %s for ALIGN - ignored\n", v);
299 rv = 1;
300 }
301 return rv;
302}
303
304static int cell_halignfn(htmldata_t *p, const char *v) {
305 int rv = 0;
306 if (!strcasecmp(v, "LEFT"))
307 p->flags |= HALIGN_LEFT;
308 else if (!strcasecmp(v, "RIGHT"))
309 p->flags |= HALIGN_RIGHT;
310 else if (!strcasecmp(v, "TEXT"))
311 p->flags |= HALIGN_TEXT;
312 else if (strcasecmp(v, "CENTER"))
313 rv = 1;
314 if (rv)
315 agwarningf("Illegal value %s for ALIGN in TD - ignored\n", v);
316 return rv;
317}
318
319static int balignfn(htmldata_t *p, const char *v) {
320 int rv = 0;
321 if (!strcasecmp(v, "LEFT"))
322 p->flags |= BALIGN_LEFT;
323 else if (!strcasecmp(v, "RIGHT"))
324 p->flags |= BALIGN_RIGHT;
325 else if (strcasecmp(v, "CENTER"))
326 rv = 1;
327 if (rv)
328 agwarningf("Illegal value %s for BALIGN in TD - ignored\n", v);
329 return rv;
330}
331
332static int heightfn(htmldata_t *p, const char *v) {
333 long u;
334
335 if (doInt(v, "HEIGHT", 0, USHRT_MAX, &u))
336 return 1;
337 p->height = (unsigned short) u;
338 return 0;
339}
340
341static int widthfn(htmldata_t *p, const char *v) {
342 long u;
343
344 if (doInt(v, "WIDTH", 0, USHRT_MAX, &u))
345 return 1;
346 p->width = (unsigned short) u;
347 return 0;
348}
349
350static int rowspanfn(htmldata_t *data, const char *v) {
351 long u;
352 htmlcell_t *const p = (htmlcell_t *)((uintptr_t)data - offsetof(htmlcell_t, data));
353
354 if (doInt(v, "ROWSPAN", 0, UINT16_MAX, &u))
355 return 1;
356 if (u == 0) {
357 agwarningf("ROWSPAN value cannot be 0 - ignored\n");
358 return 1;
359 }
360 p->rowspan = (uint16_t)u;
361 return 0;
362}
363
364static int colspanfn(htmldata_t *data, const char *v) {
365 long u;
366 htmlcell_t *const p = (htmlcell_t *)((uintptr_t)data - offsetof(htmlcell_t, data));
367
368 if (doInt(v, "COLSPAN", 0, UINT16_MAX, &u))
369 return 1;
370 if (u == 0) {
371 agwarningf("COLSPAN value cannot be 0 - ignored\n");
372 return 1;
373 }
374 p->colspan = (uint16_t)u;
375 return 0;
376}
377
378static int fontcolorfn(textfont_t *p, const char *v) {
379 // no strdup because HTML font usage is contained
380 p->color = (char *)v;
381 return 0;
382}
383
384static int facefn(textfont_t *p, const char *v) {
385 // no strdup because HTML font usage is contained
386 p->name = (char *)v;
387 return 0;
388}
389
390static int ptsizefn(textfont_t *p, const char *v) {
391 long u;
392
393 if (doInt(v, "POINT-SIZE", 0, UCHAR_MAX, &u))
394 return 1;
395 p->size = (double) u;
396 return 0;
397}
398
399static int srcfn(htmlimg_t *p, const char *v) {
400 p->src = strdup(v);
401 return 0;
402}
403
404static int scalefn(htmlimg_t *p, const char *v) {
405 p->scale = strdup(v);
406 return 0;
407}
408
409static int alignfn(int *p, const char *v) {
410 int rv = 0;
411 if (!strcasecmp(v, "RIGHT"))
412 *p = 'r';
413 else if (!strcasecmp(v, "LEFT"))
414 *p = 'l';
415 else if (!strcasecmp(v, "CENTER"))
416 *p = 'n';
417 else {
418 agwarningf("Illegal value %s for ALIGN - ignored\n", v);
419 rv = 1;
420 }
421 return rv;
422}
423
424typedef struct {
425 char *name;
426 int (*action)(htmldata_t *, const char *);
427} html_item_t;
428
429/* Tables used in binary search; MUST be alphabetized */
430static html_item_t tbl_items[] = {
431 {"align", halignfn},
432 {"bgcolor", bgcolorfn},
433 {"border", borderfn},
434 {"cellborder", cellborderfn},
435 {"cellpadding", cellpaddingfn},
436 {"cellspacing", cellspacingfn},
437 {"color", pencolorfn},
438 {"columns", columnsfn},
439 {"fixedsize", fixedsizefn},
440 {"gradientangle", gradientanglefn},
441 {"height", heightfn},
442 {"href", hreffn},
443 {"id", idfn},
444 {"port", portfn},
445 {"rows", rowsfn},
446 {"sides", sidesfn},
447 {"style", stylefn},
448 {"target", targetfn},
449 {"title", titlefn},
450 {"tooltip", titlefn},
451 {"valign", valignfn},
452 {"width", widthfn},
453};
454
455static html_item_t cell_items[] = {
456 {"align", cell_halignfn},
457 {"balign", balignfn},
458 {"bgcolor", bgcolorfn},
459 {"border", borderfn},
460 {"cellpadding", cellpaddingfn},
461 {"cellspacing", cellspacingfn},
462 {"color", pencolorfn},
463 {"colspan", colspanfn},
464 {"fixedsize", fixedsizefn},
465 {"gradientangle", gradientanglefn},
466 {"height", heightfn},
467 {"href", hreffn},
468 {"id", idfn},
469 {"port", portfn},
470 {"rowspan", rowspanfn},
471 {"sides", sidesfn},
472 {"style", stylefn},
473 {"target", targetfn},
474 {"title", titlefn},
475 {"tooltip", titlefn},
476 {"valign", valignfn},
477 {"width", widthfn},
478};
479
480typedef struct {
481 char *name;
482 int (*action)(textfont_t *, const char *);
483} font_item_t;
484
485static font_item_t font_items[] = {
486 {"color", fontcolorfn},
487 {"face", facefn},
488 {"point-size", ptsizefn},
489};
490
491typedef struct {
492 char *name;
493 int (*action)(htmlimg_t *, const char *);
494} img_item_t;
495
496static img_item_t img_items[] = {
497 {"scale", scalefn},
498 {"src", srcfn},
499};
500
501typedef struct {
502 char *name;
503 int (*action)(int *, const char *);
504} br_item_t;
505
506static br_item_t br_items[] = {
507 {"align", alignfn},
508};
509
514#define CALL_ACTION(list, elem, tp, val) \
515 (_Generic((list), html_item_t * \
516 : (html_item_t *)(elem), font_item_t * \
517 : (font_item_t *)(elem), img_item_t * \
518 : (img_item_t *)(elem), br_item_t * \
519 : (br_item_t *)(elem)) \
520 ->action((tp), (val)))
521
522/* doAttrs:
523 * General function for processing list of name/value attributes.
524 * Do binary search on items table. If match found, invoke action
525 * passing it tp and attribute value.
526 * Table size is given by nel
527 * Name/value pairs are in array atts, which is null terminated.
528 * s is the name of the HTML element being processed.
529 */
530#define doAttrs(ctx, tp, items, nel, atts, s) do { \
531 const char *name; \
532\
533 while ((name = *(atts)++) != NULL) { \
534 const char *val = *(atts)++; \
535 void *const ip = bsearch(name, (items), (nel), sizeof((items)[0]), icmp); \
536 if (ip) \
537 (ctx)->warn |= CALL_ACTION((items), ip, (tp), val); \
538 else { \
539 agwarningf("Illegal attribute %s in %s - ignored\n", name, \
540 (s)); \
541 (ctx)->warn = 1; \
542 } \
543 } \
544} while (0)
545
546static void mkBR(htmllexstate_t *ctx, const char **atts) {
547 ctx->htmllval->i = UNSET_ALIGN;
548 doAttrs(ctx, &ctx->htmllval->i, br_items, sizeof(br_items) / sizeof(br_items[0]), atts, "<BR>");
549}
550
551static htmlimg_t *mkImg(htmllexstate_t *ctx, const char **atts) {
552 htmlimg_t *img = gv_alloc(sizeof(htmlimg_t));
553
554 doAttrs(ctx, img, img_items, sizeof(img_items) / sizeof(img_items[0]), atts, "<IMG>");
555
556 return img;
557}
558
559static textfont_t *mkFont(htmllexstate_t *ctx, const char **atts, unsigned char flags) {
560 textfont_t tf = {NULL,NULL,NULL,0.0,0,0};
561
562 tf.size = -1.0; /* unassigned */
563 enum { FLAGS_MAX = (1 << GV_TEXTFONT_FLAGS_WIDTH) - 1 };
564 assert(flags <= FLAGS_MAX);
565 tf.flags = (unsigned char)(flags & FLAGS_MAX);
566 if (atts)
567 doAttrs(ctx, &tf, font_items, sizeof(font_items) / sizeof(font_items[0]), atts, "<FONT>");
568
569 return dtinsert(ctx->gvc->textfont_dt, &tf);
570}
571
572static htmlcell_t *mkCell(htmllexstate_t *ctx, const char **atts) {
574
575 cell->colspan = 1;
576 cell->rowspan = 1;
577 doAttrs(ctx, &cell->data, cell_items, sizeof(cell_items) / sizeof(cell_items[0]), atts, "<TD>");
578
579 return cell;
580}
581
582static htmltbl_t *mkTbl(htmllexstate_t *ctx, const char **atts) {
583 htmltbl_t *tbl = gv_alloc(sizeof(htmltbl_t));
584
585 tbl->row_count = SIZE_MAX; // flag that table is a raw, parsed table
586 tbl->rows = (rows_t){.dtor = free_ritem};
587 tbl->cellborder = -1; // unset cell border attribute
588 doAttrs(ctx, &tbl->data, tbl_items, sizeof(tbl_items) / sizeof(tbl_items[0]), atts, "<TABLE>");
589
590 return tbl;
591}
592
593static void startElement(void *user, const char *name, const char **atts) {
594 htmllexstate_t *ctx = user;
595
596 if (strcasecmp(name, "TABLE") == 0) {
597 ctx->htmllval->tbl = mkTbl(ctx, atts);
598 ctx->inCell = 0;
599 ctx->tok = T_table;
600 } else if (strcasecmp(name, "TR") == 0 || strcasecmp(name, "TH") == 0) {
601 ctx->inCell = 0;
602 ctx->tok = T_row;
603 } else if (strcasecmp(name, "TD") == 0) {
604 ctx->inCell = 1;
605 ctx->htmllval->cell = mkCell(ctx, atts);
606 ctx->tok = T_cell;
607 } else if (strcasecmp(name, "FONT") == 0) {
608 ctx->htmllval->font = mkFont(ctx, atts, 0);
609 ctx->tok = T_font;
610 } else if (strcasecmp(name, "B") == 0) {
611 ctx->htmllval->font = mkFont(ctx, 0, HTML_BF);
612 ctx->tok = T_bold;
613 } else if (strcasecmp(name, "S") == 0) {
614 ctx->htmllval->font = mkFont(ctx, 0, HTML_S);
615 ctx->tok = T_s;
616 } else if (strcasecmp(name, "U") == 0) {
617 ctx->htmllval->font = mkFont(ctx, 0, HTML_UL);
618 ctx->tok = T_underline;
619 } else if (strcasecmp(name, "O") == 0) {
620 ctx->htmllval->font = mkFont(ctx, 0, HTML_OL);
621 ctx->tok = T_overline;
622 } else if (strcasecmp(name, "I") == 0) {
623 ctx->htmllval->font = mkFont(ctx, 0, HTML_IF);
624 ctx->tok = T_italic;
625 } else if (strcasecmp(name, "SUP") == 0) {
626 ctx->htmllval->font = mkFont(ctx, 0, HTML_SUP);
627 ctx->tok = T_sup;
628 } else if (strcasecmp(name, "SUB") == 0) {
629 ctx->htmllval->font = mkFont(ctx, 0, HTML_SUB);
630 ctx->tok = T_sub;
631 } else if (strcasecmp(name, "BR") == 0) {
632 mkBR(ctx, atts);
633 ctx->tok = T_br;
634 } else if (strcasecmp(name, "HR") == 0) {
635 ctx->tok = T_hr;
636 } else if (strcasecmp(name, "VR") == 0) {
637 ctx->tok = T_vr;
638 } else if (strcasecmp(name, "IMG") == 0) {
639 ctx->htmllval->img = mkImg(ctx, atts);
640 ctx->tok = T_img;
641 } else if (strcasecmp(name, "HTML") == 0) {
642 ctx->tok = T_html;
643 } else {
644 lexerror(ctx, name);
645 }
646}
647
648static void endElement(void *user, const char *name)
649{
650 htmllexstate_t *ctx = user;
651
652 if (strcasecmp(name, "TABLE") == 0) {
653 ctx->tok = T_end_table;
654 ctx->inCell = 1;
655 } else if (strcasecmp(name, "TR") == 0 || strcasecmp(name, "TH") == 0) {
656 ctx->tok = T_end_row;
657 } else if (strcasecmp(name, "TD") == 0) {
658 ctx->tok = T_end_cell;
659 ctx->inCell = 0;
660 } else if (strcasecmp(name, "HTML") == 0) {
661 ctx->tok = T_end_html;
662 } else if (strcasecmp(name, "FONT") == 0) {
663 ctx->tok = T_end_font;
664 } else if (strcasecmp(name, "B") == 0) {
665 ctx->tok = T_n_bold;
666 } else if (strcasecmp(name, "U") == 0) {
667 ctx->tok = T_n_underline;
668 } else if (strcasecmp(name, "O") == 0) {
669 ctx->tok = T_n_overline;
670 } else if (strcasecmp(name, "I") == 0) {
671 ctx->tok = T_n_italic;
672 } else if (strcasecmp(name, "SUP") == 0) {
673 ctx->tok = T_n_sup;
674 } else if (strcasecmp(name, "SUB") == 0) {
675 ctx->tok = T_n_sub;
676 } else if (strcasecmp(name, "S") == 0) {
677 ctx->tok = T_n_s;
678 } else if (strcasecmp(name, "BR") == 0) {
679 if (ctx->tok == T_br)
680 ctx->tok = T_BR;
681 else
682 ctx->tok = T_end_br;
683 } else if (strcasecmp(name, "HR") == 0) {
684 if (ctx->tok == T_hr)
685 ctx->tok = T_HR;
686 else
687 ctx->tok = T_end_hr;
688 } else if (strcasecmp(name, "VR") == 0) {
689 if (ctx->tok == T_vr)
690 ctx->tok = T_VR;
691 else
692 ctx->tok = T_end_vr;
693 } else if (strcasecmp(name, "IMG") == 0) {
694 if (ctx->tok == T_img)
695 ctx->tok = T_IMG;
696 else
697 ctx->tok = T_end_img;
698 } else {
699 lexerror(ctx, name);
700 }
701}
702
703/* characterData:
704 * Generate T_string token. Do this only when immediately in
705 * <TD>..</TD> or <HTML>..</HTML>, i.e., when inCell is true.
706 * Strip out formatting characters but keep spaces.
707 * Distinguish between all whitespace vs. strings with non-whitespace
708 * characters.
709 */
710static void characterData(void *user, const char *s, int length)
711{
712 htmllexstate_t *ctx = user;
713
714 int i, cnt = 0;
715 unsigned char c;
716
717 if (ctx->inCell) {
718 for (i = length; i; i--) {
719 c = *s++;
720 if (c >= ' ') {
721 cnt++;
722 agxbputc(ctx->xb, (char)c);
723 }
724 }
725 if (cnt) ctx->tok = T_string;
726 }
727}
728#endif
729
730int initHTMLlexer(htmlscan_t *scanner, char *src, agxbuf * xb, htmlenv_t *env)
731{
732#ifdef HAVE_EXPAT
733 htmllexstate_t *ctx = &scanner->lexer;
734
735 ctx->xb = xb;
736 ctx->lb = (agxbuf){0};
737 ctx->ptr = src;
738 ctx->mode = 0;
739 ctx->warn = 0;
740 ctx->error = 0;
741 ctx->currtok = (strview_t){0};
742 ctx->prevtok = (strview_t){0};
743 ctx->inCell = 1;
744 ctx->parser = XML_ParserCreate(charsetToStr(GD_charset(env->g)));
745 ctx->gvc = GD_gvc(env->g);
746 XML_SetUserData(ctx->parser, ctx);
747 XML_SetElementHandler(ctx->parser, startElement, endElement);
748 XML_SetCharacterDataHandler(ctx->parser, characterData);
749 return 0;
750#else
751 (void)scanner;
752 (void)src;
753 (void)xb;
754 (void)env;
755
756 static atomic_flag first;
757 if (!atomic_flag_test_and_set(&first)) {
759 "Not built with libexpat. Table formatting is not available.\n");
760 }
761 return 1;
762#endif
763}
764
766{
767#ifdef HAVE_EXPAT
768 htmllexstate_t *ctx = &scanner->lexer;
769 int rv = ctx->error ? 3 : ctx->warn;
770 XML_ParserFree(ctx->parser);
771 agxbfree (&ctx->lb);
772 return rv;
773#else
774 (void)scanner;
775
776 return 1;
777#endif
778}
779
781static UNUSED void agxbput_move(agxbuf *dst, const char *src) {
782 // we cannot call `agxbput` itself because it calls `memcpy`, thereby
783 // implicitly assuming that source and destination do not overlap
784 char *src_copy = gv_strdup(src);
785 agxbput(dst, src_copy);
786 free(src_copy);
787}
788
789#ifdef HAVE_EXPAT
790/* eatComment:
791 * Given first character after open comment, eat characters
792 * up to comment close, returning pointer to closing > if it exists,
793 * or null character otherwise.
794 * We rely on HTML strings having matched nested <>.
795 */
796static char *eatComment(htmllexstate_t *ctx, char *p)
797{
798 int depth = 1;
799 char *s = p;
800 char c;
801
802 while (depth && (c = *s++)) {
803 if (c == '<')
804 depth++;
805 else if (c == '>')
806 depth--;
807 }
808 s--; /* move back to '\0' or '>' */
809 if (*s) {
810 char *t = s - 2;
811 if (t < p || !startswith(t, "--")) {
812 agwarningf("Unclosed comment\n");
813 ctx->warn = 1;
814 }
815 }
816 return s;
817}
818
819/* findNext:
820 * Return next XML unit. This is either <..>, an HTML
821 * comment <!-- ... -->, or characters up to next <.
822 */
823static char *findNext(htmllexstate_t *ctx, char *s, agxbuf* xb)
824{
825 char* t = s + 1;
826 char c;
827
828 if (*s == '<') {
829 if (startswith(t, "!--"))
830 t = eatComment(ctx, t + 3);
831 else
832 while (*t && *t != '>')
833 t++;
834 if (*t != '>') {
835 agwarningf("Label closed before end of HTML element\n");
836 ctx->warn = 1;
837 } else
838 t++;
839 } else {
840 t = s;
841 while ((c = *t) && c != '<') {
842 if (c == '&' && *(t+1) != '#') {
843 t = scanEntity(t + 1, xb);
844 }
845 else {
846 agxbputc(xb, c);
847 t++;
848 }
849 }
850 }
851 return t;
852}
853
870static void protect_rsqb(agxbuf *xb) {
871
872 // if the buffer is empty, we have nothing to do
873 if (agxblen(xb) == 0) {
874 return;
875 }
876
877 // check the last character and if it is not ], we have nothing to do
878 char *data = agxbuse(xb);
879 size_t size = strlen(data);
880 assert(size > 0);
881 if (data[size - 1] != ']') {
882 agxbput_move(xb, data);
883 return;
884 }
885
886 // truncate ] and write back the remaining prefix
887 data[size - 1] = '\0';
888 agxbput_move(xb, data);
889
890 // write an XML-escaped version of ] as a replacement
891 agxbput(xb, "&#93;");
892}
893#endif
894
895
897 return htmllineno_ctx(&scanner->lexer);
898}
899
900static unsigned long htmllineno_ctx(htmllexstate_t *ctx) {
901#ifdef HAVE_EXPAT
902 return XML_GetCurrentLineNumber(ctx->parser);
903#else
904 (void)ctx;
905
906 return 0;
907#endif
908}
909
910#ifdef DEBUG
911static void printTok(htmllexstate_t *ctx, int tok)
912{
913 char *s;
914
915 switch (tok) {
916 case T_end_br:
917 s = "T_end_br";
918 break;
919 case T_end_img:
920 s = "T_end_img";
921 break;
922 case T_row:
923 s = "T_row";
924 break;
925 case T_end_row:
926 s = "T_end_row";
927 break;
928 case T_html:
929 s = "T_html";
930 break;
931 case T_end_html:
932 s = "T_end_html";
933 break;
934 case T_end_table:
935 s = "T_end_table";
936 break;
937 case T_end_cell:
938 s = "T_end_cell";
939 break;
940 case T_end_font:
941 s = "T_end_font";
942 break;
943 case T_string:
944 s = "T_string";
945 break;
946 case T_error:
947 s = "T_error";
948 break;
949 case T_n_italic:
950 s = "T_n_italic";
951 break;
952 case T_n_bold:
953 s = "T_n_bold";
954 break;
955 case T_n_underline:
956 s = "T_n_underline";
957 break;
958 case T_n_overline:
959 s = "T_n_overline";
960 break;
961 case T_n_sup:
962 s = "T_n_sup";
963 break;
964 case T_n_sub:
965 s = "T_n_sub";
966 break;
967 case T_n_s:
968 s = "T_n_s";
969 break;
970 case T_HR:
971 s = "T_HR";
972 break;
973 case T_hr:
974 s = "T_hr";
975 break;
976 case T_end_hr:
977 s = "T_end_hr";
978 break;
979 case T_VR:
980 s = "T_VR";
981 break;
982 case T_vr:
983 s = "T_vr";
984 break;
985 case T_end_vr:
986 s = "T_end_vr";
987 break;
988 case T_BR:
989 s = "T_BR";
990 break;
991 case T_br:
992 s = "T_br";
993 break;
994 case T_IMG:
995 s = "T_IMG";
996 break;
997 case T_img:
998 s = "T_img";
999 break;
1000 case T_table:
1001 s = "T_table";
1002 break;
1003 case T_cell:
1004 s = "T_cell";
1005 break;
1006 case T_font:
1007 s = "T_font";
1008 break;
1009 case T_italic:
1010 s = "T_italic";
1011 break;
1012 case T_bold:
1013 s = "T_bold";
1014 break;
1015 case T_underline:
1016 s = "T_underline";
1017 break;
1018 case T_overline:
1019 s = "T_overline";
1020 break;
1021 case T_sup:
1022 s = "T_sup";
1023 break;
1024 case T_sub:
1025 s = "T_sub";
1026 break;
1027 case T_s:
1028 s = "T_s";
1029 break;
1030 default:
1031 s = "<unknown>";
1032 }
1033 if (tok == T_string) {
1034 const char *token_text = agxbuse(ctx->xb);
1035 fprintf(stderr, "%s \"%s\"\n", s, token_text);
1036 agxbput_move(ctx->xb, token_text);
1037 } else
1038 fprintf(stderr, "%s\n", s);
1039}
1040
1041#endif
1042
1043int htmllex(union HTMLSTYPE *htmllval, htmlscan_t *scanner)
1044{
1045#ifdef HAVE_EXPAT
1046 static char *begin_html = "<HTML>";
1047 static char *end_html = "</HTML>";
1048
1049 char *s;
1050 char *endp = 0;
1051 size_t len, llen;
1052 int rv;
1053 htmllexstate_t *ctx = &scanner->lexer;
1054
1055 ctx->htmllval = htmllval;
1056 ctx->tok = 0;
1057 do {
1058 if (ctx->mode == 2)
1059 return EOF;
1060 if (ctx->mode == 0) {
1061 ctx->mode = 1;
1062 s = begin_html;
1063 len = strlen(s);
1064 endp = 0;
1065 } else {
1066 s = ctx->ptr;
1067 if (*s == '\0') {
1068 ctx->mode = 2;
1069 s = end_html;
1070 len = strlen(s);
1071 } else {
1072 endp = findNext(ctx, s,&ctx->lb);
1073 len = (size_t)(endp - s);
1074 }
1075 }
1076
1077 protect_rsqb(&ctx->lb);
1078
1079 ctx->prevtok = ctx->currtok;
1080 ctx->currtok = (strview_t){.data = s, .size = len};
1081 if ((llen = agxblen(&ctx->lb))) {
1082 assert(llen <= INT_MAX && "XML token too long for expat API");
1083 rv = XML_Parse(ctx->parser, agxbuse(&ctx->lb), (int)llen, 0);
1084 } else {
1085 assert(len <= INT_MAX && "XML token too long for expat API");
1086 rv = XML_Parse(ctx->parser, s, (int)len, len ? 0 : 1);
1087 }
1088 if (rv == XML_STATUS_ERROR) {
1089 if (!ctx->error) {
1090 agerrorf("%s in line %lu \n",
1091 XML_ErrorString(XML_GetErrorCode(ctx->parser)), htmllineno(scanner));
1092 error_context(ctx);
1093 ctx->error = 1;
1094 ctx->tok = T_error;
1095 }
1096 }
1097 if (endp)
1098 ctx->ptr = endp;
1099 } while (ctx->tok == 0);
1100#ifdef DEBUG
1101 printTok (ctx, ctx->tok);
1102#endif
1103 return ctx->tok;
1104#else
1105 (void)htmllval;
1106 (void)scanner;
1107
1108 return EOF;
1109#endif
1110}
1111
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:108
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:295
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_alloc(size_t size)
Definition alloc.h:47
container data types API
#define dtinsert(d, o)
Definition cdt.h:186
char * scanEntity(char *t, agxbuf *xb)
Definition utils.c:1082
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1615
static int flags
Definition gc.c:63
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
#define UINT16_MAX
Definition gmlscan.c:340
#define INT8_MAX
Definition gmlscan.c:328
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:200
void agwarningf(const char *fmt,...)
Definition agerror.c:175
void agerrorf(const char *fmt,...)
Definition agerror.c:167
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:157
@ AGPREV
Definition cgraph.h:946
#define GD_charset(g)
Definition types.h:367
#define GD_gvc(g)
Definition types.h:355
replacements for ctype.h functions
static char gv_tolower(int c)
Definition gv_ctype.h:81
agxbput(xb, staging)
int htmllex(union HTMLSTYPE *htmllval, htmlscan_t *scanner)
Definition htmllex.c:1043
static UNUSED void agxbput_move(agxbuf *dst, const char *src)
agxbput, but assume that source and destination may overlap
Definition htmllex.c:781
unsigned long htmllineno(htmlscan_t *scanner)
Definition htmllex.c:896
#define XML_STATUS_ERROR
Definition htmllex.c:39
static void error_context(htmllexstate_t *ctx)
Definition htmllex.c:47
int clearHTMLlexer(htmlscan_t *scanner)
Definition htmllex.c:765
int initHTMLlexer(htmlscan_t *scanner, char *src, agxbuf *xb, htmlenv_t *env)
Definition htmllex.c:730
void htmlerror(htmlscan_t *scanner, const char *msg)
Definition htmllex.c:56
static unsigned long htmllineno_ctx(htmllexstate_t *ctx)
Definition htmllex.c:900
#define T_n_sup
Definition htmlparse.h:138
#define T_end_row
Definition htmlparse.h:126
#define T_end_table
Definition htmlparse.h:129
#define T_br
Definition htmlparse.h:148
#define T_vr
Definition htmlparse.h:145
#define T_error
Definition htmlparse.h:133
#define T_n_s
Definition htmlparse.h:140
#define T_end_cell
Definition htmlparse.h:130
#define T_n_sub
Definition htmlparse.h:139
#define T_n_bold
Definition htmlparse.h:135
#define T_html
Definition htmlparse.h:127
#define T_BR
Definition htmlparse.h:147
#define T_underline
Definition htmlparse.h:156
#define T_sup
Definition htmlparse.h:158
#define T_row
Definition htmlparse.h:125
#define T_table
Definition htmlparse.h:151
#define T_end_vr
Definition htmlparse.h:146
#define T_end_html
Definition htmlparse.h:128
#define T_IMG
Definition htmlparse.h:149
#define T_VR
Definition htmlparse.h:144
#define T_bold
Definition htmlparse.h:155
#define T_end_img
Definition htmlparse.h:124
#define T_sub
Definition htmlparse.h:159
#define T_s
Definition htmlparse.h:160
#define T_n_italic
Definition htmlparse.h:134
#define T_end_font
Definition htmlparse.h:131
#define T_overline
Definition htmlparse.h:157
#define T_hr
Definition htmlparse.h:142
#define T_font
Definition htmlparse.h:153
#define T_italic
Definition htmlparse.h:154
#define T_end_br
Definition htmlparse.h:123
#define T_n_underline
Definition htmlparse.h:136
#define T_cell
Definition htmlparse.h:152
#define T_end_hr
Definition htmlparse.h:143
#define T_string
Definition htmlparse.h:132
#define T_img
Definition htmlparse.h:150
#define T_HR
Definition htmlparse.h:141
#define T_n_overline
Definition htmlparse.h:137
cleanup & scanner
Definition htmlparse.y:289
#define PAD_SET
Definition htmltable.h:33
#define BORDER_RIGHT
Definition htmltable.h:40
#define BORDER_TOP
Definition htmltable.h:39
#define HALIGN_TEXT
Definition htmltable.h:28
#define UNSET_ALIGN
Definition htmltable.h:44
#define HALIGN_LEFT
Definition htmltable.h:26
#define VALIGN_BOTTOM
Definition htmltable.h:30
#define BALIGN_RIGHT
Definition htmltable.h:35
#define BALIGN_LEFT
Definition htmltable.h:36
#define BORDER_BOTTOM
Definition htmltable.h:41
static void free_ritem(row_t *p)
Free row. This closes and frees row’s list, then the item itself is freed.
Definition htmltable.h:117
#define SPACE_SET
Definition htmltable.h:34
#define BORDER_SET
Definition htmltable.h:32
#define BORDER_LEFT
Definition htmltable.h:38
#define BORDER_MASK
Definition htmltable.h:42
#define HALIGN_RIGHT
Definition htmltable.h:25
#define VALIGN_TOP
Definition htmltable.h:29
#define FIXED_FLAG
Definition htmltable.h:24
char * charsetToStr(int c)
Given an internal charset value, return a canonical string representation.
Definition input.c:815
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
Dt_t * textfont_dt
Definition gvcint.h:108
result of partitioning available space, part of maze
Definition grid.h:33
uint16_t rowspan
Definition htmltable.h:158
uint16_t colspan
Definition htmltable.h:157
char * bgcolor
Definition htmltable.h:87
unsigned char border
Definition htmltable.h:91
char * target
Definition htmltable.h:84
char * id
Definition htmltable.h:86
signed char space
Definition htmltable.h:90
unsigned short width
Definition htmltable.h:95
unsigned short height
Definition htmltable.h:96
int gradientangle
Definition htmltable.h:89
char * port
Definition htmltable.h:83
unsigned short flags
Definition htmltable.h:94
char * href
Definition htmltable.h:82
unsigned char pad
Definition htmltable.h:92
char * pencolor
Definition htmltable.h:88
htmlstyle_t style
Definition htmltable.h:97
char * title
Definition htmltable.h:85
graph_t * g
Definition htmltable.h:171
char * scale
Definition htmltable.h:70
char * src
Definition htmltable.h:69
HTMLSTYPE * htmllval
Definition htmlparse.h:229
agxbuf * xb
Definition htmlparse.h:220
strview_t prevtok
Definition htmlparse.h:227
strview_t currtok
Definition htmlparse.h:226
bool dashed
Definition htmltable.h:78
bool dotted
Definition htmltable.h:77
bool rounded
Definition htmltable.h:75
bool radial
Definition htmltable.h:74
bool invisible
Definition htmltable.h:76
bool vrule
vertical rule
Definition htmltable.h:143
size_t row_count
number of rows
Definition htmltable.h:139
bool hrule
horizontal rule
Definition htmltable.h:142
htmldata_t data
Definition htmltable.h:125
rows_t rows
cells
Definition htmltable.h:133
int8_t cellborder
Definition htmltable.h:136
Definition utils.c:752
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
char * color
Definition textspan.h:55
char * name
Definition textspan.h:54
unsigned int flags
Definition textspan.h:58
double size
Definition textspan.h:57
state for an in-progress string tokenization
Definition tokenize.h:36
Non-owning string references.
static bool strview_case_str_eq(strview_t a, const char *b)
compare a string reference to a string for case insensitive equality
Definition strview.h:62
#define HTML_OL
Definition textspan.h:35
#define HTML_IF
Definition textspan.h:30
#define HTML_UL
Definition textspan.h:31
#define HTML_BF
Definition textspan.h:29
#define HTML_SUP
Definition textspan.h:32
#define HTML_S
Definition textspan.h:34
#define GV_TEXTFONT_FLAGS_WIDTH
Definition textspan.h:24
#define HTML_SUB
Definition textspan.h:33
String tokenization.
static strview_t tok_get(const tok_t *t)
get the current token
Definition tokenize.h:76
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
static bool tok_end(const tok_t *t)
is this tokenizer exhausted?
Definition tokenize.h:68
static void tok_next(tok_t *t)
advance to the next token in the string being scanned
Definition tokenize.h:85
htmltbl_t * tbl
Definition htmlparse.h:171
htmlcell_t * cell
Definition htmlparse.h:170
textfont_t * font
Definition htmlparse.h:172
htmlimg_t * img
Definition htmlparse.h:173
Definition grammar.c:90
abstraction for squashing compiler warnings for unused symbols
#define UNUSED
Definition unused.h:25