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