From: erg Date: Tue, 22 Jul 2008 15:26:00 +0000 (+0000) Subject: Add new commands for testing if attributes exists. Needed because X-Git-Tag: LAST_LIBGRAPH~32^2~3830 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f52b59a641e8793bd9778c898fe6a65d59477314;p=graphviz Add new commands for testing if attributes exists. Needed because libexpr does not allow NULL for strings. --- diff --git a/cmd/gvpr/compile.c b/cmd/gvpr/compile.c index 31b794e5d..e0815df25 100644 --- a/cmd/gvpr/compile.c +++ b/cmd/gvpr/compile.c @@ -306,6 +306,36 @@ setattr (Agobj_t *objp, char* name, char* val) return agxset(objp, gsym, val); } +/* kindToStr: + */ +static char* +kindToStr (int kind) +{ + char* s; + + switch (kind) { + case AGRAPH : + s = "graph"; + break; + case AGNODE : + s = "node"; + break; + default : + s = "edge"; + break; + } + return s; +} + +/* kindOf: + * Return string rep of object's kind + */ +static char* +kindOf (Agobj_t* objp) +{ + return (kindToStr (agobjkind (objp))); +} + /* lookup: * Apply symbol to get field value of objp * Assume objp != NULL @@ -418,8 +448,14 @@ static int lookup(Expr_t * pgm, Agobj_t * objp, Exid_t * sym, Extype_t * v, return -1; break; } - } else - v->string = agget(objp, sym->name); + } else { + Agsym_t *gsym = agattrsym(objp, sym->name); + if (!gsym) { + gsym = agattr(agroot(agraphof(objp)), AGTYPE(objp), sym->name, ""); + error(ERROR_WARNING, "Using value of uninitialized attribute \"%s\" of %s \"%s\"", sym->name, kindOf (objp), agnameof(objp)); + } + v->string = agxget(objp, gsym); + } return 0; } @@ -461,13 +497,13 @@ setDfltAttr (Agraph_t *gp, char* k, char* name, char* value) return 0; } -/* getDfltAttr: +/* toKind: + * Map string to object kind */ -static char* -getDfltAttr (Agraph_t *gp, char* k, char* name) +static int +toKind (char* k) { int kind; - Agsym_t* sym; switch (*k) { case 'G' : @@ -480,13 +516,24 @@ getDfltAttr (Agraph_t *gp, char* k, char* name) kind = AGNODE; break; default : - error(ERROR_WARNING, "Unknown kind \"%s\" passed to getDflt()", k); - return 0; + error(ERROR_FATAL, "Unknown kind \"%s\" passed to getDflt()", k); break; } - sym = agattr (gp, kind, name, 0); - if (sym) return sym->defval; - else return 0; + return kind; +} + +/* getDfltAttr: + */ +static char* +getDfltAttr (Agraph_t *gp, char* k, char* name) +{ + int kind = toKind (k); + Agsym_t* sym = agattr (gp, kind, name, 0); + if (!sym) { + sym = agattr(gp, kind, name, ""); + error(ERROR_WARNING, "Uninitialized %s attribute \"%s\" in getDflt", kindToStr (kind), name); + } + return sym->defval; } /* getval: @@ -715,8 +762,7 @@ getval(Expr_t * pgm, Exnode_t * node, Exid_t * sym, Exref_t * ref, case F_kindof: objp = INT2PTR(Agobj_t *, args[0].integer); if (!objp) { - error(ERROR_WARNING, "NULL object passed to kindOf()"); - v.string = 0; + error(ERROR_FATAL, "NULL object passed to kindOf()"); } else switch (AGTYPE(objp)) { case AGRAPH : v.string = "G"; @@ -1186,18 +1232,26 @@ getval(Expr_t * pgm, Exnode_t * node, Exid_t * sym, Exref_t * ref, case F_log: v.floating = log(args[0].floating); break; + case F_hasattr: case F_get: objp = INT2PTR(Agobj_t *, args[0].integer); + char* name = args[1].string; if (!objp) { - error(ERROR_WARNING, "NULL object passed to aget()"); - v.string = 0; - } else { - char* name = args[1].string; - if (name) v.string = agget(objp, name); + error(ERROR_FATAL, "NULL object passed to aget()/hasAttr()"); + } else if (!name) { + error(ERROR_FATAL, "NULL name passed to aget()/hasAttr()"); + } + else { + Agsym_t *gsym = agattrsym(objp, name); + if (sym->index == F_hasattr) + v.integer = (gsym != NULL); else { - error(ERROR_WARNING, "NULL name passed to aget()"); - v.string = 0; - } + if (!gsym) { + gsym = agattr(agroot(agraphof(objp)), AGTYPE(objp), name, ""); + error(ERROR_WARNING, "Using value of uninitialized attribute \"%s\" of %s \"%s\" in aget()", name, kindOf (objp), agnameof(objp)); + } + v.string = agxget(objp, gsym); + } } break; case F_set: @@ -1247,25 +1301,26 @@ getval(Expr_t * pgm, Exnode_t * node, Exid_t * sym, Exref_t * ref, v.integer = 0; } break; + case F_isattr: case F_dget: gp = INT2PTR(Agraph_t *, args[0].integer); if (gp) { char* kind = args[1].string; char* name = args[2].string; if (!name) { - error(ERROR_WARNING, "NULL name passed to getDflt()"); - v.string = 0; + error(ERROR_FATAL,"NULL name passed to getDflt()/isAttr()"); } else if (!kind) { - error(ERROR_WARNING, "NULL kind passed to getDflt()"); - v.string = 0; + error(ERROR_FATAL,"NULL kind passed to getDflt()/isAttr()"); } + else if (sym->index == F_isattr) { + v.integer = (agattr(gp, toKind (kind), name, 0) != NULL); + } else { v.string = getDfltAttr(gp, kind, name); } } else { - error(ERROR_WARNING, "NULL graph passed to node()"); - v.string = 0; + error(ERROR_FATAL, "NULL graph passed to getDflt()/isAttr()"); } break; case F_canon: diff --git a/cmd/gvpr/gprdata b/cmd/gvpr/gprdata index b84d26e5d..1cf7c5f53 100644 --- a/cmd/gvpr/gprdata +++ b/cmd/gvpr/gprdata @@ -98,6 +98,8 @@ F_get "aget" FUNCTION S|A(1,O)|A(2,S) F_set "aset" FUNCTION I|A(1,O)|A(2,S)|A(3,S) F_dget "getDflt" FUNCTION S|A(1,G)|A(2,S)|A(3,S) F_dset "setDflt" FUNCTION I|A(1,G)|A(2,S)|A(3,S)|A(4,S) +F_hasattr "hasAttr" FUNCTION I|A(1,O)|A(2,S) +F_isattr "isAttr" FUNCTION I|A(1,G)|A(2,S)|A(3,S) C_flat "TV_flat" CONSTANT T_tvtyp C_dfs "TV_dfs" CONSTANT T_tvtyp C_bfs "TV_bfs" CONSTANT T_tvtyp diff --git a/cmd/gvpr/gvpr.1 b/cmd/gvpr/gvpr.1 index ada8f74a7..ff68143d0 100644 --- a/cmd/gvpr/gvpr.1 +++ b/cmd/gvpr/gvpr.1 @@ -500,11 +500,23 @@ any attribute values \fItgt\fP may initially have. \fBinduce\fP(\fIg\fP : \fBgraph_t\fP) : \fBvoid\fP extends \fIg\fP to its node\(hyinduced subgraph extension in its root graph. .TP +\fBhasAttr\fP(\fIsrc\fP : \fBobj_t\fP, \fIname\fP : \fBstring\fP) : \fBint\fP +returns non-zero if object \fIsrc\fP has an attribute whose name is +\fIname\fP. It returns 0 otherwise. +.TP +\fBisAttr\fP(\fIg\fP : \fBgraph_t\fP, \fIkind\fP : \fBstring\fP, \fIname\fP : \fBstring\fP) : \fBint\fP +returns non-zero if an attribute \fIname\fP has been defined in \fIg\fP +for objects of the given \fIkind\fP. For nodes, edges, and graphs, \fIkind\fP +should be "N", "E", and "G", respectively. +It returns 0 otherwise. +.TP \fBaget\fP(\fIsrc\fP : \fBobj_t\fP, \fIname\fP : \fBstring\fP) : \fBstring\fP returns the value of attribute \fIname\fP in object \fIsrc\fP. This is useful for those cases when \fIname\fP conflicts with one of the keywords such as "head" or "root". -Returns \fBNULL\fP on failure or if the attribute is not defined. +If the attribute has not been declared in the graph, the function will +initialize it with a default value of "". To avoid this, one should use +the \fBhasAttr\fP or \fBisAttr\fP function to check that the attribute exists. .TP \fBaset\fP(\fIsrc\fP : \fBobj_t\fP, \fIname\fP : \fBstring\fP, \fIvalue\fP : \fBstring\fP) : \fBint\fP sets the value of attribute \fIname\fP in object \fIsrc\fP to \fIvalue\fP. @@ -514,14 +526,16 @@ Returns 0 on success, non\(hyzero on failure. See \fBaget\fP above. returns the default value of attribute \fIname\fP in objects in \fIg\fP of the given \fIkind\fP. For nodes, edges, and graphs, \fIkind\fP should be "N", "E", and "G", respectively. -Returns \fBNULL\fP on failure or if the attribute is not defined. +If the attribute has not been declared in the graph, the function will +initialize it with a default value of "". To avoid this, one should use +the \fBisAttr\fP function to check that the attribute exists. .TP \fBsetDflt\fP(\fIg\fP : \fBgraph_t\fP, \fIkind\fP : \fBstring\fP, \fIname\fP : \fBstring\fP, \fIvalue\fP : \fBstring\fP) : \fBint\fP sets the default value of attribute \fIname\fP to \fIvalue\fP in objects in \fIg\fP of the given \fIkind\fP. For nodes, edges, and graphs, \fIkind\fP should be "N", "E", and "G", respectively. -Returns 0 on success, non\(hyzero on failure. See \fBsetDflt\fP above. +Returns 0 on success, non\(hyzero on failure. See \fBgetDflt\fP above. .TP \fBcompOf\fP(\fIg\fP : \fBgraph_t\fP, \fIn\fP : \fBnode_t\fP) : \fBgraph_t\fP returns the connected component of the graph \fIg\fP containing node \fIn\fP, @@ -769,7 +783,7 @@ The default value is \fBNULL\fP for each input graph. \fB$tvtype\fP : \fBtvtype_t\fP indicates how \fBgvpr\fP traverses a graph. At present, it can only take one of six values: \fBTV_flat\fP, \fBTV_dfs\fP, \fBTV_fwd\fP, -\fBTV_ref\fP, \fBTV_bfs\fP, \fBTV_ne\fP, and \fBTV_en\fP. +\fBTV_rev\fP, \fBTV_bfs\fP, \fBTV_ne\fP, and \fBTV_en\fP. \fBTV_flat\fP is the default. The meaning of these values is discussed below. .TP