#include <stdio.h>
#include <cghdr.h>
-agerrlevel_t agerrno; /* Last error */
+#define MAX(a,b) ((a)>(b)?(a):(b))
+static agerrlevel_t agerrno; /* Last error level */
static agerrlevel_t agerrlevel = AGWARN; /* Report errors >= agerrlevel */
static int agerrcnt;
static long aglast; /* Last message */
static FILE *agerrout; /* Message file */
+static agusererrf usererrf; /* User-set error function */
-void agseterr(agerrlevel_t lvl)
+agusererrf
+agseterrf (agusererrf newf)
{
+ agusererrf oldf = usererrf;
+ usererrf = newf;
+ return oldf;
+}
+
+agerrlevel_t agseterr(agerrlevel_t lvl)
+{
+ agerrlevel_t oldv = agerrlevel;
agerrlevel = lvl;
+ return oldv;
}
char *aglasterr()
return buf;
}
+/* userout:
+ * Report messages using a user-supplied write function
+ */
+static void
+userout (agerrlevel_t level, const char *fmt, va_list args)
+{
+ static char* buf;
+ static int bufsz = 1024;
+ char* np;
+ int n;
+
+ if (!buf) {
+ buf = (char*)malloc(bufsz);
+ if (!buf) {
+ fputs("userout: could not allocate memory\n", stderr );
+ return;
+ }
+ }
+
+ if (level != AGPREV) {
+ usererrf ((level == AGERR) ? "Error" : "Warning");
+ usererrf (": ");
+ }
+
+ while (1) {
+ n = vsnprintf(buf, bufsz, fmt, args);
+ if ((n > -1) && (n < bufsz)) {
+ usererrf (buf);
+ break;
+ }
+ bufsz = MAX(bufsz*2,n+1);
+ if ((np = (char*)realloc(buf, bufsz)) == NULL) {
+ fputs("userout: could not allocate memory\n", stderr );
+ return;
+ }
+ }
+ va_end(args);
+}
+
static int agerr_va(agerrlevel_t level, char *fmt, va_list args)
{
agerrlevel_t lvl;
+ /* Use previous error level if continuation message;
+ * Convert AGMAX to AGERROR;
+ * else use input level
+ */
lvl = (level == AGPREV ? agerrno : (level == AGMAX) ? AGERR : level);
- agerrcnt++;
+ agerrcnt++;
+ /* store this error level */
agerrno = lvl;
+
+ /* We report all messages whose level is bigger than the user set agerrlevel
+ * Setting agerrlevel to AGMAX turns off immediate error reporting.
+ */
if (lvl >= agerrlevel) {
- if (level != AGPREV)
- fprintf(stderr, "%s: ",
- (level == AGERR) ? "Error" : "Warning");
- vfprintf(stderr, fmt, args);
- va_end(args);
+ if (usererrf)
+ userout (level, fmt, args);
+ else {
+ if (level != AGPREV)
+ fprintf(stderr, "%s: ", (level == AGERR) ? "Error" : "Warning");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
return 0;
}
ulong AGID(void *obj);
int AGTYPE(void *obj);
.P1
+.SS "ERROR REPORTING"
+.P0
+typedef enum { AGWARN, AGERR, AGMAX, AGPREV } agerrlevel_t;
+typedef int (*agusererrf) (char*);
+agerrlevel_t agerrno;
+agerrlevel_t agseterr(agerrlevel_t);
+char *aglasterr(void);
+int agerr(agerrlevel_t level, char *fmt, ...);
+void agerrorf(char *fmt, ...);
+void agwarningf(char *fmt, ...);
+int agerrors(void);
+agusererrf agseterrf(agusererrf);
+.P1
.SH "DESCRIPTION"
Libcgraph supports graph programming by maintaining graphs in memory
and reading and writing graph files.
.PP
A ``main'' or ``root'' graph defines a namespace for a collection of
graph objects (subgraphs, nodes, edges) and their attributes.
-Objects may be named by unique strings or by 32-bit IDs.
+Objects may be named by unique strings or by integer IDs.
.PP
\fBagopen\fP creates a new graph with the given name and kind.
(Graph kinds are \fBAgdirected\fP, \fBAgundirected\fP,
without scanning each individual node and edge.
.SH "NODES"
A node is created by giving a unique string name or
-programmer defined 32-bit ID, and is represented by a
+programmer defined integer ID, and is represented by a
unique internal object. (Node equality can checked
by pointer comparison.)
.PP
a new node is created and returned, otherwise a nil
pointer is returned.
\fBagidnode\fP allows a programmer to specify the node
-by a unique 32-bit ID.
+by a unique integer ID.
\fBagsubnode\fP performs a similar operation on
an existing node and a subgraph.
.PP
a nil pointer is returned. If the \fBname\fP
is NULL, then an anonymous internal
value is generated. \fBagidedge\fP allows a programmer
-to create an edge by giving its unique 32-bit ID.
+to create an edge by giving its unique integer ID.
\fBagfstin\fP, \fBagnxtint\fP, \fBagfstout\fP, and
\fBagnxtout\fP visit directed in- and out- edge lists,
and ordinarily apply only in directed graphs.
counts, and pointers needed by algorithms. Application programmers
define the fields of these records, but they must be declared with
a common header as shown below.
+.PP
.P0
typedef struct Agrec_s {
Agrec_t header;
/* programmer-defined fields follow */
} Agrec_t;
.P1
+.PP
Records are created and managed by Libcgraph. A programmer must
explicitly attach them to the objects in a graph, either to
individual objects one at a time via \fBagbindrec\fP, or to
Programmer-defined disciplines customize certain resources-
ID namespace, memory, and I/O - needed by Libcgraph.
A discipline struct (or NIL) is passed at graph creation time.
+.PP
.P0
struct Agdisc_s { /* user's discipline */
Agmemdisc_t *mem;
Agiodisc_t *io;
} ;
.P1
+.PP
A default discipline is supplied when NIL is given for
any of these fields.
An ID allocator discipline allows a client to control assignment
-of IDs (uninterpreted 32-bit values) to objects, and possibly how
+of IDs (uninterpreted integer values) to objects, and possibly how
they are mapped to and from strings.
.P0
void (*close)(void *state);
} ;
.P1
-
-\f5open\fP permits the ID discipline to initialize any data
+.PP
+\fIopen\fP permits the ID discipline to initialize any data
structures that maintains per individual graph.
Its return value is then passed as the first argument to
all subsequent ID manager calls.
-
-\f5alloc\fP informs the ID manager that Libcgraph is attempting
+.PP
+\fIalloc\fP informs the ID manager that Libcgraph is attempting
to create an object with a specific ID that was given by a client.
The ID manager should return TRUE (nonzero) if the ID can be
allocated, or FALSE (which aborts the operation).
-
-\f5free\fP is called to inform the ID manager that the
+.PP
+\fIfree\fP is called to inform the ID manager that the
object labeled with the given ID is about to go out of existence.
-
-\f5map\fP is called to create or look-up IDs by string name
+.PP
+\fImap\fP is called to create or look-up IDs by string name
(if supported by the ID manager). Returning TRUE (nonzero)
in all cases means that the request succeeded (with a valid
ID stored through \f5result\fP. There are four cases:
.PP
-\f5name != NULL\fP and \f5createflag == 1\fP:
+\fIname != NULL\fP and \f5createflag == 1\fP:
This requests mapping a string (e.g. a name in a graph file) into a new ID.
If the ID manager can comply, then it stores the result and returns TRUE.
It is then also responsible for being able to \f5print\fP the ID again
an allocated ID by the ID manager, then the manager must return this ID.
Otherwise, the ID manager may either return FALSE, or may store
any unallocated ID into result. (This is convenient, for example,
-if names are known to be digit strings that are directly converted into 32 bit values.)
+if names are known to be digit strings that are directly converted into integer values.)
.PP
\f5name == NULL\fP and \f5createflag == 0\fP: forbidden.
.PP
} ;
.P1
+.P0
+.SH "GENERIC OBJECTS"
+\fBagroot\fP takes any graph object (graph, subgraph, node, edge) and returns
+the root graph in which it lives. \fBagraphof\fP does the same, except it
+is the identity function on graphs and subgraphs. Note that there is no
+function to return the least subgraph containing an object, in part because
+this is not well-defined as nodes and edges may be in incomparable subgraphs.
+.PP
+\fBagcontains(\fIg\fP,\fIobj\fP)\fP returns non-zero if \fIobj\fP is a member
+of (sub)graph \fIg\fP. \fBagdelete(\fIg\fP,\fIobj\fP)\fP is equivalent
+to \fBagclose\fP, \fBagdelnode\fP, and \fBagclose\fP for \fIobj\fP being a
+graph, node or edge, respectively. It returns -1 if \fIobj\fP does not
+belong to \fIg\fP.
+.PP
+\fBagnameof\fP returns a string descriptor for the object. It returns the name
+of the node or graph, and the key of an edge.
+\fBagobjkind\fP is a synonym for \fBAGTYPE\fP.
+.PP
+\fBAGDATA\fP, \fBAGID\fP, and \fBAGTYPE\fP are macros returning the specified
+fields of the argument object. The first is described in the \fBRECORDS\fP
+section above. The second returns the unique integer ID associated with
+the object. The last returns \fBAGRAPH\fP, \fBAGNODE\fP, and \fBAGEDGE\fP
+depending on the type of the object.
+
+typedef int (*agusererrf) (char*);
+agusererrf agseterrf(agusererrf);
+.SH "ERROR REPORTING"
+The library provides a variety of mechanisms to control the reporting
+of errors and warnings. At present, there are basically two types of
+messages: warnings and errors. A message is only written if its
+type has higher priority than a programmer-controlled minimum, which is
+\fBAGWARN\fP by default. The programmer can set this value using
+\fBagseterr\fP, which returns the previous value. Calling
+\fBagseterr(AGMAX)\fP turns off the writing of messages.
+.PP
+The function \fBagerr\fP if the main entry point for reporting an
+anomaly. The first argument indicates the type of message. Usually,
+the first argument in \fBAGWARN\fP or \fBAGERR\fP to indicate warnings
+and errors, respectively. Sometimes additional context information is
+only available in functions calling the function where the error is
+actually caught. In this case, the calling function can indicate that
+it is continuing the current error by using \fBAGPREV\fP as the first
+argument. The remaining arguments to \fBagerr\fP are the same as
+the arguments to \fBprintf\fP.
+.PP
+The functions \fBagwarningf\fP and \fBagerrorf\fP are shorthand for
+\fBagerr(AGERR,...)\fP and \fBagerr(AGWARN,...)\fP, respectively.
+.PP
+Some applications desire to directly control the writing of messages.
+Such an application can use the function \fBagseterrf\fP to register
+the function that the library should call to actually write the message.
+The previous error function is returned. By default, the message is
+written to \fBstderr\fP.
+.PP
+Errors not written are stored in a log file. The last recorded error
+can be retreived by calling \fBaglasterr\fP.
+.PP
+The function \fBagerrors\fP returns non-zero if errors have been reported.
.SH "EXAMPLE PROGRAM"
.P0
#include <graphviz/cgraph.h>
#include <stdarg.h>
+#include <stdlib.h>
#include "libgraph.h"
#include "parser.h"
#include "triefa.cP"
static agerrlevel_t agerrlevel = AGWARN; /* Report errors >= agerrlevel */
static long aglast; /* Last message */
static FILE *agerrout; /* Message file */
+static agusererrf usererrf; /* User-set error function */
+
+agusererrf
+agseterrf (agusererrf newf)
+{
+ agusererrf oldf = usererrf;
+ usererrf = newf;
+ return oldf;
+}
void agseterr(agerrlevel_t lvl)
{
return buf;
}
+static void
+userout (agerrlevel_t level, const char *fmt, va_list args)
+{
+ static char* buf;
+ static int bufsz = 1024;
+ char* np;
+ int n;
+
+ if (!buf) {
+ buf = (char*)malloc(bufsz);
+ if (!buf) {
+ fputs("userout: could not allocate memory\n", stderr );
+ return;
+ }
+ }
+
+ if (level != AGPREV) {
+ usererrf ((level == AGERR) ? "Error" : "Warning");
+ usererrf (": ");
+ }
+
+ while (1) {
+ n = vsnprintf(buf, bufsz, fmt, args);
+ if ((n > -1) && (n < bufsz)) {
+ usererrf (buf);
+ break;
+ }
+ bufsz = MAX(bufsz*2,n+1);
+ if ((np = (char*)realloc(buf, bufsz)) == NULL) {
+ fputs("userout: could not allocate memory\n", stderr );
+ return;
+ }
+ }
+ va_end(args);
+}
+
+/* agerr_va:
+ * Main error reporting function
+ */
static int agerr_va(agerrlevel_t level, const char *fmt, va_list args)
{
agerrlevel_t lvl;
+ /* Use previous error level if continuation message;
+ * Convert AGMAX to AGERROR;
+ * else use input level
+ */
lvl = (level == AGPREV ? agerrno : (level == AGMAX) ? AGERR : level);
+ /* store this error level and maximum error level used */
agerrno = lvl;
agmaxerr = MAX(agmaxerr, agerrno);
+
+ /* We report all messages whose level is bigger than the user set agerrlevel
+ * Setting agerrlevel to AGMAX turns off immediate error reporting.
+ */
if (lvl >= agerrlevel) {
- if (level != AGPREV)
- fprintf(stderr, "%s: ",
- (level == AGERR) ? "Error" : "Warning");
- vfprintf(stderr, fmt, args);
- va_end(args);
+ if (usererrf)
+ userout (level, fmt, args);
+ else {
+ if (level != AGPREV)
+ fprintf(stderr, "%s: ", (level == AGERR) ? "Error" : "Warning");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
return 0;
}
+ /* If error is not immediately reported, store in log file */
if (!agerrout) {
agerrout = tmpfile();
if (!agerrout)
return 0;
}
+/* agerr:
+ * Varargs function for reporting errors with level argument
+ */
int agerr(agerrlevel_t level, char *fmt, ...)
{
va_list args;
return agerr_va(level, fmt, args);
}
+/* agerrorf:
+ * Varargs function for reporting errors
+ */
void agerrorf(const char *fmt, ...)
{
va_list args;
agerr_va(AGERR, fmt, args);
}
+/* agwarningf:
+ * Varargs function for reporting warnings
+ */
void agwarningf(char *fmt, ...)
{
va_list args;