Graphviz 13.0.0~dev.20250210.0415
Loading...
Searching...
No Matches
gvpr.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * gpr: graph pattern recognizer
13 *
14 * Written by Emden Gansner
15 */
16
17#include "builddate.h"
18#include <assert.h>
19#include <ast/error.h>
20#include <cgraph/cgraph.h>
21#include <cgraph/ingraphs.h>
22#include <common/globals.h>
23#include <getopt.h>
24#include <gvpr/actions.h>
25#include <gvpr/compile.h>
26#include <gvpr/gprstate.h>
27#include <gvpr/gvpr.h>
28#include <setjmp.h>
29#include <stdbool.h>
30#include <stddef.h>
31#include <stdio.h>
32#include <string.h>
33#include <unistd.h>
34#include <util/agxbuf.h>
35#include <util/alloc.h>
36#include <util/exit.h>
37#include <util/gv_ctype.h>
38#include <util/list.h>
39#include <util/unreachable.h>
40
41static char *Info[] = {
42 "gvpr", /* Program */
43 PACKAGE_VERSION, /* Version */
44 BUILDDATE /* Build Date */
45};
46
47static const char *usage =
48 " [-o <ofile>] [-a <args>] ([-f <prog>] | 'prog') [files]\n\
49 -c - use source graph for output\n\
50 -f <pfile> - find program in file <pfile>\n\
51 -i - create node induced subgraph\n\
52 -a <args> - string arguments available as ARGV[0..]\n\
53 -o <ofile> - write output to <ofile>; stdout by default\n\
54 -n - no read-ahead of input graphs\n\
55 -q - turn off warning messages\n\
56 -V - print version info\n\
57 -? - print usage info\n\
58If no files are specified, stdin is used\n";
59
60typedef struct {
61 char *cmdName; /* command name */
62 FILE *outFile; /* output stream; stdout default */
63 char *program; /* program source */
64 int useFile; /* true if program comes from a file */
67 char **inFiles;
68 int argc;
69 char **argv;
70 int state; /* > 0 : continue; <= 0 finish */
72} options;
73
74static FILE *openOut(char *name) {
75 FILE *outs = fopen(name, "w");
76 if (outs == 0) {
77 error(ERROR_ERROR, "could not open %s for writing", name);
78 }
79 return outs;
80}
81
82/* gettok:
83 * Tokenize a string. Tokens consist of either a non-empty string
84 * of non-space characters, or all characters between a pair of
85 * single or double quotes. As usual, we map
86 * \c -> c
87 * for all c
88 * Return next argument token, returning NULL if none.
89 * sp is updated to point to next character to be processed.
90 * NB. There must be white space between tokens. Otherwise, they
91 * are concatenated.
92 */
93static char *gettok(char **sp) {
94 char *s = *sp;
95 char *ws = s;
96 char *rs = s;
97 char c;
98 char q = '\0'; /* if non-0, in quote mode with quote char q */
99
100 while (gv_isspace(*rs))
101 rs++;
102 if ((c = *rs) == '\0')
103 return NULL;
104 while ((c = *rs)) {
105 if (q && (q == c)) { /* end quote */
106 q = '\0';
107 } else if (!q && ((c == '"') || (c == '\''))) {
108 q = c;
109 } else if (c == '\\') {
110 rs++;
111 c = *rs;
112 if (c)
113 *ws++ = c;
114 else {
116 "backslash in argument followed by no character - ignored");
117 rs--;
118 }
119 } else if (q || !gv_isspace(c))
120 *ws++ = c;
121 else
122 break;
123 rs++;
124 }
125 if (*rs)
126 rs++;
127 else if (q)
128 error(ERROR_WARNING, "no closing quote for argument %s", s);
129 *sp = rs;
130 *ws = '\0';
131 return s;
132}
133
134#define NUM_ARGS 100
135
136/* parseArgs:
137 * Split s into whitespace separated tokens, allowing quotes.
138 * Append tokens to argument list and return new number of arguments.
139 * argc is the current number of arguments, with the arguments
140 * stored in *argv.
141 */
142static int parseArgs(char *s, int argc, char ***argv) {
143 int i, cnt = 0;
144 char *args[NUM_ARGS];
145 char *t;
146 char **av;
147
148 assert(argc >= 0);
149
150 while ((t = gettok(&s))) {
151 if (cnt == NUM_ARGS) {
153 "at most %d arguments allowed per -a flag - ignoring rest",
154 NUM_ARGS);
155 break;
156 }
157 args[cnt++] = t;
158 }
159
160 if (cnt) {
161 int oldcnt = argc;
162 argc = oldcnt + cnt;
163 av = gv_recalloc(*argv, (size_t)oldcnt, (size_t)argc, sizeof(char *));
164 for (i = 0; i < cnt; i++)
165 av[oldcnt + i] = gv_strdup(args[i]);
166 *argv = av;
167 }
168 return argc;
169}
170
171#if defined(_WIN32) && !defined(__MINGW32__)
172#define PATHSEP '\\'
173#define LISTSEP ';'
174#else
175#define PATHSEP '/'
176#define LISTSEP ':'
177#endif
178
179static char *concat(char *pfx, char *sfx) {
180 agxbuf sp = {0};
181 agxbprint(&sp, "%s%s", pfx, sfx);
182 return agxbdisown(&sp);
183}
184
185/* resolve:
186 * Translate -f arg parameter into a pathname.
187 * If arg contains '/', return arg.
188 * Else search directories in GVPRPATH for arg.
189 * Return NULL on error.
190 */
191static char *resolve(char *arg, int verbose) {
192 char *path;
193 char *s;
194 char *cp;
195 char c;
196 char *fname = 0;
197 char *pathp = NULL;
198 size_t sz;
199
200 if (strchr(arg, PATHSEP))
201 return gv_strdup(arg);
202
203 path = getenv("GVPRPATH");
204 if (!path)
205 path = getenv("GPRPATH"); // deprecated
206 if (path && (c = *path)) {
207 if (c == LISTSEP) {
208 pathp = path = concat(DFLT_GVPRPATH, path);
209 } else if ((c = path[strlen(path) - 1]) == LISTSEP) {
210 pathp = path = concat(path, DFLT_GVPRPATH);
211 }
212 } else
213 path = DFLT_GVPRPATH;
214 if (verbose)
215 fprintf(stderr, "PATH: %s\n", path);
216 agxbuf fp = {0};
217
218 while (*path && !fname) {
219 if (*path == LISTSEP) { /* skip colons */
220 path++;
221 continue;
222 }
223 cp = strchr(path, LISTSEP);
224 if (cp) {
225 sz = (size_t)(cp - path);
226 agxbput_n(&fp, path, sz);
227 path = cp + 1; /* skip past current colon */
228 } else {
229 sz = agxbput(&fp, path);
230 path += sz;
231 }
232 agxbprint(&fp, "%c%s", PATHSEP, arg);
233 s = agxbuse(&fp);
234
235 if (access(s, R_OK) == 0) {
236 fname = gv_strdup(s);
237 }
238 }
239
240 if (!fname)
241 error(ERROR_ERROR, "Could not find file \"%s\" in GVPRPATH", arg);
242
243 agxbfree(&fp);
244 free(pathp);
245 if (verbose)
246 fprintf(stderr, "file %s resolved to %s\n", arg, fname);
247 return fname;
248}
249
250static char *getOptarg(int c, char **argp, int *argip, int argc, char **argv) {
251 char *rv;
252 char *arg = *argp;
253 int argi = *argip;
254
255 if (*arg) {
256 rv = arg;
257 while (*arg)
258 arg++;
259 *argp = arg;
260 } else if (argi < argc) {
261 rv = argv[argi++];
262 *argip = argi;
263 } else {
264 rv = NULL;
265 error(ERROR_WARNING, "missing argument for option -%c", c);
266 }
267 return rv;
268}
269
270/* doFlags:
271 * Process a command-line argument starting with a '-'.
272 * argi is the index of the next available item in argv[].
273 * argc has its usual meaning.
274 *
275 * return > 0 given next argi value
276 * = 0 for exit with 0
277 * < 0 for error
278 */
279static int doFlags(char *arg, int argi, int argc, char **argv, options *opts) {
280 int c;
281
282 while ((c = *arg++)) {
283 switch (c) {
284 case 'c':
285 opts->compflags.srcout = true;
286 break;
287 case 'C':
288 opts->compflags.srcout = true;
289 opts->compflags.clone = true;
290 break;
291 case 'f':
292 if ((optarg = getOptarg(c, &arg, &argi, argc, argv)) &&
293 (opts->program = resolve(optarg, opts->verbose))) {
294 opts->useFile = 1;
295 } else
296 return -1;
297 break;
298 case 'i':
299 opts->compflags.induce = true;
300 break;
301 case 'n':
302 opts->readAhead = 0;
303 break;
304 case 'a':
305 if ((optarg = getOptarg(c, &arg, &argi, argc, argv))) {
306 opts->argc = parseArgs(optarg, opts->argc, &(opts->argv));
307 } else
308 return -1;
309 break;
310 case 'o':
311 if (!(optarg = getOptarg(c, &arg, &argi, argc, argv)) ||
312 !(opts->outFile = openOut(optarg)))
313 return -1;
314 break;
315 case 'q':
316 setTraceLevel(ERROR_ERROR); /* Don't emit warning messages */
317 break;
318 case 'v':
319 opts->verbose = 1;
320 break;
321 case 'V':
322 fprintf(stderr, "%s version %s (%s)\n", Info[0], Info[1], Info[2]);
323 return 0;
324 break;
325 case '?':
326 if (optopt == '\0' || optopt == '?')
327 fprintf(stderr, "Usage: gvpr%s", usage);
328 else {
330 }
331 return 0;
332 break;
333 default:
334 error(ERROR_WARNING, "option -%c unrecognized", c);
335 break;
336 }
337 }
338 return argi;
339}
340
341static void freeOpts(options opts) {
342 if (opts.outFile != NULL && opts.outFile != stdout)
343 fclose(opts.outFile);
344 free(opts.inFiles);
345 if (opts.useFile)
346 free(opts.program);
347 for (int i = 0; i < opts.argc; i++)
348 free(opts.argv[i]);
349 free(opts.argv);
350}
351
352DEFINE_LIST(strs, char *)
353
354/* scanArgs:
355 * Parse command line options.
356 */
357static options scanArgs(int argc, char **argv) {
358 char *arg;
359 options opts = {0};
360
361 opts.cmdName = argv[0];
362 opts.state = 1;
363 opts.readAhead = 1;
364 setErrorId(opts.cmdName);
365 opts.verbose = 0;
366
367 strs_t input_filenames = {0};
368
369 /* loop over arguments */
370 for (int i = 1; i < argc;) {
371 arg = argv[i++];
372 if (*arg == '-') {
373 i = doFlags(arg + 1, i, argc, argv, &opts);
374 if (i <= 0) {
375 opts.state = i;
376 goto opts_done;
377 }
378 } else if (arg)
379 strs_append(&input_filenames, arg);
380 }
381
382 /* Handle additional semantics */
383 if (opts.useFile == 0) {
384 if (strs_is_empty(&input_filenames)) {
385 error(ERROR_ERROR, "No program supplied via argument or -f option");
386 opts.state = -1;
387 } else {
388 opts.program = strs_pop_front(&input_filenames);
389 }
390 }
391 if (strs_is_empty(&input_filenames)) {
392 opts.inFiles = 0;
393 strs_free(&input_filenames);
394 } else {
395 strs_append(&input_filenames, NULL);
396 opts.inFiles = strs_detach(&input_filenames);
397 }
398
399 if (!opts.outFile)
400 opts.outFile = stdout;
401
402opts_done:
403 if (opts.state <= 0) {
404 if (opts.state < 0)
406 strs_free(&input_filenames);
407 }
408
409 return opts;
410}
411
412static Agobj_t *evalEdge(Gpr_t *state, Expr_t *prog, comp_block *xprog,
413 Agedge_t *e) {
414 case_stmt *cs;
415 bool okay;
416
417 state->curobj = (Agobj_t *)e;
418 for (size_t i = 0; i < xprog->n_estmts; i++) {
419 cs = xprog->edge_stmts + i;
420 if (cs->guard)
421 okay = exeval(prog, cs->guard, state).integer != 0;
422 else
423 okay = true;
424 if (okay) {
425 if (cs->action)
426 exeval(prog, cs->action, state);
427 else
428 agsubedge(state->target, e, 1);
429 }
430 }
431 return state->curobj;
432}
433
434static Agobj_t *evalNode(Gpr_t *state, Expr_t *prog, comp_block *xprog,
435 Agnode_t *n) {
436 case_stmt *cs;
437 bool okay;
438
439 state->curobj = (Agobj_t *)n;
440 for (size_t i = 0; i < xprog->n_nstmts; i++) {
441 cs = xprog->node_stmts + i;
442 if (cs->guard)
443 okay = exeval(prog, cs->guard, state).integer != 0;
444 else
445 okay = true;
446 if (okay) {
447 if (cs->action)
448 exeval(prog, cs->action, state);
449 else
450 agsubnode(state->target, n, 1);
451 }
452 }
453 return (state->curobj);
454}
455
460
461static Agnode_t *nextNode(Gpr_t *state, nodestream *nodes) {
462 Agnode_t *np;
463
464 if (state->tvroot != nodes->oldroot) {
465 np = nodes->oldroot = state->tvroot;
466 } else if (state->flags & GV_NEXT_SET) {
467 np = nodes->oldroot = state->tvroot = state->tvnext;
468 state->flags &= ~GV_NEXT_SET;
469 } else if (nodes->prev) {
470 np = nodes->prev = agnxtnode(state->curgraph, nodes->prev);
471 } else {
472 np = nodes->prev = agfstnode(state->curgraph);
473 }
474 return np;
475}
476
477#define MARKED(x) (((x)->iu.integer) & 1)
478#define MARK(x) (((x)->iu.integer) = 1)
479#define ONSTACK(x) (((x)->iu.integer) & 2)
480#define PUSH(x, e) (((x)->iu.integer) |= 2, (x)->ine = (e))
481#define POP(x) (((x)->iu.integer) &= (~2))
482
483typedef Agedge_t *(*fstedgefn_t)(Agraph_t *, Agnode_t *);
484typedef Agedge_t *(*nxttedgefn_t)(Agraph_t *, Agedge_t *, Agnode_t *);
485
486#define PRE_VISIT 1
487#define POST_VISIT 2
488
489typedef struct {
492 unsigned char undirected;
493 unsigned char visit;
494} trav_fns;
495
497static Agedge_t *agnxtout_(Agraph_t *g, Agedge_t *e, Agnode_t *ignored) {
498 (void)ignored;
499 return agnxtout(g, e);
500}
501
503static Agedge_t *agnxtin_(Agraph_t *g, Agedge_t *e, Agnode_t *ignored) {
504 (void)ignored;
505 return agnxtin(g, e);
506}
507
510static trav_fns REVfns = {agfstin, agnxtin_, 0, 0};
511
512DEFINE_LIST(node_queue, Agnode_t *)
513
514static void travBFS(Gpr_t *state, Expr_t *prog, comp_block *xprog) {
515 nodestream nodes;
516 node_queue_t q = {0};
517 ndata *nd;
518 Agnode_t *n;
519 Agedge_t *cure;
520 Agedge_t *nxte;
521 Agraph_t *g = state->curgraph;
522
523 nodes.oldroot = 0;
524 nodes.prev = 0;
525 while ((n = nextNode(state, &nodes))) {
526 nd = nData(n);
527 if (MARKED(nd))
528 continue;
529 PUSH(nd, 0);
530 node_queue_push_back(&q, n);
531 while (!node_queue_is_empty(&q)) {
532 n = node_queue_pop_front(&q);
533 nd = nData(n);
534 MARK(nd);
535 POP(nd);
536 state->tvedge = nd->ine;
537 if (!evalNode(state, prog, xprog, n))
538 continue;
539 for (cure = agfstedge(g, n); cure; cure = nxte) {
540 nxte = agnxtedge(g, cure, n);
541 nd = nData(cure->node);
542 if (MARKED(nd))
543 continue;
544 if (!evalEdge(state, prog, xprog, cure))
545 continue;
546 if (!ONSTACK(nd)) {
547 node_queue_push_back(&q, cure->node);
548 PUSH(nd, cure);
549 }
550 }
551 }
552 }
553 state->tvedge = 0;
554 node_queue_free(&q);
555}
556
557DEFINE_LIST(edge_stack, Agedge_t *)
558
559static void travDFS(Gpr_t *state, Expr_t *prog, comp_block *xprog,
560 trav_fns *fns) {
561 Agnode_t *n;
562 edge_stack_t stk = {0};
563 Agnode_t *curn;
564 Agedge_t *cure;
565 Agedge_t *entry;
566 int more;
567 ndata *nd;
568 nodestream nodes;
570
571 nodes.oldroot = 0;
572 nodes.prev = 0;
573 while ((n = nextNode(state, &nodes))) {
574 nd = nData(n);
575 if (MARKED(nd))
576 continue;
577 seed.out.node = n;
578 seed.in.node = 0;
579 curn = n;
580 entry = &(seed.out);
581 state->tvedge = cure = 0;
582 MARK(nd);
583 PUSH(nd, 0);
584 if (fns->visit & PRE_VISIT)
585 evalNode(state, prog, xprog, n);
586 more = 1;
587 while (more) {
588 if (cure)
589 cure = fns->nxtedge(state->curgraph, cure, curn);
590 else
591 cure = fns->fstedge(state->curgraph, curn);
592 if (cure) {
593 if (entry == agopp(cure)) /* skip edge used to get here */
594 continue;
595 nd = nData(cure->node);
596 if (MARKED(nd)) {
597 /* For undirected DFS, visit an edge only if its head
598 * is on the stack, to avoid visiting it twice.
599 * This is no problem in directed DFS.
600 */
601 if (fns->undirected) {
602 if (ONSTACK(nd))
603 evalEdge(state, prog, xprog, cure);
604 } else
605 evalEdge(state, prog, xprog, cure);
606 } else {
607 evalEdge(state, prog, xprog, cure);
608 edge_stack_push_back(&stk, entry);
609 state->tvedge = entry = cure;
610 curn = cure->node;
611 cure = 0;
612 if (fns->visit & PRE_VISIT)
613 evalNode(state, prog, xprog, curn);
614 MARK(nd);
615 PUSH(nd, entry);
616 }
617 } else {
618 if (fns->visit & POST_VISIT)
619 evalNode(state, prog, xprog, curn);
620 nd = nData(curn);
621 POP(nd);
622 cure = entry;
623 entry = edge_stack_is_empty(&stk) ? NULL : edge_stack_pop_back(&stk);
624 if (entry == &(seed.out))
625 state->tvedge = 0;
626 else
627 state->tvedge = entry;
628 if (entry)
629 curn = entry->node;
630 else
631 more = 0;
632 }
633 }
634 }
635 state->tvedge = 0;
636 edge_stack_free(&stk);
637}
638
639static void travNodes(Gpr_t *state, Expr_t *prog, comp_block *xprog) {
640 Agnode_t *n;
641 Agnode_t *next;
642 Agraph_t *g = state->curgraph;
643 for (n = agfstnode(g); n; n = next) {
644 next = agnxtnode(g, n);
645 evalNode(state, prog, xprog, n);
646 }
647}
648
649static void travEdges(Gpr_t *state, Expr_t *prog, comp_block *xprog) {
650 Agnode_t *n;
651 Agnode_t *next;
652 Agedge_t *e;
653 Agedge_t *nexte;
654 Agraph_t *g = state->curgraph;
655 for (n = agfstnode(g); n; n = next) {
656 next = agnxtnode(g, n);
657 for (e = agfstout(g, n); e; e = nexte) {
658 nexte = agnxtout(g, e);
659 evalEdge(state, prog, xprog, e);
660 }
661 }
662}
663
664static void travFlat(Gpr_t *state, Expr_t *prog, comp_block *xprog) {
665 Agnode_t *n;
666 Agnode_t *next;
667 Agedge_t *e;
668 Agedge_t *nexte;
669 Agraph_t *g = state->curgraph;
670 for (n = agfstnode(g); n; n = next) {
671 next = agnxtnode(g, n);
672 if (!evalNode(state, prog, xprog, n))
673 continue;
674 if (xprog->n_estmts > 0) {
675 for (e = agfstout(g, n); e; e = nexte) {
676 nexte = agnxtout(g, e);
677 evalEdge(state, prog, xprog, e);
678 }
679 }
680 }
681}
682
683/* doCleanup:
684 * Reset node traversal data
685 */
686static void doCleanup(Agraph_t *g) {
687 Agnode_t *n;
688 ndata *nd;
689
690 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
691 nd = nData(n);
692 nd->ine = NULL;
693 nd->iu.integer = 0;
694 }
695}
696
697/* traverse:
698 * return true if traversal requires cleanup
699 */
700static bool traverse(Gpr_t *state, Expr_t *prog, comp_block *bp, bool cleanup) {
701 if (!state->target) {
702 char *target;
703 agxbuf tmp = {0};
704
705 if (state->name_used) {
706 agxbprint(&tmp, "%s%d", state->tgtname, state->name_used);
707 target = agxbuse(&tmp);
708 } else
709 target = state->tgtname;
710 state->name_used++;
711 /* make sure target subgraph does not exist */
712 while (agsubg(state->curgraph, target, 0)) {
713 state->name_used++;
714 agxbprint(&tmp, "%s%d", state->tgtname, state->name_used);
715 target = agxbuse(&tmp);
716 }
717 state->target = openSubg(state->curgraph, target);
718 agxbfree(&tmp);
719 }
720 if (!state->outgraph)
721 state->outgraph = state->target;
722
723 switch (state->tvt) {
724 case TV_flat:
725 travFlat(state, prog, bp);
726 break;
727 case TV_bfs:
728 if (cleanup)
729 doCleanup(state->curgraph);
730 travBFS(state, prog, bp);
731 cleanup = true;
732 break;
733 case TV_dfs:
734 if (cleanup)
735 doCleanup(state->curgraph);
737 travDFS(state, prog, bp, &DFSfns);
738 cleanup = true;
739 break;
740 case TV_fwd:
741 if (cleanup)
742 doCleanup(state->curgraph);
744 travDFS(state, prog, bp, &FWDfns);
745 cleanup = true;
746 break;
747 case TV_rev:
748 if (cleanup)
749 doCleanup(state->curgraph);
751 travDFS(state, prog, bp, &REVfns);
752 cleanup = true;
753 break;
754 case TV_postdfs:
755 if (cleanup)
756 doCleanup(state->curgraph);
758 travDFS(state, prog, bp, &DFSfns);
759 cleanup = true;
760 break;
761 case TV_postfwd:
762 if (cleanup)
763 doCleanup(state->curgraph);
765 travDFS(state, prog, bp, &FWDfns);
766 cleanup = true;
767 break;
768 case TV_postrev:
769 if (cleanup)
770 doCleanup(state->curgraph);
772 travDFS(state, prog, bp, &REVfns);
773 cleanup = true;
774 break;
775 case TV_prepostdfs:
776 if (cleanup)
777 doCleanup(state->curgraph);
779 travDFS(state, prog, bp, &DFSfns);
780 cleanup = true;
781 break;
782 case TV_prepostfwd:
783 if (cleanup)
784 doCleanup(state->curgraph);
786 travDFS(state, prog, bp, &FWDfns);
787 cleanup = true;
788 break;
789 case TV_prepostrev:
790 if (cleanup)
791 doCleanup(state->curgraph);
793 travDFS(state, prog, bp, &REVfns);
794 cleanup = true;
795 break;
796 case TV_ne:
797 travNodes(state, prog, bp);
798 travEdges(state, prog, bp);
799 break;
800 case TV_en:
801 travEdges(state, prog, bp);
802 travNodes(state, prog, bp);
803 break;
804 default:
805 UNREACHABLE();
806 }
807 return cleanup;
808}
809
810/* addOutputGraph:
811 * Append output graph to option struct.
812 * We know uopts and state->outgraph are non-NULL.
813 */
814static void addOutputGraph(Gpr_t *state, gvpropts *uopts) {
815 Agraph_t *g = state->outgraph;
816
817 if ((agroot(g) == state->curgraph) && !uopts->ingraphs)
818 g = (Agraph_t *)cloneO(0, (Agobj_t *)g);
819
820 uopts->outgraphs = gv_recalloc(uopts->outgraphs, uopts->n_outgraphs,
821 uopts->n_outgraphs + 1, sizeof(Agraph_t *));
822 uopts->n_outgraphs++;
823 uopts->outgraphs[uopts->n_outgraphs - 1] = g;
824}
825
826static void chkClose(Agraph_t *g) {
827 gdata *data;
828
829 data = gData(g);
830 if (data->lock.locked)
831 data->lock.zombie = true;
832 else
833 agclose(g);
834}
835
836static Agraph_t *ing_read(void *fp) { return readG(fp); }
837
846
847/* gvexitf:
848 * Only used if GV_USE_EXIT not set during exeval.
849 * This implies setjmp/longjmp set up.
850 */
851static void gvexitf(void *env, int v) {
852 gvpr_state_t *st = env;
853
854 longjmp(st->state->jbuf, v);
855}
856
857static void gverrorf(Expr_t *handle, Exdisc_t *discipline, int level,
858 const char *fmt, ...) {
859 va_list ap;
860
861 va_start(ap, fmt);
862 errorv((discipline && handle) ? *((char **)handle) : (char *)handle, level,
863 fmt, ap);
864 va_end(ap);
865
866 if (level >= ERROR_ERROR) {
867 Gpr_t *state = discipline->user;
868 if (state->flags & GV_USE_EXIT)
869 graphviz_exit(1);
870 else if (state->flags & GV_USE_JUMP)
871 longjmp(state->jbuf, 1);
872 }
873}
874
875/* gvpr_core:
876 * Return 0 on success; non-zero on error.
877 *
878 * FIX/TODO:
879 * - close non-source/non-output graphs
880 * - flag to clone target graph?
881 * - remove assignment in boolean warning if wrapped in ()
882 * - do automatic cast for array indices if type is known
883 * - array initialization
884 */
885static int gvpr_core(int argc, char *argv[], gvpropts *uopts,
886 gvpr_state_t *gs) {
888 int rv = 0;
889
891
892 gs->opts = scanArgs(argc, argv);
893 if (gs->opts.state <= 0) {
894 return gs->opts.state;
895 }
896
897 if (gs->opts.verbose)
899 gs->prog = parseProg(gs->opts.program, gs->opts.useFile);
900 if (gs->prog == NULL) {
901 return 1;
902 }
903 info.outFile = gs->opts.outFile;
904 info.argc = gs->opts.argc;
905 info.argv = gs->opts.argv;
906 info.errf = gverrorf;
907 if (uopts)
908 info.flags = uopts->flags;
909 else
910 info.flags = 0;
911 if ((uopts->flags & GV_USE_EXIT))
912 info.exitf = 0;
913 else
914 info.exitf = gvexitf;
915 gs->state = openGPRState(&info);
916 if (gs->state == NULL) {
917 return 1;
918 }
919 if (uopts->bindings)
920 addBindings(gs->state, uopts->bindings);
921 gs->xprog = compileProg(gs->prog, gs->state, gs->opts.compflags);
922 if (gs->xprog == NULL) {
923 return 1;
924 }
925
926 initGPRState(gs->state);
927
928 if ((uopts->flags & GV_USE_OUTGRAPH)) {
929 uopts->outgraphs = 0;
930 uopts->n_outgraphs = 0;
931 }
932
933 if (!(uopts->flags & GV_USE_EXIT)) {
934 gs->state->flags |= GV_USE_JUMP;
935 if ((rv = setjmp(gs->state->jbuf))) {
936 return rv;
937 }
938 }
939
940 bool incoreGraphs = uopts && uopts->ingraphs;
941
942 if (gs->opts.verbose)
943 fprintf(stderr, "Parse/compile/init: %.2f secs.\n", gvelapsed_sec());
944 /* do begin */
945 if (gs->xprog->begin_stmt != NULL)
946 exeval(gs->xprog->prog, gs->xprog->begin_stmt, gs->state);
947
948 /* if program is not null */
949 if (gs->xprog->uses_graph) {
950 if (uopts && uopts->ingraphs)
951 gs->ing = newIngGraphs(0, uopts->ingraphs, ing_read);
952 else
953 gs->ing = newIng(0, gs->opts.inFiles, ing_read);
954
955 if (gs->opts.verbose)
957 Agraph_t *nextg = NULL;
958 for (gs->state->curgraph = nextGraph(gs->ing); gs->state->curgraph;
959 gs->state->curgraph = nextg) {
960 if (gs->opts.verbose)
961 fprintf(stderr, "Read graph: %.2f secs.\n", gvelapsed_sec());
962 gs->state->infname = fileName(gs->ing);
963 if (gs->opts.readAhead)
964 nextg = gs->state->nextgraph = nextGraph(gs->ing);
965 bool cleanup = false;
966
967 for (size_t i = 0; i < gs->xprog->n_blocks; i++) {
968 comp_block *bp = gs->xprog->blocks + i;
969
970 /* begin graph */
971 if (incoreGraphs && gs->opts.compflags.clone)
972 gs->state->curgraph =
973 (Agraph_t *)cloneO(0, &gs->state->curgraph->base);
974 gs->state->curobj = &gs->state->curgraph->base;
975 gs->state->tvroot = 0;
976 if (bp->begg_stmt)
977 exeval(gs->xprog->prog, bp->begg_stmt, gs->state);
978
979 /* walk graph */
980 if (bp->does_walk_graph) {
981 cleanup = traverse(gs->state, gs->xprog->prog, bp, cleanup);
982 }
983 }
984
985 /* end graph */
986 gs->state->curobj = &gs->state->curgraph->base;
987 if (gs->xprog->endg_stmt != NULL)
988 exeval(gs->xprog->prog, gs->xprog->endg_stmt, gs->state);
989 if (gs->opts.verbose)
990 fprintf(stderr, "Finish graph: %.2f secs.\n", gvelapsed_sec());
991
992 /* if $O == $G and $T is empty, delete $T */
993 if (gs->state->outgraph == gs->state->curgraph &&
994 gs->state->target != NULL && !agnnodes(gs->state->target))
995 agdelete(gs->state->curgraph, gs->state->target);
996
997 /* output graph, if necessary
998 * For this, the outgraph must be defined, and either
999 * be non-empty or the -c option was used.
1000 */
1001 if (gs->state->outgraph != NULL &&
1002 (agnnodes(gs->state->outgraph) || gs->opts.compflags.srcout)) {
1003 if (uopts && (uopts->flags & GV_USE_OUTGRAPH))
1004 addOutputGraph(gs->state, uopts);
1005 else
1006 sfioWrite(gs->state->outgraph, gs->opts.outFile);
1007 }
1008
1009 if (!incoreGraphs)
1010 chkClose(gs->state->curgraph);
1011 gs->state->target = 0;
1012 gs->state->outgraph = 0;
1013
1014 if (gs->opts.verbose)
1015 gvstart_timer();
1016 if (!gs->opts.readAhead)
1017 nextg = nextGraph(gs->ing);
1018 if (gs->opts.verbose && nextg != NULL) {
1019 fprintf(stderr, "Read graph: %.2f secs.\n", gvelapsed_sec());
1020 }
1021 }
1022 }
1023
1024 /* do end */
1025 gs->state->curgraph = 0;
1026 gs->state->curobj = 0;
1027 if (gs->xprog->end_stmt != NULL)
1028 exeval(gs->xprog->prog, gs->xprog->end_stmt, gs->state);
1029
1030 return 0;
1031}
1032
1037int gvpr(int argc, char *argv[], gvpropts *uopts) {
1038 gvpr_state_t gvpr_state = {0};
1039
1040 // initialize opts to something that makes freeOpts() a no-op if we fail early
1041 gvpr_state.opts.outFile = stdout;
1042
1043 int rv = gvpr_core(argc, argv, uopts, &gvpr_state);
1044
1045 // free all allocated resources
1046 freeParseProg(gvpr_state.prog);
1047 freeCompileProg(gvpr_state.xprog);
1048 closeGPRState(gvpr_state.state);
1049 if (gvpr_state.ing != NULL) {
1050 closeIngraph(gvpr_state.ing);
1051 }
1052 freeOpts(gvpr_state.opts);
1053
1054 return rv;
1055}
1056
Agobj_t * cloneO(Agraph_t *g, Agobj_t *obj)
Definition actions.c:353
int sfioWrite(Agraph_t *g, FILE *fp)
Definition actions.c:547
void gvstart_timer(void)
Definition actions.c:867
double gvelapsed_sec(void)
Definition actions.c:869
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:250
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:327
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static char * gv_strdup(const char *original)
Definition alloc.h:101
int verbose
Definition bcomps.c:62
abstract graph C library, Cgraph API
comp_prog * compileProg(parse_prog *inp, Gpr_t *state, compflags_t flags)
Definition compile.c:2368
Agraph_t * openSubg(Agraph_t *g, char *name)
Definition compile.c:2501
void freeCompileProg(comp_prog *p)
Definition compile.c:2448
Agraph_t * readG(FILE *fp)
Definition compile.c:2468
#define nData(n)
Definition compile.h:57
#define gData(g)
Definition compile.h:58
static char * fname
void setTraceLevel(int i)
Definition error.c:33
void errorv(const char *id, int level, const char *s, va_list ap)
Definition error.c:35
void setErrorId(char *id)
Definition error.c:30
void setErrorErrors(int errors)
Definition error.c:31
#define ERROR_WARNING
Definition error.h:35
#define ERROR_ERROR
Definition error.h:36
#define ERROR_USAGE
Definition error.h:42
Extype_t exeval(Expr_t *ex, Exnode_t *exnode, void *env)
Definition exeval.c:1965
static long seed
Definition exeval.c:1036
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
static void cleanup(void)
Definition gmlparse.c:128
void free(void *)
void initGPRState(Gpr_t *state)
Definition gprstate.c:30
void addBindings(Gpr_t *state, gvprbinding *bindings)
Definition gprstate.c:91
Gpr_t * openGPRState(gpr_info *info)
Definition gprstate.c:34
void closeGPRState(Gpr_t *state)
Definition gprstate.c:119
@ TV_flat
Definition gprstate.h:27
@ TV_fwd
Definition gprstate.h:29
@ TV_rev
Definition gprstate.h:29
@ TV_postdfs
Definition gprstate.h:30
@ TV_dfs
Definition gprstate.h:29
@ TV_prepostfwd
Definition gprstate.h:31
@ TV_en
Definition gprstate.h:27
@ TV_prepostrev
Definition gprstate.h:31
@ TV_prepostdfs
Definition gprstate.h:31
@ TV_postfwd
Definition gprstate.h:30
@ TV_bfs
Definition gprstate.h:28
@ TV_ne
Definition gprstate.h:27
@ TV_postrev
Definition gprstate.h:30
node NULL
Definition grammar.y:163
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:200
int agnnodes(Agraph_t *g)
Definition graph.c:159
#define agopp(e)
opposite edge: flip Agedgepair_s.out ⇄ Agedgepair_s.in/*#end#*‍/
Definition cgraph.h:885
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:351
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:69
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:94
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:85
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:55
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:20
Agraph_t * agroot(void *obj)
Definition obj.c:168
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
replacements for ctype.h functions
static bool gv_isspace(int c)
Definition gv_ctype.h:55
agxbput(xb, staging)
static opts_t opts
Definition gvgen.c:394
static void freeOpts(options opts)
Definition gvpr.c:341
static char * concat(char *pfx, char *sfx)
Definition gvpr.c:179
#define MARKED(x)
Definition gvpr.c:477
static char * resolve(char *arg, int verbose)
Definition gvpr.c:191
static char * Info[]
Definition gvpr.c:41
static Agobj_t * evalNode(Gpr_t *state, Expr_t *prog, comp_block *xprog, Agnode_t *n)
Definition gvpr.c:434
static void gvexitf(void *env, int v)
Definition gvpr.c:851
int gvpr(int argc, char *argv[], gvpropts *uopts)
Definition gvpr.c:1037
static void chkClose(Agraph_t *g)
Definition gvpr.c:826
static void travNodes(Gpr_t *state, Expr_t *prog, comp_block *xprog)
Definition gvpr.c:639
static void doCleanup(Agraph_t *g)
Definition gvpr.c:686
#define ONSTACK(x)
Definition gvpr.c:479
static char * gettok(char **sp)
Definition gvpr.c:93
#define PRE_VISIT
Definition gvpr.c:486
static FILE * openOut(char *name)
Definition gvpr.c:74
static bool traverse(Gpr_t *state, Expr_t *prog, comp_block *bp, bool cleanup)
Definition gvpr.c:700
#define MARK(x)
Definition gvpr.c:478
static Agedge_t * agnxtin_(Agraph_t *g, Agedge_t *e, Agnode_t *ignored)
agnxtin wrapper to tweak calling convention
Definition gvpr.c:503
#define LISTSEP
Definition gvpr.c:176
Agedge_t *(* nxttedgefn_t)(Agraph_t *, Agedge_t *, Agnode_t *)
Definition gvpr.c:484
static options scanArgs(int argc, char **argv)
Definition gvpr.c:357
static trav_fns DFSfns
Definition gvpr.c:508
static void travDFS(Gpr_t *state, Expr_t *prog, comp_block *xprog, trav_fns *fns)
Definition gvpr.c:559
static trav_fns REVfns
Definition gvpr.c:510
#define POST_VISIT
Definition gvpr.c:487
static trav_fns FWDfns
Definition gvpr.c:509
static void travBFS(Gpr_t *state, Expr_t *prog, comp_block *xprog)
Definition gvpr.c:514
static void travFlat(Gpr_t *state, Expr_t *prog, comp_block *xprog)
Definition gvpr.c:664
static void travEdges(Gpr_t *state, Expr_t *prog, comp_block *xprog)
Definition gvpr.c:649
static Agnode_t * nextNode(Gpr_t *state, nodestream *nodes)
Definition gvpr.c:461
static void addOutputGraph(Gpr_t *state, gvpropts *uopts)
Definition gvpr.c:814
static int gvpr_core(int argc, char *argv[], gvpropts *uopts, gvpr_state_t *gs)
Definition gvpr.c:885
static int doFlags(char *arg, int argi, int argc, char **argv, options *opts)
Definition gvpr.c:279
static void gverrorf(Expr_t *handle, Exdisc_t *discipline, int level, const char *fmt,...)
Definition gvpr.c:857
Agedge_t *(* fstedgefn_t)(Agraph_t *, Agnode_t *)
Definition gvpr.c:483
#define PATHSEP
Definition gvpr.c:175
static Agedge_t * agnxtout_(Agraph_t *g, Agedge_t *e, Agnode_t *ignored)
agnxtout wrapper to tweak calling convention
Definition gvpr.c:497
static Agraph_t * ing_read(void *fp)
Definition gvpr.c:836
static const char * usage
Definition gvpr.c:47
#define NUM_ARGS
Definition gvpr.c:134
static Agobj_t * evalEdge(Gpr_t *state, Expr_t *prog, comp_block *xprog, Agedge_t *e)
Definition gvpr.c:412
static int parseArgs(char *s, int argc, char ***argv)
Definition gvpr.c:142
#define PUSH(x, e)
Definition gvpr.c:480
#define POP(x)
Definition gvpr.c:481
static char * getOptarg(int c, char **argp, int *argip, int argc, char **argv)
Definition gvpr.c:250
graph pattern scanning and processing language API, main function gvpr
#define GV_USE_OUTGRAPH
Definition gvpr.h:49
#define GV_USE_EXIT
Definition gvpr.h:47
#define GV_NEXT_SET
Definition gvpr.h:53
#define GV_USE_JUMP
Definition gvpr.h:51
table Syntax error
Definition htmlparse.y:294
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Definition ingraphs.c:156
void closeIngraph(ingraph_state *sp)
Definition ingraphs.c:147
Agraph_t * nextGraph(ingraph_state *sp)
Definition ingraphs.c:61
ingraph_state * newIng(ingraph_state *sp, char **files, Agraph_t *(*readf)(void *))
Definition ingraphs.c:120
ingraph_state * newIngGraphs(ingraph_state *sp, Agraph_t **graphs, Agraph_t *(*readf)(void *))
Definition ingraphs.c:128
supports user-supplied data
#define DEFINE_LIST(name, type)
Definition list.h:22
void freeParseProg(parse_prog *prog)
Definition parse.c:491
parse_prog * parseProg(char *input, int isFile)
parses input into gpr sections
Definition parse.c:381
Agnode_t * node
Definition cgraph.h:272
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:424
Agobj_t base
Definition cgraph.h:425
void * user
Definition expr.h:194
Definition expr.h:198
char * tgtname
Definition gprstate.h:43
Agobj_t * curobj
Definition gprstate.h:39
Agraph_t * target
Definition gprstate.h:37
Agraph_t * curgraph
Definition gprstate.h:35
int flags
Definition gprstate.h:53
Agraph_t * outgraph
Definition gprstate.h:38
Agnode_t * tvroot
Definition gprstate.h:47
Agnode_t * tvnext
Definition gprstate.h:48
char * infname
Definition gprstate.h:44
jmp_buf jbuf
Definition gprstate.h:56
Agraph_t * nextgraph
Definition gprstate.h:36
int name_used
Definition gprstate.h:50
trav_type tvt
Definition gprstate.h:46
Exnode_t * guard
Definition compile.h:26
Exnode_t * action
Definition compile.h:27
case_stmt * node_stmts
Definition compile.h:71
Exnode_t * begg_stmt
Definition compile.h:67
size_t n_nstmts
Definition compile.h:69
size_t n_estmts
Definition compile.h:70
case_stmt * edge_stmts
Definition compile.h:72
bool does_walk_graph
does this block have a node or edge statement?
Definition compile.h:68
Exnode_t * begin_stmt
Definition compile.h:78
comp_block * blocks
Definition compile.h:80
Expr_t * prog
Definition compile.h:77
size_t n_blocks
Definition compile.h:79
bool uses_graph
does this program use the input graph?
Definition compile.h:76
Exnode_t * endg_stmt
Definition compile.h:81
Exnode_t * end_stmt
Definition compile.h:82
bool srcout
Definition compile.h:61
bool clone
Definition compile.h:63
Definition legal.c:50
collective managed state used in gvpr_core
Definition gvpr.c:839
parse_prog * prog
Definition gvpr.c:840
comp_prog * xprog
Definition gvpr.c:842
options opts
Definition gvpr.c:844
ingraph_state * ing
Definition gvpr.c:841
Gpr_t * state
Definition gvpr.c:843
size_t n_outgraphs
if GV_USE_OUTGRAPH set, output graphs
Definition gvpr.h:65
Agraph_t ** outgraphs
Definition gvpr.h:66
gvprbinding * bindings
Definition gvpr.h:70
int flags
Definition gvpr.h:69
Agraph_t ** ingraphs
Definition gvpr.h:64
Agnode_t * prev
Definition gvpr.c:458
Agnode_t * oldroot
Definition gvpr.c:457
Definition gvpr.c:60
compflags_t compflags
Definition gvpr.c:65
int argc
Definition gvpr.c:68
char * program
Definition gvpr.c:63
int readAhead
Definition gvpr.c:66
int verbose
Definition gvpr.c:71
char * cmdName
Definition gvpr.c:61
FILE * outFile
Definition gvpr.c:62
int useFile
Definition gvpr.c:64
int state
Definition gvpr.c:70
char ** inFiles
Definition gvpr.c:67
char ** argv
Definition gvpr.c:69
Definition types.h:81
unsigned char visit
Definition gvpr.c:493
nxttedgefn_t nxtedge
Definition gvpr.c:491
unsigned char undirected
Definition gvpr.c:492
fstedgefn_t fstedge
Definition gvpr.c:490
long long integer
Definition exparse.c:324
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30