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