]> granicus.if.org Git - graphviz/commitdiff
Initial version of Cgraph created.
authornorth <devnull@localhost>
Thu, 25 Oct 2007 20:27:57 +0000 (20:27 +0000)
committernorth <devnull@localhost>
Thu, 25 Oct 2007 20:27:57 +0000 (20:27 +0000)
lib/cgraph/pend.c [new file with mode: 0644]
lib/cgraph/rec.c [new file with mode: 0644]
lib/cgraph/refstr.c [new file with mode: 0644]

diff --git a/lib/cgraph/pend.c b/lib/cgraph/pend.c
new file mode 100644 (file)
index 0000000..bd0c8c1
--- /dev/null
@@ -0,0 +1,299 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#include <cghdr.h>
+
+static char DRName[] = "_AG_pending";
+
+typedef struct symlist_s {
+    Agsym_t *sym;
+    struct symlist_s *link;
+} symlist_t;
+
+/* this record describes one pending callback on one object */
+typedef struct {
+    Dtlink_t link;
+    unsigned long key;         /* universal key for main or sub-object */
+    Agraph_t *g;
+    Agobj_t *obj;
+    symlist_t *symlist;                /* attributes involved */
+} pending_cb_t;
+
+typedef struct {
+    Agrec_t h;
+    struct {
+       Dict_t *g, *n, *e;
+    } ins, mod, del;
+} pendingset_t;
+
+static void free_symlist(pending_cb_t * pcb)
+{
+    symlist_t *s, *t;
+
+    for (s = pcb->symlist; s; s = t) {
+       t = s->link;
+       agfree(pcb->g, s);
+    }
+}
+
+static void freef(Dict_t * dict, void *ptr, Dtdisc_t * disc)
+{
+    pending_cb_t *pcb;
+
+    NOTUSED(dict);
+    NOTUSED(disc);
+    pcb = ptr;
+    free_symlist(pcb);
+    agfree(pcb->g, pcb);
+}
+
+static Dtdisc_t Disc = {
+    offsetof(pending_cb_t, key),       /* sort by 'key' */
+    sizeof(unsigned long),
+    0,                         /* link offset */
+    NIL(Dtmake_f),
+    freef,
+    NIL(Dtcompar_f),
+    NIL(Dthash_f)
+};
+
+static Dict_t *dictof(pendingset_t * ds, Agobj_t * obj, int kind)
+{
+    Dict_t **dict_ref = NIL(Dict_t **);
+
+    dict_ref = 0;
+    switch (AGTYPE(obj)) {
+    case AGRAPH:
+       switch (kind) {
+       case CB_INITIALIZE:
+           dict_ref = &(ds->ins.g);
+           break;
+       case CB_UPDATE:
+           dict_ref = &(ds->mod.g);
+           break;
+       case CB_DELETION:
+           dict_ref = &(ds->del.g);
+           break;
+       default:
+           break;
+       }
+       break;
+    case AGNODE:
+       switch (kind) {
+       case CB_INITIALIZE:
+           dict_ref = &(ds->ins.n);
+           break;
+       case CB_UPDATE:
+           dict_ref = &(ds->mod.n);
+           break;
+       case CB_DELETION:
+           dict_ref = &(ds->del.n);
+           break;
+       default:
+           break;
+       }
+       break;
+    case AGEDGE:
+       switch (kind) {
+       case CB_INITIALIZE:
+           dict_ref = &(ds->ins.e);
+           break;
+       case CB_UPDATE:
+           dict_ref = &(ds->mod.e);
+           break;
+       case CB_DELETION:
+           dict_ref = &(ds->del.e);
+           break;
+       default:
+           break;
+       }
+       break;
+    default:
+       break;
+    }
+
+    if (dict_ref == 0)
+       agerr(AGERR, "pend dictof a bad object");
+    if (*dict_ref == NIL(Dict_t *))
+       *dict_ref = agdtopen(agraphof(obj), &Disc, Dttree);
+    return *dict_ref;
+}
+
+static unsigned long genkey(Agobj_t * obj)
+{
+    return obj->tag.id;
+}
+
+static pending_cb_t *lookup(Dict_t * dict, Agobj_t * obj)
+{
+    pending_cb_t key, *rv;
+
+    key.key = genkey(obj);
+    rv = (pending_cb_t *) dtsearch(dict, &key);
+    return rv;
+}
+
+static void record_sym(Agobj_t * obj, pending_cb_t * handle,
+                      Agsym_t * optsym)
+{
+    symlist_t *sym, *nsym, *psym;
+
+    psym = NIL(symlist_t *);
+    for (sym = handle->symlist; sym; psym = sym, sym = sym->link) {
+       if (sym->sym == optsym)
+           break;
+       if (sym == NIL(symlist_t *)) {
+           nsym = agalloc(agraphof(obj), sizeof(symlist_t));
+           nsym->sym = optsym;
+           if (psym)
+               psym->link = nsym;
+           else
+               handle->symlist = nsym;
+       }
+       /* else we already have a callback registered */
+    }
+}
+
+static pending_cb_t *insert(Dict_t * dict, Agraph_t * g, Agobj_t * obj,
+                           Agsym_t * optsym)
+{
+    pending_cb_t *handle;
+    handle = agalloc(agraphof(obj), sizeof(pending_cb_t));
+    handle->obj = obj;
+    handle->key = genkey(obj);
+    handle->g = g;
+    if (optsym) {
+       handle->symlist =
+           (symlist_t *) agalloc(handle->g, sizeof(symlist_t));
+       handle->symlist->sym = optsym;
+    }
+    dtinsert(dict, handle);
+    return handle;
+}
+
+static void purge(Dict_t * dict, Agobj_t * obj)
+{
+    pending_cb_t *handle;
+
+    if ((handle = lookup(dict, obj))) {
+       dtdelete(dict, handle);
+    }
+}
+
+void agrecord_callback(Agraph_t * g, Agobj_t * obj, int kind,
+                      Agsym_t * optsym)
+{
+    pendingset_t *pending;
+    Dict_t *dict;
+    pending_cb_t *handle;
+
+    pending =
+       (pendingset_t *) agbindrec(g, DRName, sizeof(pendingset_t), FALSE);
+
+    switch (kind) {
+    case CB_INITIALIZE:
+       assert(lookup(dictof(pending, obj, CB_UPDATE), obj) == 0);
+       assert(lookup(dictof(pending, obj, CB_DELETION), obj) == 0);
+       dict = dictof(pending, obj, CB_INITIALIZE);
+       handle = lookup(dict, obj);
+       if (handle == 0)
+           handle = insert(dict, g, obj, optsym);
+       break;
+    case CB_UPDATE:
+       if (lookup(dictof(pending, obj, CB_INITIALIZE), obj))
+           break;
+       if (lookup(dictof(pending, obj, CB_DELETION), obj))
+           break;
+       dict = dictof(pending, obj, CB_UPDATE);
+       handle = lookup(dict, obj);
+       if (handle == 0)
+           handle = insert(dict, g, obj, optsym);
+       record_sym(obj, handle, optsym);
+       break;
+    case CB_DELETION:
+       purge(dictof(pending, obj, CB_INITIALIZE), obj);
+       purge(dictof(pending, obj, CB_UPDATE), obj);
+       dict = dictof(pending, obj, CB_DELETION);
+       handle = lookup(dict, obj);
+       if (handle == 0)
+           handle = insert(dict, g, obj, optsym);
+       break;
+    default:
+       agerr(AGERR,"agrecord_callback of a bad object");
+    }
+}
+
+static void cb(Dict_t * dict, int callback_kind)
+{
+    pending_cb_t *pcb;
+    Agraph_t *g;
+    symlist_t *psym;
+    Agcbstack_t *stack;
+
+    if (dict)
+       while ((pcb = (pending_cb_t *) dtfirst(dict))) {
+           g = pcb->g;
+           stack = g->clos->cb;
+           switch (callback_kind) {
+           case CB_INITIALIZE:
+               aginitcb(g, pcb->obj, stack);
+               break;
+           case CB_UPDATE:
+               for (psym = pcb->symlist; psym; psym = psym->link)
+                   agupdcb(g, pcb->obj, psym->sym, stack);
+               break;
+           case CB_DELETION:
+               agdelcb(g, pcb->obj, stack);
+               break;
+           }
+           dtdelete(dict, pcb);
+       }
+}
+
+static void agrelease_callbacks(Agraph_t * g)
+{
+    pendingset_t *pending;
+    if (NOT(g->clos->callbacks_enabled)) {
+       g->clos->callbacks_enabled = TRUE;
+       pending =
+           (pendingset_t *) agbindrec(g, DRName, sizeof(pendingset_t),
+                                      FALSE);
+       /* this destroys objects in the opposite of their order of creation */
+       cb(pending->ins.g, CB_INITIALIZE);
+       cb(pending->ins.n, CB_INITIALIZE);
+       cb(pending->ins.e, CB_INITIALIZE);
+
+       cb(pending->mod.g, CB_UPDATE);
+       cb(pending->mod.n, CB_UPDATE);
+       cb(pending->mod.e, CB_UPDATE);
+
+       cb(pending->del.e, CB_DELETION);
+       cb(pending->del.n, CB_DELETION);
+       cb(pending->del.g, CB_DELETION);
+    }
+}
+
+int agcallbacks(Agraph_t * g, int flag)
+{
+    if (flag && NOT(g->clos->callbacks_enabled))
+       agrelease_callbacks(g);
+    if (g->clos->callbacks_enabled) {
+       g->clos->callbacks_enabled = flag;
+       return TRUE;
+    }
+    g->clos->callbacks_enabled = flag;
+    return FALSE;
+}
diff --git a/lib/cgraph/rec.c b/lib/cgraph/rec.c
new file mode 100644 (file)
index 0000000..ebbe4bc
--- /dev/null
@@ -0,0 +1,266 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#include       <cghdr.h>
+
+/*
+ * run time records
+ */
+
+static void set_data(Agobj_t * obj, Agrec_t * data, int mtflock)
+{
+    Agedge_t *e;
+
+    obj->data = data;
+    obj->tag.mtflock = mtflock;
+    if ((AGTYPE(obj) == AGINEDGE) || (AGTYPE(obj) == AGOUTEDGE)) {
+       e = agopp((Agedge_t *) obj);
+       AGDATA(e) = data;
+       e->base.tag.mtflock = mtflock;
+    }
+}
+
+/* find record in circular list and do optional move-to-front */
+Agrec_t *aggetrec(void *obj, char *name, int mtf)
+{
+    Agobj_t *hdr;
+    Agrec_t *d, *first;
+
+    hdr = (Agobj_t *) obj;
+    first = d = hdr->data;
+    while (d) {
+       if ((d->name == name) || streq(name, d->name))
+           break;
+       d = d->next;
+       if (d == first) {
+           d = NIL(Agrec_t *);
+           break;
+       }
+    }
+    if (d) {
+       if (hdr->tag.mtflock) {
+           if (mtf && (hdr->data != d))
+               agerr(AGERR, "move to front lock inconsistency");
+       } else {
+           if ((d != first) || (mtf != hdr->tag.mtflock))
+               set_data(hdr, d, mtf);  /* Always optimize */
+       }
+    }
+    return d;
+}
+
+/* insert the record in data list of this object (only) */
+static void objputrec(Agraph_t * g, Agobj_t * obj, void *arg)
+{
+    Agrec_t *firstrec, *newrec;
+
+    newrec = arg;
+    firstrec = obj->data;
+    if (firstrec == NIL(Agrec_t *))
+       newrec->next = newrec;  /* 0 elts */
+    else {
+       if (firstrec->next == firstrec) {
+           firstrec->next = newrec;    /* 1 elt */
+           newrec->next = firstrec;
+       } else {
+           newrec->next = firstrec->next;
+           firstrec->next = newrec;
+       }
+    }
+    if (NOT(obj->tag.mtflock))
+       set_data(obj, newrec, FALSE);
+}
+
+/* attach a new record of the given size to the object.
+ */
+void *agbindrec(void *arg_obj, char *recname, unsigned int recsize,
+               int mtf)
+{
+    Agraph_t *g;
+    Agobj_t *obj;
+    Agrec_t *rec;
+
+    obj = (Agobj_t *) arg_obj;
+    g = agraphof(obj);
+    rec = aggetrec(obj, recname, FALSE);
+    if ((rec == NIL(Agrec_t *)) && (recsize > 0)) {
+       rec = (Agrec_t *) agalloc(g, (int) recsize);
+       rec->name = agstrdup(g, recname);
+       switch (obj->tag.objtype) {
+       case AGRAPH:
+           objputrec(g, obj, rec);
+           break;
+       case AGNODE:
+           objputrec(g, obj, rec);
+           break;
+       case AGINEDGE:
+       case AGOUTEDGE:
+           objputrec(g, obj, rec);
+           break;
+       }
+    }
+    if (mtf)
+       aggetrec(arg_obj, recname, TRUE);
+    return (void *) rec;
+}
+
+
+/* if obj points to rec, move its data pointer. break any mtf lock(?) */
+static void objdelrec(Agraph_t * g, Agobj_t * obj, void *arg_rec)
+{
+    Agrec_t *rec = (Agrec_t *) arg_rec, *newrec;
+    if (obj->data == rec) {
+       if (rec->next == rec)
+           newrec = NIL(Agrec_t *);
+       else
+           newrec = rec->next;
+       set_data(obj, newrec, FALSE);
+    }
+}
+
+/* delete a record from a circular data list */
+static void listdelrec(Agobj_t * obj, Agrec_t * rec)
+{
+    Agrec_t *prev;
+
+    prev = obj->data;
+    while (prev->next != rec) {
+       prev = prev->next;
+       assert(prev != obj->data);
+    }
+    /* following is a harmless no-op if the list is trivial */
+    prev->next = rec->next;
+}
+
+int agdelrec(void *arg_obj, char *name)
+{
+    Agobj_t *obj;
+    Agrec_t *rec;
+    Agraph_t *g;
+
+    obj = (Agobj_t *) arg_obj;
+    g = agraphof(obj);
+    rec = aggetrec(obj, name, FALSE);
+    if (rec) {
+       listdelrec(obj, rec);   /* zap it from the circular list */
+       switch (obj->tag.objtype) {     /* refresh any stale pointers */
+       case AGRAPH:
+           objdelrec(g, obj, rec);
+           break;
+       case AGNODE:
+       case AGINEDGE:
+       case AGOUTEDGE:
+           agapply(agroot(g), obj, objdelrec, rec, FALSE);
+           break;
+       }
+       agstrfree(g, rec->name);
+       agfree(g, rec);
+       return SUCCESS;
+    } else
+       return FAILURE;
+}
+
+static void simple_delrec(Agraph_t * g, Agobj_t * obj, void *rec_name)
+{
+    agdelrec(obj, rec_name);
+}
+
+#ifdef OLD
+void agclean(Agraph_t * g, char *graphdata, char *nodedata, char *edgedata)
+{
+    Agnode_t *n;
+    Agedge_t *e;
+
+    if (nodedata || edgedata) {
+       for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+           if (edgedata)
+               for (e = agfstout(n); e; e = agnxtout(g, e)) {
+                   agdelrec(e, edgedata);
+               }
+           if (nodedata)
+               agdelrec(n, nodedata);
+       }
+    }
+    agdelrec(g, graphdata);
+}
+#endif
+
+void aginit(Agraph_t * g, int kind, char *rec_name, int rec_size, int mtf)
+{
+    Agnode_t *n;
+    Agedge_t *e;
+
+    switch (kind) {
+    case AGRAPH:
+       agbindrec(g, rec_name, rec_size, mtf);
+       break;
+    case AGNODE:
+    case AGOUTEDGE:
+    case AGINEDGE:
+       for (n = agfstnode(g); n; n = agnxtnode(g, n))
+           if (kind == AGNODE)
+               agbindrec(n, rec_name, rec_size, mtf);
+           else {
+               for (e = agfstout(g, n); e; e = agnxtout(g, e))
+                   agbindrec(e, rec_name, rec_size, mtf);
+           }
+       break;
+    default:
+       break;
+    }
+}
+
+void agclean(Agraph_t * g, int kind, char *rec_name)
+{
+    Agnode_t *n;
+    Agedge_t *e;
+
+    switch (kind) {
+    case AGRAPH:
+       agapply(g, (Agobj_t *) g, simple_delrec, rec_name, TRUE);
+       break;
+    case AGNODE:
+    case AGOUTEDGE:
+    case AGINEDGE:
+       for (n = agfstnode(g); n; n = agnxtnode(g, n))
+           if (kind == AGNODE)
+               agdelrec(n, rec_name);
+           else {
+               for (e = agfstout(g, n); e; e = agnxtout(g, e))
+                   agdelrec(e, rec_name);
+           }
+       break;
+    default:
+       break;
+    }
+}
+
+void agrecclose(Agobj_t * obj)
+{
+    Agraph_t *g;
+    Agrec_t *rec, *nrec;
+
+    g = agraphof(obj);
+    if ((rec = obj->data)) {
+       do {
+           nrec = rec->next;
+           agstrfree(g, rec->name);
+           agfree(g, rec);
+           rec = nrec;
+       } while (rec != obj->data);
+    }
+    obj->data = NIL(Agrec_t *);
+}
diff --git a/lib/cgraph/refstr.c b/lib/cgraph/refstr.c
new file mode 100644 (file)
index 0000000..18c3cbc
--- /dev/null
@@ -0,0 +1,208 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#include <cghdr.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+ * reference counted strings.
+ */
+
+static unsigned long HTML_BIT; /* msbit of unsigned long */
+static unsigned long CNT_BITS; /* complement of HTML_BIT */
+
+typedef struct refstr_t {
+    Dtlink_t link;
+    unsigned long refcnt;
+    char *s;
+    char store[1];             /* this is actually a dynamic array */
+} refstr_t;
+
+static Dtdisc_t Refstrdisc = {
+    offsetof(refstr_t, s),     /* key */
+    -1,                                /* size */
+    0,                         /* link offset */
+    NIL(Dtmake_f),
+    agdictobjfree,
+    NIL(Dtcompar_f),
+    NIL(Dthash_f),
+    agdictobjmem,
+    NIL(Dtevent_f)
+};
+
+static Dict_t *Refdict_default;
+
+/* refdict:
+ * Return the string dictionary associated with g.
+ * If necessary, create it.
+ * As a side-effect, set html masks. This assumes 8-bit bytes.
+ */
+static Dict_t *refdict(Agraph_t * g)
+{
+    Dict_t **dictref;
+
+    if (g)
+       dictref = &(g->clos->strdict);
+    else
+       dictref = &Refdict_default;
+    if (*dictref == NIL(Dict_t *)) {
+       *dictref = agdtopen(g, &Refstrdisc, Dttree);
+       HTML_BIT = ((unsigned int) 1) << (sizeof(unsigned int) * 8 - 1);
+       CNT_BITS = ~HTML_BIT;
+    }
+    return *dictref;
+}
+
+void agstrclose(Agraph_t * g)
+{
+    agdtclose(g, refdict(g));
+}
+
+static refstr_t *refsymbind(Dict_t * strdict, char *s)
+{
+    refstr_t key, *r;
+    key.s = s;
+    r = (refstr_t *) dtsearch(strdict, &key);
+    return r;
+}
+
+static char *refstrbind(Dict_t * strdict, char *s)
+{
+    refstr_t *r;
+    r = refsymbind(strdict, s);
+    if (r)
+       return r->s;
+    else
+       return NIL(char *);
+}
+
+char *agstrbind(Agraph_t * g, char *s)
+{
+    return refstrbind(refdict(g), s);
+}
+
+char *agstrdup(Agraph_t * g, char *s)
+{
+    refstr_t *r;
+    Dict_t *strdict;
+    size_t sz;
+
+    if (s == NIL(char *))
+        return NIL(char *);
+    strdict = refdict(g);
+    r = refsymbind(strdict, s);
+    if (r)
+       r->refcnt++;
+    else {
+       sz = sizeof(refstr_t) + strlen(s);
+       if (g)
+           r = (refstr_t *) agalloc(g, sz);
+       else
+           r = (refstr_t *) malloc(sz);
+       r->refcnt = 1;
+       strcpy(r->store, s);
+       r->s = r->store;
+       dtinsert(strdict, r);
+    }
+    return r->s;
+}
+
+char *agstrdup_html(Agraph_t * g, char *s)
+{
+    refstr_t *r;
+    Dict_t *strdict;
+    size_t sz;
+
+    if (s == NIL(char *))
+        return NIL(char *);
+    strdict = refdict(g);
+    r = refsymbind(strdict, s);
+    if (r)
+       r->refcnt++;
+    else {
+       sz = sizeof(refstr_t) + strlen(s);
+       if (g)
+           r = (refstr_t *) agalloc(g, sz);
+       else
+           r = (refstr_t *) malloc(sz);
+       r->refcnt = 1 | HTML_BIT;
+       strcpy(r->store, s);
+       r->s = r->store;
+       dtinsert(strdict, r);
+    }
+    return r->s;
+}
+
+int agstrfree(Agraph_t * g, char *s)
+{
+    refstr_t *r;
+    Dict_t *strdict;
+
+    if (s == NIL(char *))
+        return FAILURE;
+
+    strdict = refdict(g);
+    r = refsymbind(strdict, s);
+    if (r && (r->s == s)) {
+       r->refcnt--;
+       if ((r->refcnt && CNT_BITS) == 0) {
+           agdtdelete(g, strdict, r);
+           /*
+              if (g) agfree(g,r);
+              else free(r);
+            */
+       }
+    }
+    if (r == NIL(refstr_t *))
+       return FAILURE;
+    return SUCCESS;
+}
+
+/* aghtmlstr:
+ * Return true if s is an HTML string.
+ * We assume s points to the datafield store[0] of a refstr.
+ */
+int aghtmlstr(char *s)
+{
+    refstr_t *key;
+
+    if (s == NULL)
+       return 0;
+    key = (refstr_t *) (s - offsetof(refstr_t, store[0]));
+    return (key->refcnt & HTML_BIT);
+}
+
+#ifdef DEBUG
+static int refstrprint(Dict_t * dict, void *ptr, void *user)
+{
+    refstr_t *r;
+
+    NOTUSED(dict);
+    r = ptr;
+    NOTUSED(user);
+    write(2, r->s, strlen(r->s));
+    write(2, "\n", 1);
+    return 0;
+}
+
+void agrefstrdump(Agraph_t * g)
+{
+    dtwalk(Refdict_default, refstrprint, 0);
+}
+#endif