/*********************************************************************** * Copyright (C) 1995 Joe English * Freely redistributable *********************************************************************** * * esis.c,v 1.25 1999/07/25 20:49:37 joe Exp * * Author: Joe English * Created: 1 Mar 1995 * Description: ESIS data retrieval functions */ #include #include #include #include "project.h" #include "strmap.h" #include "strmgt.h" #include "pile.h" #include "esis.h" #include "esisp.h" /*+++ * Node inquiry routines: */ ESISNode esis_rootnode(ESISDocument doc) { return doc->rootnode; } void esis_set_docname(ESISDocument doc, const char *name) { doc->rootnode->name = intern(name); } ESISToken esis_docname(ESISDocument doc) { return doc->rootnode->name; } ESISNodeType esis_nodetype(ESISNode nd) { return nd->type; } ESISToken esis_gi(ESISNode nd) { if (nd->type == EN_EL) return nd->name; else return 0; } ESISToken esis_ename(ESISNode nd) { if (nd->type == EN_REFERENCE || nd->type == EN_ENTITY) return nd->name; else return 0; } ESISToken esis_attname(ESISNode nd) { return (nd->type == EN_AT) ? nd->name : 0; } static ESISString entprop(ESISNode nd, const char *propname) { if (nd->type == EN_REFERENCE) nd = nd->reference; if (!nd) return 0; return esis_getprop(nd, propname); } ESISString esis_sysid(ESISNode nd) { return entprop(nd, ENTPROP_SYSID); } ESISString esis_pubid(ESISNode nd) { return entprop(nd, ENTPROP_PUBID); } ESISString esis_dcn(ESISNode nd) { return entprop(nd, ENTPROP_NOTATION); } /* * Application properties: */ int esis_hasprop(ESISNode nd, const char *name) { return nd->properties && (strmap_get(nd->properties, name) != 0); } ESISString esis_getprop(ESISNode nd, const char *name) { return nd->properties ? strmap_get(nd->properties, name) : 0; } void esis_setprop(ESISNode nd, const char *name, const char *val) { if (!nd->properties) nd->properties = strmap_create(); strmap_set(nd->properties, name, val); } void esis_unsetprop(ESISNode nd, const char *name) { if (nd->properties) strmap_unset(nd->properties, name); } /* * path locators: */ int esis_depth(ESISNode nd) { return nd ? 1 + esis_depth(nd->parent) : 0; } int esis_seqno(ESISNode nd) { return nd ? 1 + esis_seqno(nd->prev) : 0; } /* * Pathloc stuff: * %%% Describe this. * See "Stupid Pathloc Tricks" for details... */ int esis_docpos(ESISNode nd, ESISPathlocAddr *docpos) { if (nd->pathno == -1) return 0; docpos->marklist[0] = nd->pathno; docpos->marklist[1] = nd->width; docpos->marklist[2] = nd->depth; docpos->marklist[3] = nd->height; return 1; } /* NB: this relies on the fact that PI nodes have pathno == -1, * otherwise it would trip over them. */ ESISNode esis_stepdown(ESISNode nd, ESISPathlocAddr *docpos) { if (docpos->p.pathno + docpos->p.width <= nd->pathno) return 0; nd = nd->children; while (nd && nd->pathno + nd->width <= docpos->p.pathno) nd = nd->next; return nd; } ESISNode esis_locate(ESISNode nd, ESISPathlocAddr *docpos) { /* %%% also breaks on SDs; hack around that for now: */ if (nd && nd->type == EN_SD) { nd = nd->children; while (nd && nd->type != EN_EL) nd = nd->next; } /* %%% End hack. */ while (nd && nd->depth < docpos->p.depth) nd = esis_stepdown(nd, docpos); return nd; } #define traverseto(n) (n) /* %%% check status */ ESISNode esis_parent(ESISNode nd) { return traverseto(nd->parent); } ESISNode esis_nextsib(ESISNode nd) { return traverseto(nd->next); } ESISNode esis_prevsib(ESISNode nd) { return traverseto(nd->prev); } ESISNode esis_firstchild(ESISNode nd) { return traverseto(nd->children); } ESISNode esis_docroot(ESISNode nd) { while (nd->parent) nd = nd->parent; return traverseto(nd); } ESISNode esis_firstpreorder(ESISNode nd) { return nd; } ESISNode esis_nextpreorder(ESISNode start, ESISNode nd) { if (nd->children) return nd->children; while (nd && nd != start && !nd->next) nd = nd->parent; return (nd && nd != start) ? nd->next : 0; } ESISNode esis_lastpreorder(ESISNode nd) { while (nd->children) { nd = nd->children; while (nd->next) nd = nd->next; } return nd; } ESISNode esis_prevpreorder(ESISNode start, ESISNode nd) { if (nd == start) return 0; if (!nd->prev) return nd->parent; /* else */ return esis_lastpreorder(nd->prev); } ESISNode esis_firstpostorder(ESISNode); ESISNode esis_nextpostorder(ESISNode start, ESISNode nd); ESISNode esis_firstatt(ESISNode nd) { ESISNode at = nd->attributes; while (at && at->type != EN_AT) at = at->next; return at; } ESISNode esis_nextatt(ESISNode at) { ASSERT(at->type == EN_AT, "Passed non-attribute to esis_nextatt"); at = at->next; while (at && at->type != EN_AT) at = at->next; return at; } ESISNode esis_findatt(ESISNode nd, const char *attname) { attname = ucintern(attname); if (nd->type == EN_REFERENCE) nd = nd->reference; if (!nd) return 0; for (nd = nd->attributes; nd; nd = nd->next) if (nd->type == EN_AT && tokcmpic(nd->name, attname)) return traverseto(nd); return 0; } int esis_hasatt(ESISNode nd, const char *attname) { ESISNode attnode = esis_findatt(nd, attname); return attnode && (attnode->text != 0); } /* %%% should return NULL if #IMPLIED, check presence/absence in qattval */ ESISString esis_attval(ESISNode nd, const char *attname) { ESISNode at = esis_findatt(nd, attname); if (at) return at->text ? at->text : ""; else return 0; } ESISString esis_text(ESISNode nd) { switch (nd->type) { case EN_RE : return "\n"; case EN_CDATA : case EN_SDATA : case EN_PI : case EN_ENTITY : case EN_AT : return nd->text; /* These have no direct text content: */ case EN_REFERENCE : case EN_SD : case EN_PEL : case EN_EL : return 0; default: ASSERT(0,"esis_text: forgot a node type") break; } return 0; } int esis_traverse(ESISNode node, ESISEventHandler callback, void *closure) { int status = 1; ESISNode child; switch (node->type) { case EN_EL: /* Recursive case */ status = (*callback)(EV_START, node, closure); if (!status) return 0; for (child=esis_firstchild(node); child; child=esis_nextsib(child)) { status = esis_traverse(child, callback, closure); if (!status) return 0; } status = (*callback)(EV_END, node, closure); return status; case EN_PEL: case EN_SD: /* don't process (?%%%?) */ /* Only recurse: */ for (child=esis_firstchild(node); child; child=esis_nextsib(child)) { status = esis_traverse(child, callback, closure); if (!status) return 0; } return status; case EN_CDATA: return (*callback)(EV_CDATA, node, closure); case EN_SDATA: return (*callback)(EV_SDATA, node, closure); case EN_RE: return (*callback)(EV_RE, node, closure); case EN_PI: return (*callback)(EV_PI, node, closure); case EN_REFERENCE: return (*callback)(EV_DATAENT, node, closure); case EN_ENTITY: return (*callback)(EV_DATAENT, node, closure); /* These should not normally appear; * %%% it's not clear what to do with ILINK and LINKEND ... */ case EN_RELATION: case EN_ILINK: case EN_LINKEND: case EN_AT: case EN_ERROR: default: return 0; } } const char *esis_nodetype_name(ESISNodeType nodetype) { switch (nodetype) { case EN_SD: return "SD"; case EN_EL: return "EL"; case EN_PEL: return "PEL"; case EN_CDATA: return "CDATA"; case EN_SDATA: return "SDATA"; case EN_RE: return "RE"; case EN_REFERENCE: return "REFERENCE"; case EN_ENTITY: return "ENTITY"; case EN_PI: return "PI"; case EN_AT: return "AT"; case EN_RELATION: return "RELATION"; case EN_ILINK: return "ILINK"; case EN_LINKEND: return "LINKEND"; default: ASSERT(0,"Oops! Forgot a node type"); } return 0; } ESISNodeType esis_string_to_nodetype(const char *name) { if (tokcmpic(name,"SD")) return EN_SD; if (tokcmpic(name,"EL")) return EN_EL; if (tokcmpic(name,"PEL")) return EN_PEL; if (tokcmpic(name,"CDATA")) return EN_CDATA; if (tokcmpic(name,"SDATA")) return EN_SDATA; if (tokcmpic(name,"RE")) return EN_RE; if (tokcmpic(name,"REFERENCE")) return EN_REFERENCE; if (tokcmpic(name,"PI")) return EN_PI; if (tokcmpic(name,"AT")) return EN_AT; if (tokcmpic(name,"ENTITY")) return EN_ENTITY; if (tokcmpic(name,"RELATION")) return EN_RELATION; if (tokcmpic(name,"ILINK")) return EN_ILINK; if (tokcmpic(name,"LINKEND")) return EN_LINKEND; return EN_ERROR; } const char *esis_evtype_name(ESISEventType evtype) { char *evname = 0; switch(evtype) { case EV_EOF: evname = "EOF"; break; case EV_START: evname = "START"; break; case EV_END: evname = "END"; break; case EV_DATAENT: evname = "DATAENT"; break; case EV_PI: evname = "PI"; break; case EV_CDATA: evname = "CDATA"; break; case EV_SDATA: evname = "SDATA"; break; case EV_RE: evname = "RE"; break; case EV_SDSTART: evname = "SDSTART"; break; case EV_SDEND: evname = "SDEND"; break; case EV_ERROR: evname = "ERROR"; break; default: ASSERT(0,"Forgot an event type"); evname = " - internal error - "; break; } return evname; } ESISEventType esis_string_to_evtype(const char *name) { if (tokcmpic(name, "EOF")) return EV_EOF; if (tokcmpic(name, "START")) return EV_START; if (tokcmpic(name, "END")) return EV_END; if (tokcmpic(name, "PI")) return EV_PI; if (tokcmpic(name, "CDATA")) return EV_CDATA; if (tokcmpic(name, "SDATA")) return EV_SDATA; if (tokcmpic(name, "RE")) return EV_RE; if (tokcmpic(name, "DATAENT")) return EV_DATAENT; if (tokcmpic(name, "SDSTART")) return EV_SDSTART; if (tokcmpic(name, "SDEND")) return EV_SDEND; return EV_ERROR; } /*+++ HyTime-like stuff * (%%% move to separate module file...) */ /* %%% treeloc... double-check this; I don't think it's * doing the same thing as HyTime wrt root nodes & PI nodes. * %%% error-checking is bad */ #include ESISNode esis_treeloc(ESISNode root, const char *marklist) { char *nextp = (/*!const*/ char *)marklist; do { long childno,i; childno = strtol(nextp, &nextp, 10); if (!nextp) break; while (isspace(*nextp)) ++nextp; for (i=1; root && i