#pragma prototyped
#include <actions.h>
#include <error.h>
#include <ast.h>
#include <compile.h>
#include <sfstr.h>
#include <string.h>
#include <stdio.h>
#define KINDS(p) ((AGTYPE(p) == AGRAPH) ? "graph" : (AGTYPE(p) == AGNODE) ? "node" : "edge")
Agraph_t*
sameG (void* p1, void* p2, char* fn, char* msg)
{
Agobj_t* obj1 = OBJ(p1);
Agobj_t* obj2 = OBJ(p2);
Agraph_t* root;
root = agroot(agraphof(obj1));
if (root != agroot(agraphof(obj2))) {
if (msg)
error (ERROR_WARNING, "%s in %s() belong to different graphs", msg, fn);
else
error (ERROR_WARNING, "%s and %s in %s() belong to different graphs",
KINDS(obj1), KINDS(obj2), fn);
return 0;
}
else return root;
}
int
indexOf (char* s1, char* s2)
{
char c1 = *s2;
char c;
char* p;
int len2;
if (c1 == '\0') return 0;
p = s1;
len2 = strlen(s2)-1;
while ((c = *p++)) {
if (c != c1) continue;
if (strncmp (p, s2+1, len2) == 0) return ((p-s1)-1);
}
return -1;
}
int
match (char* str, char* pat)
{
int sub[2];
if (strgrpmatch(str, pat, sub, 1, STR_MAXIMAL)) {
return (sub[0]);
}
else return -1;
}
void
nodeInduce(Agraph_t *selected)
{
Agnode_t* n;
Agedge_t* e;
Agraph_t* base;
if (!selected) return;
base = agroot (selected);
if (base == selected) return;
for (n = agfstnode(selected); n; n = agnxtnode(n)) {
for (e = agfstout(agsubnode(base,n,FALSE)); e; e = agnxtout(e)) {
if (agsubnode(selected,aghead(e),FALSE))
agsubedge(selected,e,TRUE);
}
}
}
static void
copyAttr (int kind, Agobj_t* src, Agobj_t* tgt)
{
Agraph_t* srcg;
Agraph_t* tgtg;
Agsym_t* sym = 0;
Agsym_t* tsym = 0;
srcg = agraphof(src);
tgtg = agraphof(tgt);
while ((sym = agnxtattr(srcg,kind,sym))) {
tsym = agattrsym (tgt, sym->name);
if (!tsym)
tsym = agattr(tgtg,kind,sym->name,"");
agxset (tgt, tsym, agxget(src,sym));
}
}
Agobj_t*
copy(Agraph_t *g, Agobj_t* obj)
{
Agobj_t* nobj = 0;
Agedge_t* e;
Agnode_t* h;
Agnode_t* t;
int kind = AGTYPE(obj);
char* name = agnameof(obj);
if ((kind != AGRAPH) && !g) {
error (ERROR_FATAL, "NULL graph with non-graph object in copy()");
return 0;
}
switch (kind) {
case AGNODE :
nobj = (Agobj_t*)openNode (g, name);
break;
case AGRAPH :
if (g)
nobj = (Agobj_t*)openSubg (g, name);
else
nobj = (Agobj_t*)openG (name, ((Agraph_t*)obj)->desc);
break;
case AGEDGE :
e = (Agedge_t*)obj;
t = openNode (g, agnameof(agtail(e)));
h = openNode (g, agnameof(aghead(e)));
nobj = (Agobj_t*)openEdge (t, h, name);
break;
}
if (nobj) copyAttr (kind,obj,nobj);
return nobj;
}
static Agraph_t*
cloneSubg (Agraph_t* tgt, Agraph_t* g)
{
Agraph_t* ng;
Agraph_t* sg;
Agnode_t* t;
Agnode_t* newt;
Agnode_t* newh;
Agedge_t* e;
Agedge_t* newe;
ng = (Agraph_t*)(copy (tgt, OBJ(g)));
if (!ng) return 0;
for (t = agfstnode(g); t; t = agnxtnode (t)) {
newt = agnode (tgt, agnameof(t), 0);
if (!newt)
error (ERROR_PANIC, "node %s not found in cloned graph %s",
agnameof(t), agnameof (tgt));
agsubnode(ng, newt, 1);
}
for (t = agfstnode(g); t; t = agnxtnode (t)) {
newt = agnode (tgt, agnameof(t), 0);
for (e = agfstout(t); e; e = agnxtout (e)) {
newh = agnode (tgt, agnameof(aghead(e)), 0);
newe = agedge (newt, newh, agnameof(e), 0);
if (!newe)
error (ERROR_PANIC, "edge (%s,%s)[%s] not found in cloned graph %s",
agnameof(agtail(e)), agnameof(aghead(e)), agnameof(e), agnameof(tgt));
agsubedge(ng, newe, 1);
}
}
for (sg = agfstsubg(g); sg; sg = agnxtsubg (sg)) {
if (!cloneSubg(ng, sg)) {
error (ERROR_FATAL, "error cloning subgraph %s from graph %s",
agnameof(sg), agnameof(g));
}
}
return ng;
}
static void
cloneGraph (Agraph_t* tgt, Agraph_t* src)
{
Agedge_t* e;
Agnode_t* t;
Agraph_t* sg;
for (t = agfstnode(src); t; t = agnxtnode (t)) {
if (!copy(tgt, OBJ(t))) {
error (ERROR_FATAL, "error cloning node %s from graph %s",
agnameof(t), agnameof(src));
}
}
for (t = agfstnode(src); t; t = agnxtnode (t)) {
for (e = agfstout(t); e; e = agnxtout (e)) {
if (!copy(tgt, OBJ(e))) {
error (ERROR_FATAL, "error cloning edge (%s,%s)[%s] from graph %s",
agnameof(agtail(e)), agnameof(aghead(e)), agnameof(e),
agnameof(src));
}
}
}
for (sg = agfstsubg(src); sg; sg = agnxtsubg (sg)) {
if (!cloneSubg(tgt, sg)) {
error (ERROR_FATAL, "error cloning subgraph %s from graph %s",
agnameof(sg), agnameof(src));
}
}
}
Agobj_t*
clone(Agraph_t *g, Agobj_t* obj)
{
Agobj_t* nobj = 0;
Agedge_t* e;
Agnode_t* h;
Agnode_t* t;
int kind = AGTYPE(obj);
char* name = agnameof(obj);
if ((kind != AGRAPH) && !g) {
error (ERROR_FATAL, "NULL graph with non-graph object in clone()");
return 0;
}
switch (kind) {
case AGNODE :
nobj = (Agobj_t*)openNode (g, name);
if (nobj) copyAttr (kind,obj,nobj);
break;
case AGRAPH :
if (g)
nobj = (Agobj_t*)openSubg (g, name);
else
nobj = (Agobj_t*)openG (name, ((Agraph_t*)obj)->desc);
if (nobj) copyAttr (kind,obj,nobj);
cloneGraph ((Agraph_t*)nobj, (Agraph_t*)obj);
break;
case AGEDGE :
e = (Agedge_t*)obj;
t = (Agnode_t*)clone (g, OBJ(agtail(e)));
h = (Agnode_t*)clone (g, OBJ(aghead(e)));
nobj = (Agobj_t*)openEdge (t, h, name);
if (nobj) copyAttr (kind,obj,nobj);
break;
}
return nobj;
}
#define CCMARKED(n) (((nData(n))->iu.integer)&2)
#define CCMARK(n) (((nData(n))->iu.integer) |= 2)
#define CCUNMARK(n) (((nData(n))->iu.integer) &= ~2)
static void
cc_dfs (Agraph_t* comp, Agnode_t* n)
{
Agedge_t* e;
Agnode_t* other;
CCMARK (n);
agidnode (comp, AGID(n), 1);
for (e = agfstedge(n); e ; e = agnxtedge(e,n)) {
if (agtail(e) == n) other = aghead(e);
else other = agtail(e);
if (!CCMARKED(other)) cc_dfs(comp,other);
}
}
Agraph_t*
compOf (Agraph_t* g, Agnode_t* n)
{
Agraph_t* cg;
Agnode_t* np;
static int id;
char name[64];
if (!(n = agidnode (g,AGID(n),0))) return 0;
for (np = agfstnode(g); np; np = agnxtnode (np))
CCUNMARK (np);
sprintf (name, "_cc_%d", id++);
cg = openSubg(g,name);
cc_dfs (cg, n);
return cg;
}
Agedge_t*
isEdge (Agnode_t* t, Agnode_t* h, char* key)
{
Agraph_t* root;
if ((root = sameG(t,h,"isEdge", "tail and head node"))) {
t = (Agnode_t*)agrebind (root, OBJ(t));
h = (Agnode_t*)agrebind (root, OBJ(h));
return agedge (t,h,key,0);
}
else return 0;
}
int
isIn (Agraph_t* gp, Agobj_t* objp)
{
if (!sameG(gp,objp,"isIn",0)) return 0;
switch (AGTYPE(objp)) {
case AGRAPH :
return (agparent((Agraph_t*)objp) == gp);
case AGNODE :
return (agidnode (gp, AGID(objp), 0) != 0);
default :
return (agsubedge (gp, (Agedge_t*)objp, 0) != 0);
}
}
Agnode_t*
addNode (Agraph_t* gp, Agnode_t* np)
{
if (!sameG(gp,np,"addNode",0)) return 0;
return agsubnode (gp, np, 1);
}
Agedge_t*
addEdge (Agraph_t* gp, Agedge_t* ep)
{
if (!sameG(gp,ep,"addEdge",0)) return 0;
return agsubedge (gp, ep, 1);
}
int
lockGraph (Agraph_t* g, int v)
{
gdata* data;
int oldv;
if (g != agroot(g)) {
error (ERROR_WARNING, "Graph argument to lock() is not a root graph");
return -1;
}
data = gData(g);
oldv = data->lock & 1;
if (v > 0) data->lock |= 1;
else if ((v == 0) && oldv) {
if (data->lock & 2) agclose (g);
else data->lock = 0;
}
return oldv;
}
int
deleteObj (Agraph_t* g, Agobj_t* obj)
{
gdata* data;
if (AGTYPE(obj) == AGRAPH) {
g = (Agraph_t*)obj;
if (g != agroot(g)) return agclose(g);
data = gData(g);
if (data->lock & 1) {
error (ERROR_WARNING, "Cannot delete locked graph %s", agnameof(g));
data->lock |= 2;
return -1;
}
else agclose(g);
}
if (!g) g = agroot(agraphof(obj));
obj = agrebind (g, obj);
if (obj) return agdelete (g, obj);
else return -1;
}
int
writeFile (Agraph_t* g, char* f)
{
int rv;
Sfio_t* fp;
if (!f) {
error(1,"NULL string passed to writeG");
return 1;
}
fp = sfopen (0, f, "w");
if (!fp) {
error(1,"Could not open %s for writing in writeG", f);
return 1;
}
rv = agwrite (g, fp);
sfclose (fp);
return rv;
}
Agraph_t*
readFile (char* f)
{
Agraph_t* gp;
Sfio_t* fp;
if (!f) {
error(1,"NULL string passed to readG");
return 0;
}
fp = sfopen (0, f, "r");
if (!fp) {
error(1,"Could not open %s for reading in readG", f);
return 0;
}
gp = readG (fp);
sfclose (fp);
return gp;
}
int
fwriteFile (Expr_t* ex, Agraph_t* g, int fd)
{
Sfio_t* sp;
if (fd < 0
|| fd >= elementsof(ex->file)
|| !((sp = ex->file[fd])))
{
exerror("fwriteG: %d: invalid descriptor", fd);
return 0;
}
return agwrite (g, sp);
}
Agraph_t*
freadFile (Expr_t* ex, int fd)
{
Sfio_t* sp;
if (fd < 0
|| fd >= elementsof(ex->file)
|| !((sp = ex->file[fd])))
{
exerror("freadG: %d: invalid descriptor", fd);
return 0;
}
return readG (sp);
}
int
openFile (Expr_t* ex, char* fname, char* mode)
{
int idx;
for (idx = 3; idx < elementsof(ex->file); idx++)
if (!ex->file[idx]) break;
if (idx == elementsof(ex->file)) {
error(1,"openF: no available descriptors");
return -1;
}
ex->file[idx] = sfopen (0, fname, mode);
if (ex->file[idx]) return idx;
else return -1;
}
int
closeFile (Expr_t* ex, int fd)
{
int rv;
if ((0 <= fd) && (fd <= 2)) {
error(1,"closeF: cannot close standard stream %d", fd);
return -1;
}
if (!ex->file[fd]) {
error(1,"closeF: stream %d not open", fd);
return -1;
}
rv = sfclose (ex->file[fd]);
if (!rv) ex->file[fd] = 0;
return rv;
}
char*
readLine (Expr_t* ex, int fd)
{
Sfio_t* sp;
int c;
Sfio_t* tmps;
char* line;
if (fd < 0 || fd >= elementsof(ex->file) || !((sp = ex->file[fd]))) {
exerror("readL: %d: invalid descriptor", fd);
return exstring (ex, "");
}
tmps = sfstropen();
while (((c = sfgetc (sp)) > 0) && (c != '\n'))
sfputc(tmps, c);
if (c == '\n')
sfputc(tmps, c);
line = exstring (ex, sfstruse(tmps));
sfclose (tmps);
return line;
}
int
compare (Agobj_t* l, Agobj_t* r)
{
char lkind, rkind;
if (AGID(l) < AGID(r)) return -1;
else if (AGID(l) > AGID(r)) return 1;
lkind = AGTYPE(l);
rkind = AGTYPE(r);
if (lkind == 3) lkind = 2;
if (rkind == 3) rkind = 2;
if (lkind == rkind) return 0;
else if (lkind < rkind) return -1;
else return 1;
}
char*
canon (Expr_t* pgm, char* arg)
{
int sz;
char* buf;
char* p;
sz = strlen(arg) + 10;
buf = exstralloc (pgm, 0, sz);
p = agcanonstr (arg, buf);
if (p == arg)
exstrfree (pgm, buf);
else
p = exstralloc (pgm, buf, strlen(buf) + 1);
return p;
}