wolffd@0: .de P0 wolffd@0: .nf wolffd@0: \f5 wolffd@0: .. wolffd@0: .de P1 wolffd@0: \fP wolffd@0: .fi wolffd@0: .. wolffd@0: .de Ss wolffd@0: .fl wolffd@0: .ne 2 wolffd@0: .SS "\\$1" wolffd@0: .. wolffd@0: .TH LIBCGRAPH 3 "30 JULY 2007" wolffd@0: .SH "NAME" wolffd@0: \fBlibcgraph\fR \- abstract graph library wolffd@0: .SH "SYNOPSIS" wolffd@0: ."ta .75i 1.5i 2.25i 3i 3.75i 4.5i 5.25i 6i wolffd@0: .PP wolffd@0: .nf wolffd@0: .P0 wolffd@0: #include wolffd@0: .P1 wolffd@0: .SS "TYPES" wolffd@0: .P0 wolffd@0: Agraph_t; wolffd@0: Agnode_t; wolffd@0: Agedge_t; wolffd@0: Agdesc_t; wolffd@0: Agdisc_t; wolffd@0: Agsym_t; wolffd@0: .P1 wolffd@0: .SS "GRAPHS" wolffd@0: .P0 wolffd@0: Agraph_t *agopen(char *name, Agdesc_t kind, Agdisc_t *disc); wolffd@0: int agclose(Agraph_t *g); wolffd@0: Agraph_t *agread(void *channel, Agdisc_t *); wolffd@0: void agreadline(int line_no); wolffd@0: void agsetfile(char *file_name); wolffd@0: Agraph_t *agconcat(Agraph_t *g, void *channel, Agdisc_t *disc) wolffd@0: int agwrite(Agraph_t *g, void *channel); wolffd@0: int agnnodes(Agraph_t *g),agnedges(Agraph_t *g); wolffd@0: int agisdirected(Agraph_t * g),agisundirected(Agraph_t * g),agisstrict(Agraph_t * g), agissimple(Agraph_t * g); wolffd@0: .SS "SUBGRAPHS" wolffd@0: .P0 wolffd@0: Agraph_t *agsubg(Agraph_t *g, char *name, int createflag); wolffd@0: Agraph_t *agidsubg(Agraph_t * g, unsigned long id, int cflag); wolffd@0: Agraph_t *agfstsubg(Agraph_t *g), agnxtsubg(Agraph_t *); wolffd@0: Agraph_t *agparent(Agraph_t *g); wolffd@0: int agdelsubg(Agraph_t * g, Agraph_t * sub); /* same as agclose() */ wolffd@0: .P1 wolffd@0: .SS "NODES" wolffd@0: .P0 wolffd@0: Agnode_t *agnode(Agraph_t *g, char *name, int createflag); wolffd@0: Agnode_t *agidnode(Agraph_t *g, ulong id, int createflag); wolffd@0: Agnode_t *agsubnode(Agraph_t *g, Agnode_t *n, int createflag); wolffd@0: Agnode_t *agfstnode(Agraph_t *g); wolffd@0: Agnode_t *agnxtnode(Agraph_t *g, Agnode_t *n); wolffd@0: Agnode_t *agprvnode(Agraph_t *g, Agnode_t *n); wolffd@0: Agnode_t *aglstnode(Agraph_t *g); wolffd@0: int agdelnode(Agraph_t *g, Agnode_t *n); wolffd@0: int agdegree(Agnode_t *n, int use_inedges, int use_outedges); wolffd@0: .P1 wolffd@0: .SS "EDGES" wolffd@0: .P0 wolffd@0: Agedge_t *agedge(Agraph_t* g, Agnode_t *t, Agnode_t *h, char *name, int createflag); wolffd@0: Agedge_t *agidedge(Agraph_t * g, Agnode_t * t, Agnode_t * h, unsigned long id, int createflag); wolffd@0: Agedge_t *agsubedge(Agraph_t *g, Agedge_t *e, int createflag); wolffd@0: Agnode_t *aghead(Agedge_t *e), *agtail(Agedge_t *e); wolffd@0: Agedge_t *agfstedge(Agraph_t* g, Agnode_t *n); wolffd@0: Agedge_t *agnxtedge(Agraph_t* g, Agedge_t *e, Agnode_t *n); wolffd@0: Agedge_t *agfstin(Agraph_t* g, Agnode_t *n); wolffd@0: Agedge_t *agnxtin(Agraph_t* g, Agedge_t *e); wolffd@0: Agedge_t *agfstout(Agraph_t* g, Agnode_t *n); wolffd@0: Agedge_t *agnxtout(Agraph_t* g, Agedge_t *e); wolffd@0: int agdeledge(Agraph_t *g, Agedge_t *e); wolffd@0: .SS "STRING ATTRIBUTES" wolffd@0: .P0 wolffd@0: Agsym_t *agattr(Agraph_t *g, int kind, char *name, char *value); wolffd@0: Agsym_t *agattrsym(void *obj, char *name); wolffd@0: Agsym_t *agnxtattr(Agraph_t *g, int kind, Agsym_t *attr); wolffd@0: char *agget(void *obj, char *name); wolffd@0: char *agxget(void *obj, Agsym_t *sym); wolffd@0: int agset(void *obj, char *name, char *value); wolffd@0: int agxset(void *obj, Agsym_t *sym, char *value); wolffd@0: int agsafeset(void *obj, char *name, char *value, char *def); wolffd@0: .P1 wolffd@0: .SS "RECORDS" wolffd@0: .P0 wolffd@0: void *agbindrec(void *obj, char *name, unsigned int size, move_to_front); wolffd@0: Agrec_t *aggetrec(void *obj, char *name, int move_to_front); wolffd@0: int agdelrec(Agraph_t *g, void *obj, char *name); wolffd@0: int agcopyattr(void *, void *); wolffd@0: void aginit(Agraph_t * g, int kind, char *rec_name, int rec_size, int move_to_front); wolffd@0: void agclean(Agraph_t * g, int kind, char *rec_name); wolffd@0: .P1 wolffd@0: .SS "CALLBACKS" wolffd@0: .P0 wolffd@0: Agcbdisc_t *agpopdisc(Agraph_t *g); wolffd@0: void agpushdisc(Agraph_t *g, Agcbdisc_t *disc); wolffd@0: void agmethod(Agraph_t *g, void *obj, Agcbdisc_t *disc, int initflag); wolffd@0: .P1 wolffd@0: .SS "MEMORY" wolffd@0: .P0 wolffd@0: void *agalloc(Agraph_t *g, size_t request); wolffd@0: void *agrealloc(Agraph_t *g, void *ptr, size_t oldsize, size_t newsize); wolffd@0: void agfree(Agraph_t *g, void *ptr); wolffd@0: .P1 wolffd@0: .SS "STRINGS" wolffd@0: .P0 wolffd@0: char *agstrdup(Agraph_t *, char *); wolffd@0: char *agstrdup_html(Agraph_t *, char *); wolffd@0: int aghtmlstr(char *); wolffd@0: char *agstrbind(Agraph_t * g, char *); wolffd@0: int strfree(Agraph_t *, char *); wolffd@0: char *agcanonStr(char *); wolffd@0: char *agstrcanon(char *, char *); wolffd@0: .P1 wolffd@0: .SS "GENERIC OBJECTS" wolffd@0: .P0 wolffd@0: Agraph_t *agraphof(void*); wolffd@0: Agraph_t *agroot(void*); wolffd@0: int agcontains(Agraph_t*, void*); wolffd@0: char *agnameof(void*); wolffd@0: void agdelete(Agraph_t *g, void *obj); wolffd@0: int agobjkind(void *obj); wolffd@0: Agrec_t *AGDATA(void *obj); wolffd@0: ulong AGID(void *obj); wolffd@0: int AGTYPE(void *obj); wolffd@0: .P1 wolffd@0: .SH "DESCRIPTION" wolffd@0: Libcgraph supports graph programming by maintaining graphs in memory wolffd@0: and reading and writing graph files. wolffd@0: Graphs are composed of nodes, edges, and nested subgraphs. wolffd@0: These graph objects may be attributed with string name-value pairs wolffd@0: and programmer-defined records (see Attributes). wolffd@0: .PP wolffd@0: All of Libcgraph's global symbols have the prefix \fBag\fR (case varying). wolffd@0: .SH "GRAPH AND SUBGRAPHS" wolffd@0: .PP wolffd@0: A ``main'' or ``root'' graph defines a namespace for a collection of wolffd@0: graph objects (subgraphs, nodes, edges) and their attributes. wolffd@0: Objects may be named by unique strings or by 32-bit IDs. wolffd@0: .PP wolffd@0: \fBagopen\fP creates a new graph with the given name and kind. wolffd@0: (Graph kinds are \fBAgdirected\fP, \fBAgundirected\fP, wolffd@0: \fBAgstrictdirected\fP, and \fBAgstrictundirected\fP. wolffd@0: A strict graph cannot have multi-edges or self-arcs.) wolffd@0: \fBagclose\fP deletes a graph, freeing its associated storage. wolffd@0: \fBagread\fP, \fBagwrite\fP, and \fBagconcat\fP perform file I/O wolffd@0: using the graph file language described below. \fBagread\fP wolffd@0: constructs a new graph while \fBagconcat\fP merges the file wolffd@0: contents with a pre-existing graph. Though I/O methods may wolffd@0: be overridden, the default is that the channel argument is wolffd@0: a stdio FILE pointer. \fBagsetfile\fP and \fBagreadline\fP wolffd@0: are helper functions that simply set the current file name wolffd@0: and input line number for subsequent error reporting. wolffd@0: .PP wolffd@0: \fBagsubg\fP finds or creates wolffd@0: a subgraph by name. A new subgraph is is initially empty and wolffd@0: is of the same kind as its parent. Nested subgraph trees may be created. wolffd@0: A subgraph's name is only interpreted relative to its parent. wolffd@0: A program can scan subgraphs under a given graph wolffd@0: using \fBagfstsubg\fP and \fRagnxtsubg\fP. A subgraph is wolffd@0: deleted with \fBagdelsubg\fP (or \fBagclose\fP). wolffd@0: .PP wolffd@0: By default, nodes are stored in ordered sets for efficient random wolffd@0: access to insert, find, and delete nodes. wolffd@0: The edges of a node are also stored in ordered sets. wolffd@0: The sets are maintained internally as splay tree dictionaries wolffd@0: using Phong Vo's cdt library. wolffd@0: .PP wolffd@0: \fBagnnodes\fP, \fBagnedges\fP, and \fBagdegree\fP return the wolffd@0: sizes of node and edge sets of a graph. The \fBagdegree\fP returns wolffd@0: the size of the edge set of a nodes, and takes flags wolffd@0: to select in-edges, out-edges, or both. wolffd@0: .PP wolffd@0: An \fBAgdisc_t\fP defines callbacks to be invoked by libcgraph when wolffd@0: initializing, modifying, or finalizing graph objects. (Casual users can ignore wolffd@0: the following.) Disciplines are kept on a stack. Libcgraph automatically wolffd@0: calls the methods on the stack, top-down. Callbacks are installed wolffd@0: with \fBagpushdisc\fP, uninstalled with \fBagpopdisc\fP, and wolffd@0: can be held pending or released via \fBagcallbacks\fP. wolffd@0: .PP wolffd@0: (Casual users may ignore the following. wolffd@0: When Libcgraph is compiled with Vmalloc (which is not the default), wolffd@0: each graph has its own heap. wolffd@0: Programmers may allocate application-dependent data within the wolffd@0: same heap as the rest of the graph. The advantage is that wolffd@0: a graph can be deleted by atomically freeing its entire heap wolffd@0: without scanning each individual node and edge. wolffd@0: .SH "NODES" wolffd@0: A node is created by giving a unique string name or wolffd@0: programmer defined 32-bit ID, and is represented by a wolffd@0: unique internal object. (Node equality can checked wolffd@0: by pointer comparison.) wolffd@0: .PP wolffd@0: \fBagnode\fP searches in a graph or subgraph for a node wolffd@0: with the given name, and returns it if found. wolffd@0: If not found, if \fBcreateflag\fP is boolean true wolffd@0: a new node is created and returned, otherwise a nil wolffd@0: pointer is returned. wolffd@0: \fBagidnode\fP allows a programmer to specify the node wolffd@0: by a unique 32-bit ID. wolffd@0: \fBagsubnode\fP performs a similar operation on wolffd@0: an existing node and a subgraph. wolffd@0: .Pp wolffd@0: \fBagfstnode\fP and \fBagnxtnode\fP scan node lists. wolffd@0: \fBagprvnode\fP and \fPaglstnode\fP are symmetric but scan backward. wolffd@0: The default sequence is order of creation (object timestamp.) wolffd@0: \fBagdelnode\fP removes a node from a graph or subgraph. wolffd@0: .SH "EDGES" wolffd@0: .PP wolffd@0: An abstract edge has two endpoint nodes called tail and head wolffd@0: where the all outedges of the same node have it as the tail wolffd@0: value and similarly all inedges have it as the head. wolffd@0: In an undirected graph, head and tail are interchangable. wolffd@0: If a graph has multi-edges between the same pair of nodes, wolffd@0: the edge's string name behaves as a secondary key. wolffd@0: .Pp wolffd@0: \fBagedge\fP searches in a graph of subgraph for an wolffd@0: edge between the given endpoints (with an optional wolffd@0: multi-edge selector name) and returns it if found. wolffd@0: Otherwise, if \fBcreateflag\fP is boolean true, wolffd@0: a new edge is created and returned: otherwise wolffd@0: a nil pointer is returned. If the \fBname\fP wolffd@0: is NULL, then an anonymous internal wolffd@0: value is generated. \fBagidedge\fP allows a programmer wolffd@0: to create an edge by giving its unique 32-bit ID. wolffd@0: \fBagfstin\fP, \fBagnxtint\fP, \fBagfstout\fP, and wolffd@0: \fBagnxtout\fP visit directed in- and out- edge lists, wolffd@0: and ordinarily apply only in directed graphs. wolffd@0: \fBagfstedge\fP and \fBagnxtedge\fP visit all edges wolffd@0: incident to a node. \fBagtail\fP and \fBaghead\fP wolffd@0: get the endpoint of an edge. wolffd@0: .SH "INTERNAL ATTRIBUTES" wolffd@0: Programmer-defined values may be dynamically wolffd@0: attached to graphs, subgraphs, nodes, and edges. wolffd@0: Such values are either uninterpreted binary records wolffd@0: (for implementing efficient algorithms) wolffd@0: or character string data (for I/O). wolffd@0: .SH "STRING ATTRIBUTES" wolffd@0: String attributes are handled automatically in reading wolffd@0: and writing graph files. wolffd@0: A string attribute is identified by name and by wolffd@0: an internal symbol table entry (\fBAgsym_t\fP) created by Libcgraph. wolffd@0: Attributes of nodes, edges, and graphs (with their subgraphs) wolffd@0: have separate namespaces. The contents of an \fBAgsym_t\fP wolffd@0: is listed below, followed by primitives to operate on string wolffd@0: attributes. wolffd@0: .P0 wolffd@0: typedef struct Agsym_s { /* symbol in one of the above dictionaries */ wolffd@0: Dtlink_t link; wolffd@0: char *name; /* attribute's name */ wolffd@0: char *defval; /* its default value for initialization */ wolffd@0: int id; /* its index in attr[] */ wolffd@0: unsigned char kind; /* referent object type */ wolffd@0: unsigned char fixed; /* immutable value */ wolffd@0: } Agsym_t; wolffd@0: .P1 wolffd@0: .PP wolffd@0: \fBagattr\fP creates or looks up attributes. wolffd@0: \fBkind\fP may be \fBAGRAPH\fP, \fBAGNODE\fP, or \fBAGEDGE\fP. wolffd@0: If \fBvalue\fP is \fB(char*)0)\fP, the request is to search wolffd@0: for an existing attribute of the given kind and name. wolffd@0: Otherwise, if the attribute already exists, its default wolffd@0: for creating new objects is set to the given value; wolffd@0: if it does not exist, a new attribute is created with the wolffd@0: given default, and the default is applied to all pre-existing wolffd@0: objects of the given kind. If \fBg\fP is NIL, the default is wolffd@0: set for all graphs created subsequently. wolffd@0: \fBagattrsym\fP is a helper function wolffd@0: that looks up an attribute for a graph object given as an argument. wolffd@0: \fBagnxtattr\fP permits traversing the list of attributes of wolffd@0: a given type. If \fBNIL\fP is passed as an argument it gets wolffd@0: the first attribute, otherwise it returns the next one in wolffd@0: succession or returns \fBNIL\fP at the end of the list. wolffd@0: \fBagget\fP and \fPagset\fP allow fetching and updating a wolffd@0: string attribute for an object taking the attribute name as wolffd@0: an argument. \fBagxget\fP and \fBagxset\fP do this but with wolffd@0: an attribute symbol table entry as an argument (to avoid wolffd@0: the cost of the string lookup). \fBagsafeset\fP is a wolffd@0: convenience function that ensures the given attribute is wolffd@0: declared before setting it locally on an object. wolffd@0: wolffd@0: .SH "STRINGS" wolffd@0: Libcgraph performs its own storage management of strings as wolffd@0: reference-counted strings. wolffd@0: The caller does not need to dynamically allocate storage. wolffd@0: .PP wolffd@0: \fBagstrdup\fP returns a pointer to a reference-counted copy of wolffd@0: the argument string, creating one if necessary. \fBagstrbind\fP wolffd@0: returns a pointer to a reference-counted string if it exists, or NULL if not. wolffd@0: All uses of cgraph strings need to be freed using \fBagstrfree\fP wolffd@0: in order to correctly maintain the reference count. wolffd@0: .PP wolffd@0: \fBagcanonStr\fP returns a pointer to a version of the input string wolffd@0: canonicalized for output for later re-parsing. This includes quoting wolffd@0: special characters and keywords. It uses its own internal buffer, so wolffd@0: the value will be lost on the next call to \fBagcanonStr\fP. wolffd@0: \fBagstrcanon\fP is an unsafe version of \fBagcanonStr\fP, in which wolffd@0: the application passes in a buffer as the second argument. Note that wolffd@0: the buffer may not be used; if the input string is in canonical form, wolffd@0: the function will just return a pointer to it. wolffd@0: .PP wolffd@0: The cgraph parser handles HTML-like strings. These should be wolffd@0: indistinguishable from other strings for most purposes. To create wolffd@0: an HTML-like string, use \fBagstrdup_html\fP. The \fBaghtmlstr\fP wolffd@0: function can be used to query if a string is an ordinary string or wolffd@0: an HTML-like string. wolffd@0: .SH "RECORDS" wolffd@0: Uninterpreted records may be attached to graphs, subgraphs, nodes, wolffd@0: and edges for efficient operations on values such as marks, weights, wolffd@0: counts, and pointers needed by algorithms. Application programmers wolffd@0: define the fields of these records, but they must be declared with wolffd@0: a common header as shown below. wolffd@0: .P0 wolffd@0: typedef struct Agrec_s { wolffd@0: Agrec_t header; wolffd@0: /* programmer-defined fields follow */ wolffd@0: } Agrec_t; wolffd@0: .P1 wolffd@0: Records are created and managed by Libcgraph. A programmer must wolffd@0: explicitly attach them to the objects in a graph, either to wolffd@0: individual objects one at a time via \fBagbindrec\fP, or to wolffd@0: all the objects of the same class in a graph via \fBaginit\fP. wolffd@0: The \fBname\fP argument a record distinguishes various types of records, wolffd@0: and is programmer defined (Libcgraph reserves the prefix \fB_ag\fR). wolffd@0: If size is 0, the call to \fBagbindrec\fP is simply a lookup. wolffd@0: \fBagdelrec\fP is the deletes records one at a time. wolffd@0: \fBagclean\fP does the same for all objects of the same wolffd@0: class in an entire graph. wolffd@0: wolffd@0: Internally, records are maintained in circular linked lists wolffd@0: attached to graph objects. wolffd@0: To allow referencing application-dependent data without function wolffd@0: calls or search, Libcgraph allows setting and locking the list wolffd@0: pointer of a graph, node, or edge on a particular record. wolffd@0: This pointer can be obtained with the macro \fBAGDATA(obj)\fP. wolffd@0: A cast, generally within a macro or inline function, wolffd@0: is usually applied to convert the list pointer to wolffd@0: an appropriate programmer-defined type. wolffd@0: wolffd@0: To control the setting of this pointer, wolffd@0: the \fBmove_to_front\fP flag may be \fBAG_MTF_FALSE\fP, wolffd@0: \fBAG_MTF_SOFT\fP, or \fBAG_MTF_HARD\fP accordingly. wolffd@0: The \fBAG_MTF_SOFT\fP field is only a hint that decreases wolffd@0: overhead in subsequent calls of \fBaggetrec\fP; wolffd@0: \fBAG_MTF_HARD\fP guarantees that a lock was obtained. wolffd@0: To release locks, use \fBAG_MTF_SOFT\fP or \fBAG_MTF_FALSE\fP. wolffd@0: Use of this feature implies cooperation or at least isolation wolffd@0: from other functions also using the move-to-front convention. wolffd@0: wolffd@0: .SH "DISCIPLINES" wolffd@0: (The following is not intended for casual users.) wolffd@0: Programmer-defined disciplines customize certain resources- wolffd@0: ID namespace, memory, and I/O - needed by Libcgraph. wolffd@0: A discipline struct (or NIL) is passed at graph creation time. wolffd@0: .P0 wolffd@0: struct Agdisc_s { /* user's discipline */ wolffd@0: Agmemdisc_t *mem; wolffd@0: Agiddisc_t *id; wolffd@0: Agiodisc_t *io; wolffd@0: } ; wolffd@0: .P1 wolffd@0: A default discipline is supplied when NIL is given for wolffd@0: any of these fields. wolffd@0: wolffd@0: An ID allocator discipline allows a client to control assignment wolffd@0: of IDs (uninterpreted 32-bit values) to objects, and possibly how wolffd@0: they are mapped to and from strings. wolffd@0: wolffd@0: .P0 wolffd@0: struct Agiddisc_s { /* object ID allocator */ wolffd@0: void *(*open)(Agraph_t *g); /* associated with a graph */ wolffd@0: int (*map)(void *state, int objtype, char *str, ulong *id, int createflag); wolffd@0: int (*alloc)(void *state, int objtype, ulong id); wolffd@0: void (*free)(void *state, int objtype, ulong id); wolffd@0: char *(*print)(void *state, int objtype, ulong id); wolffd@0: void (*close)(void *state); wolffd@0: } ; wolffd@0: .P1 wolffd@0: wolffd@0: \f5open\fP permits the ID discipline to initialize any data wolffd@0: structures that maintains per individual graph. wolffd@0: Its return value is then passed as the first argument to wolffd@0: all subsequent ID manager calls. wolffd@0: wolffd@0: \f5alloc\fP informs the ID manager that Libcgraph is attempting wolffd@0: to create an object with a specific ID that was given by a client. wolffd@0: The ID manager should return TRUE (nonzero) if the ID can be wolffd@0: allocated, or FALSE (which aborts the operation). wolffd@0: wolffd@0: \f5free\fP is called to inform the ID manager that the wolffd@0: object labeled with the given ID is about to go out of existence. wolffd@0: wolffd@0: \f5map\fP is called to create or look-up IDs by string name wolffd@0: (if supported by the ID manager). Returning TRUE (nonzero) wolffd@0: in all cases means that the request succeeded (with a valid wolffd@0: ID stored through \f5result\fP. There are four cases: wolffd@0: .PP wolffd@0: \f5name != NULL\fP and \f5createflag == 1\fP: wolffd@0: This requests mapping a string (e.g. a name in a graph file) into a new ID. wolffd@0: If the ID manager can comply, then it stores the result and returns TRUE. wolffd@0: It is then also responsible for being able to \f5print\fP the ID again wolffd@0: as a string. Otherwise the ID manager may return FALSE but it must wolffd@0: implement the following (at least for graph file reading and writing to work): wolffd@0: .PP wolffd@0: \f5name == NULL\fP and \f5createflag == 1\fP: wolffd@0: The ID manager creates a unique new ID of its own choosing. wolffd@0: Although it may return FALSE if it does not support anonymous objects, wolffd@0: but this is strongly discouraged (to support "local names" in graph files.) wolffd@0: .PP wolffd@0: \f5name != NULL\fP and \f5createflag == 0\fP: wolffd@0: This is a namespace probe. If the name was previously mapped into wolffd@0: an allocated ID by the ID manager, then the manager must return this ID. wolffd@0: Otherwise, the ID manager may either return FALSE, or may store wolffd@0: any unallocated ID into result. (This is convenient, for example, wolffd@0: if names are known to be digit strings that are directly converted into 32 bit values.) wolffd@0: .PP wolffd@0: \f5name == NULL\fP and \f5createflag == 0\fP: forbidden. wolffd@0: .PP wolffd@0: \f5print\fP is allowed to return a pointer to a static buffer; wolffd@0: a caller must copy its value if needed past subsequent calls. wolffd@0: \f5NULL\fP should be returned by ID managers that do not map names. wolffd@0: .PP wolffd@0: The \f5map\fP and \f5alloc\fP calls do not pass a pointer to the wolffd@0: newly allocated object. If a client needs to install object wolffd@0: pointers in a handle table, it can obtain them via wolffd@0: new object callbacks. wolffd@0: .P0 wolffd@0: struct Agiodisc_s { wolffd@0: int (*fread)(void *chan, char *buf, int bufsize); wolffd@0: int (*putstr)(void *chan, char *str); wolffd@0: int (*flush)(void *chan); /* sync */ wolffd@0: /* error messages? */ wolffd@0: } ; wolffd@0: wolffd@0: struct Agmemdisc_s { /* memory allocator */ wolffd@0: void *(*open)(void); /* independent of other resources */ wolffd@0: void *(*alloc)(void *state, size_t req); wolffd@0: void *(*resize)(void *state, void *ptr, size_t old, size_t req); wolffd@0: void (*free)(void *state, void *ptr); wolffd@0: void (*close)(void *state); wolffd@0: } ; wolffd@0: .P1 wolffd@0: wolffd@0: .SH "EXAMPLE PROGRAM" wolffd@0: .P0 wolffd@0: #include wolffd@0: typedef struct mydata_s {Agrec_t hdr; int x,y,z;} mydata; wolffd@0: wolffd@0: main(int argc, char **argv) wolffd@0: { wolffd@0: Agraph_t *g; wolffd@0: Agnode_t *v; wolffd@0: Agedge_t *e; wolffd@0: Agsym_t *attr; wolffd@0: Dict_t *d wolffd@0: int cnt; wolffd@0: mydata *p; wolffd@0: wolffd@0: if (g = agread(stdin,NIL(Agdisc_t*))) { wolffd@0: cnt = 0; attr = 0; wolffd@0: while (attr = agnxtattr(g, AGNODE, attr)) cnt++; wolffd@0: printf("The graph %s has %d attributes\n",agnameof(g),cnt); wolffd@0: wolffd@0: /* make the graph have a node color attribute, default is blue */ wolffd@0: attr = agattr(g,AGNODE,"color","blue"); wolffd@0: wolffd@0: /* create a new graph of the same kind as g */ wolffd@0: h = agopen("tmp",g->desc); wolffd@0: wolffd@0: /* this is a way of counting all the edges of the graph */ wolffd@0: cnt = 0; wolffd@0: for (v = agfstnode(g); v; v = agnxtnode(g,v)) wolffd@0: for (e = agfstout(g,v); e; e = agnxtout(g,e)) wolffd@0: cnt++; wolffd@0: wolffd@0: /* attach records to edges */ wolffd@0: for (v = agfstnode(g); v; v = agnxtnode(g,v)) wolffd@0: for (e = agfstout(g,v); e; e; = agnxtout(g,e)) { wolffd@0: p = (mydata*) agbindrec(g,e,"mydata",sizeof(mydata),TRUE); wolffd@0: p->x = 27; /* meaningless data access example */ wolffd@0: ((mydata*)(AGDATA(e)))->y = 999; /* another example */ wolffd@0: } wolffd@0: } wolffd@0: } wolffd@0: .P1 wolffd@0: .SH "EXAMPLE GRAPH FILES" wolffd@0: .P0 wolffd@0: digraph G { wolffd@0: a -> b; wolffd@0: c [shape=box]; wolffd@0: a -> c [weight=29,label="some text]; wolffd@0: subgraph anything { wolffd@0: /* the following affects only x,y,z */ wolffd@0: node [shape=circle]; wolffd@0: a; x; y -> z; y -> z; /* multiple edges */ wolffd@0: } wolffd@0: } wolffd@0: wolffd@0: strict graph H { wolffd@0: n0 -- n1 -- n2 -- n0; /* a cycle */ wolffd@0: n0 -- {a b c d}; /* a star */ wolffd@0: n0 -- n3; wolffd@0: n0 -- n3 [weight=1]; /* same edge because graph is strict */ wolffd@0: } wolffd@0: .P1 wolffd@0: .SH "SEE ALSO" wolffd@0: Libcdt(3) wolffd@0: wolffd@0: .SH "BUGS" wolffd@0: It is difficult to change endpoints of edges, delete string attributes or wolffd@0: modify edge keys. The work-around is to create a new object and copy the wolffd@0: contents of an old one (but new object obviously has a different ID, wolffd@0: internal address, and object creation timestamp). wolffd@0: wolffd@0: The API lacks convenient functions to substitute programmer-defined ordering of wolffd@0: nodes and edges but in principle this can be supported. wolffd@0: .SH "AUTHOR" wolffd@0: Stephen North, north@research.att.com, AT&T Research.