2 * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
7 * Open Software Foundation, Inc.
9 * Permission is hereby granted to use, copy, modify and freely distribute
10 * the software in this file and its documentation for any purpose without
11 * fee, provided that the above copyright notice appears in all copies and
12 * that both the copyright notice and this permission notice appear in
13 * supporting documentation. Further, provided that the name of Open
14 * Software Foundation, Inc. ("OSF") not be used in advertising or
15 * publicity pertaining to distribution of the software without prior
16 * written permission from OSF. OSF makes no representations about the
17 * suitability of this software for any purpose. It is provided "as is"
18 * without express or implied warranty.
20 /* ________________________________________________________________________
22 * General utility functions for 'instant' program. These are used
23 * throughout the rest of the program.
25 * Entry points for this module:
26 * Split(s, &n, flags) split string into n tokens
27 * NewMap(slot_incr) create a new mapping structure
28 * FindMapping(map, name) find mapping by name; return mapping
29 * FindMappingVal(map, name) find mapping by name; return value
30 * SetMapping(map, s) set mapping based on string
31 * OpenFile(filename) open file, looking in inst path
32 * FindElementPath(elem, s) find path to element
33 * PrintLocation(ele, fp) print location of element in tree
34 * NearestOlderElem(elem, name) find prev elem up tree with name
35 * OutputString(s, fp, track_pos) output string
36 * AddElemName(name) add elem to list of known elements
37 * AddAttName(name) add att name to list of known atts
38 * FindAttByName(elem, name) find an elem's att by name
39 * FindContext(elem, lev, context) find context of elem
40 * QRelation(elem, name, rel_flag) find relation elem has to named elem
41 * DescendTree(elem, enter_f, leave_f, data_f, dp) descend doc tree,
42 * calling functions for each elem/node
43 * ________________________________________________________________________
48 "$TOG: util.c /main/13 1997/10/09 16:09:50 bill $";
56 #include <sys/types.h>
63 /* forward references */
64 static char *LookupSDATA(char *);
65 static int CheckOutputBuffer(int length);
67 static OutputBuffer_t outputBuffer; /* init'd to all 0 by compiler */
69 /* ______________________________________________________________________ */
70 /* "Split" a string into tokens. Given a string that has space-separated
71 * (space/tab) tokens, return a pointer to an array of pointers to the
72 * tokens. Like what the shell does with *argv[]. The array can be is
73 * static or allocated. Space can be allocated for string, or allocated.
75 * Pointer to string to pick apart.
76 * Pointer to max number of tokens to find; actual number found is
77 * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
78 * code). If more tokens than the number specified, make last token be
79 * a single string composed of the rest of the tokens (includes spaces).
80 * Flag. Bit 0 says whether to make a copy of input string (since we'll
81 * clobber parts of it). To free the string, use the pointer to
82 * the first token returned by the function (or *ret_value).
83 * Bit 1 says whether to allocate the vector itself. If not, use
84 * (and return) a static vector.
86 * Pointer to the provided string (for convenience of caller).
91 char *s, /* input string */
92 int *ntok, /* # of tokens desired (input)/found (return) */
93 int flag /* dup string? allocate a vector? */
99 static char *local_tokens[100];
101 /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
105 if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
110 if (!s) return 0; /* no string */
112 /* Point to 1st token (there may be initial space) */
113 while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
114 if (*s == EOS) return 0; /* none found? */
116 /* See if caller wants us to copy the input string. */
117 if (flag & S_STRDUP) s = strdup(s);
119 /* See if caller wants us to allocate the returned vector. */
120 if (flag & S_ALVEC) {
122 Malloc(n_alloc, tokens, char *);
123 /* if caller did not specify max tokens to find, set to more than
124 * there will possibly ever be */
125 if (!ntok || !(*ntok)) maxnt = 10000;
127 else tokens = local_tokens;
129 i = 0; /* index into vector */
130 tokens[0] = s; /* s already points to 1st token */
132 tokens[i] = s; /* point vector member at start of token */
134 /* If we allocated vector, see if we need more space. */
135 if ((flag & S_ALVEC) && i >= n_alloc) {
137 Realloc(n_alloc, tokens, char *);
139 if (i >= maxnt) break; /* is this the last one? */
140 while (*s && !IsWhite(*s)) s++; /* skip past end of token */
141 if (*s == EOS) break; /* at end of input string? */
142 if (*s) *s++ = EOS; /* terminate token string */
143 while (*s && IsWhite(*s)) s++; /* skip space - to next token */
145 if (ntok) *ntok = i; /* return number of tokens found */
146 tokens[i] = 0; /* null-terminate vector */
150 /* ______________________________________________________________________ */
151 /* Mapping routines. These are used for name-value pairs, like attributes,
152 * variables, and counters. A "Map" is an opaque data structure used
153 * internally by these routines. The caller gets one when creating a new
154 * map, then hands it to other routines that need it. A "Mapping" is a
155 * name/value pair. The user has access to this.
156 * Here's some sample usage:
160 * SetMappingNV(V, "home", "/users/bowe");
161 * printf("Home: %s\n", FindMappingVal(V, "home");
164 /* Allocate new map structure. Only done once for each map/variable list.
166 * Number of initial slots to allocate space for. This is also the
167 * "chunk size" - how much to allocate when we use up the given space.
169 * Pointer to the (opaque) map structure. (User passes this to other
179 if (!slot_increment) slot_increment = 1;
180 M->slot_incr = slot_increment;
184 /* Given pointer to a Map and a name, find the mapping.
186 * Pointer to map structure (as returned by NewMap().
189 * Pointer to the matching mapping structure, or null if not found.
200 if (!M || M->n_used == 0) return NULL;
201 for (m=M->maps,i=0; i<M->n_used; i++)
202 if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
207 /* Given pointer to a Map and a name, return string value of the mapping.
209 * Pointer to map structure (as returned by NewMap().
212 * Pointer to the value (string), or null if not found.
221 if (!M || M->n_used == 0) return NULL;
222 if ((m = FindMapping(M, name))) return m->sval;
227 /* Set a mapping/variable in Map M. Input string is a name-value pair where
228 * there is some amount of space after the name. The correct mapping is done.
230 * Pointer to map structure (as returned by NewMap().
231 * Pointer to variable name (string).
232 * Pointer to variable value (string).
242 char buf[LINESIZE], *cp, *s;
246 /* First, look to see if it's a "well-known" variable. */
247 if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
248 if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
249 if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
251 m = FindMapping(M, name); /* find existing mapping (if set) */
253 /* Need more slots for mapping structures? Allocate in clumps. */
254 if (M->n_used == 0) {
255 M->n_alloc = M->slot_incr;
256 Malloc(M->n_alloc, M->maps, Mapping_t);
258 else if (M->n_used >= M->n_alloc) {
259 M->n_alloc += M->slot_incr;
260 Realloc(M->n_alloc, M->maps, Mapping_t);
263 /* OK, we have a string mapping */
264 if (m) { /* exists - just replace value */
266 m->sval = strdup(value);
267 if (value) m->sval = strdup(value);
271 if (name) { /* just in case */
272 m = &M->maps[M->n_used];
274 m->name = strdup(name);
275 if (value) m->sval = strdup(value);
282 /* See if the value is a command to run. If so, run the command
283 * and replace the value with the output.
287 s++; /* point to command */
288 if ((pp = popen(s, "r"))) { /* run cmd, read its output */
291 while (fgets(cp, LINESIZE-i, pp)) {
296 "Prog execution of variable '%s' too long.\n",
303 m->sval = strdup(buf);
307 sprintf(buf, "Could not start program '%s'", s);
314 /* Separate name and value from input string, then pass to SetMappingNV.
316 * Pointer to map structure (as returned by NewMap().
317 * Pointer to variable name and value (string), in form "name value".
329 fprintf(stderr, "SetMapping: Map not initialized.\n");
334 while (*val && !IsWhite(*val)) val++; /* point past end of name */
336 *val++ = EOS; /* terminate name */
337 while (*val && IsWhite(*val)) val++; /* point to value */
339 if (name) SetMappingNV(M, name, val);
342 /* ______________________________________________________________________ */
343 /* Opens a file for reading. If not found in current directory, try
344 * lib directories (from TPT_LIB env variable, or -l option).
348 * FILE pointer to open file, or null if it not found or can't open.
359 static char **libdirs;
360 static int nlibdirs = -1;
362 if ((fp=fopen(filename, "r"))) return fp;
364 if (*filename == '/') return NULL; /* full path specified? */
370 for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
372 libdirs = Split(s, &nlibdirs, S_ALVEC);
376 for (i=0; i<nlibdirs; i++) {
377 sprintf(buf, "%s/%s", libdirs[i], filename);
378 if ((fp=fopen(buf, "r"))) return fp;
383 /* ______________________________________________________________________ */
384 /* This will find the path to an tag. The format is the:
385 * tag1(n1):tag2(n2):tag3
386 * where the tags are going down the tree and the numbers indicate which
387 * child (the first is numbered 1) the next tag is.
388 * Returns pointer to the string just written to (so you can use this
389 * function as a printf arg).
391 * Pointer to element under consideration.
392 * String to write path into (provided by caller).
394 * Pointer to the provided string (for convenience of caller).
403 int i, e_path[MAX_DEPTH];
406 /* Move up the tree, noting "birth order" of each element encountered */
407 for (ep=e; ep->parent; ep=ep->parent)
408 e_path[ep->depth-1] = ep->my_eorder;
409 /* Move down the tree, printing the element names to the string. */
410 for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
411 sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
414 sprintf(cp, "%s", e->gi);
418 /* ______________________________________________________________________ */
419 /* Print some location info about a tag. Helps user locate error.
420 * Messages are indented 2 spaces (convention for multi-line messages).
422 * Pointer to element under consideration.
423 * FILE pointer of where to print.
432 char *s, buf[LINESIZE];
434 if (!e || !fp) return;
435 fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
436 if ((s=NearestOlderElem(e, "TITLE")))
437 fprintf(fp, " Position hint: TITLE='%s'\n", s);
440 fprintf(fp, " At or near instance file: %s, line: %d\n",
441 e->infile, e->lineno);
443 fprintf(fp, " At or near instance line: %d\n", e->lineno);
446 fprintf(fp, " ID: %s\n", e->id);
449 /* ______________________________________________________________________ */
450 /* Finds the data part of the nearest "older" tag (up the tree, and
451 * preceding) whose tag name matches the argument, or "TITLE", if null.
452 * Returns a pointer to the first chunk of character data.
454 * Pointer to element under consideration.
455 * Name (GI) of element we'll return data from.
457 * Pointer to that element's data content.
469 if (!name) name = "TITLE"; /* useful default */
471 for (; e->parent; e=e->parent) /* move up tree */
472 for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
474 if (!strcmp(name, ep->econt[i]->gi))
475 return ep->econt[i]->ndcont ?
476 ep->econt[i]->dcont[0] : "-empty-";
482 /* ______________________________________________________________________ */
483 /* Expands escaped strings in the input buffer (things like tabs, newlines,
484 * octal characters - using C style escapes) if outputting the buffer to
485 * the specified fp. If fp is NULL, we're only preparing the output
486 * for the interpreter so don't expand escaped strings. The hat/anchor
487 * character forces that position to appear at the beginning of a line.
488 * The cursor position is kept track of (optionally) so that this can be
491 * Pointer to element under consideration.
492 * FILE pointer of where to print.
493 * Flag saying whether or not to keep track of our position in the output
494 * stream. (We want to when writing to a file, but not for stderr.)
505 static int char_pos; /* remembers our character position */
506 static int interp_pos; /* like char_pos but when output is to interp */
507 int *ppos; /* points to appropriate line position var */
510 ppos = &char_pos; /* writing to file */
512 ppos = &interp_pos; /* buffer will be read by interpreter */
514 if (!s) s = "^"; /* no string - go to start of line */
517 if (fp && (*s == '\\')) { /* don't expand for interpreter */
519 if (track_pos) (*ppos)++;
521 default: c = *s; break;
523 case 's': c = ' '; break;
525 case 't': c = TAB; break;
527 case 'n': c = NL; *ppos = 0; break;
529 case 'r': c = CR; *ppos = 0; break;
531 case '0': case '1': case '2': case '3':
532 case '4': case '5': case '6': case '7':
533 /* for octal numbers (C style) of the form \012 */
535 for (s++; ((*s >= '0') && (*s <= '7')); s++)
536 c = (c << 3) + (*s - '0');
540 case '|': /* SDATA */
541 s++; /* point past \| */
543 /* find matching/closing \| */
545 while (*cp && *cp != '\\' && cp[1] != '|') cp++;
548 *cp = EOS; /* terminate sdata string */
550 s = cp; /* s now points to | */
552 cp = LookupSDATA(sdata);
553 if (cp) OutputString(cp, fp, track_pos);
555 /* not found - output sdata thing in brackets */
564 else { /* not escaped - just pass the character */
566 /* If caller wants us to track position, see if it's an anchor
567 * (ie, align at a newline). */
570 /* If we're already at the start of a line, don't do
571 * another newline. */
572 if (*ppos != 0) c = NL;
576 if (c == NL) *ppos = 0;
578 else if (c == ANCHOR) c = NL;
584 /* ______________________________________________________________________ */
585 /* resets the output buffer
587 void ClearOutputBuffer()
589 outputBuffer.current = outputBuffer.base;
592 /* ______________________________________________________________________ */
593 /* determines if there is already some text in the output buffer,
594 * returns 1 if so, else 0
596 int OutputBufferActive()
598 return (outputBuffer.current != outputBuffer.base);
601 /* ______________________________________________________________________ */
602 /* terminates output buffer with a null char and returns the buffer
604 char *GetOutputBuffer()
606 if (CheckOutputBuffer(1))
607 *(outputBuffer.current)++ = '\0';
609 return outputBuffer.base;
612 /* ______________________________________________________________________ */
613 /* insures that there's enough room in outputBuffer to hold a string
614 * of the given length.
615 * Arguments: the length of the string
617 static int CheckOutputBuffer(
622 int oldSize, incr = OBUF_INCR;
624 while (length > incr) incr += OBUF_INCR;
626 if ((outputBuffer.current - outputBuffer.base + length)
629 oldBase = outputBuffer.base;
630 oldSize = outputBuffer.size;
631 outputBuffer.size += incr;
634 realloc(outputBuffer.base, outputBuffer.size) :
635 malloc(outputBuffer.size);
636 if (outputBuffer.base == NULL) {
637 outputBuffer.base = oldBase;
638 outputBuffer.size = oldSize;
641 outputBuffer.current =
642 outputBuffer.base + (outputBuffer.current - oldBase);
649 /* ______________________________________________________________________ */
650 /* local version of fflush(3S)
652 * special cases a FILE of NULL to simply return success
655 int FFlush(FILE *stream)
657 if (stream) return fflush(stream);
662 /* ______________________________________________________________________ */
663 /* local version of putc(3S)
665 * special cases a FILE of NULL by working into a buffer for later
666 * use by the interpreter
668 * extra special hack: call Tcl interpreter with the character; worry
669 * about "stream" somo other time, we'll default to stdout
678 static char commandBuf[] = "OutputString \" ";
681 pc = &(commandBuf[14]);
682 switch (c) { /* escape those things that throw off tcl */
696 result = Tcl_Eval(interpreter, commandBuf);
698 if (result != TCL_OK) {
700 "interpreter error \"%s\" at line %d executing:\n",
702 interpreter->errorLine);
703 fprintf(stderr, "\"%s\"\n", commandBuf);
709 if ((CheckOutputBuffer(1)) == 0)
710 return EOF; /* out of space and can't grow the buffer */
712 *(outputBuffer.current)++ = (char) c;
717 /* ______________________________________________________________________ */
718 /* local version of fputs(3S)
720 * special cases a FILE of NULL by working into a buffer for later
721 * use by the interpreter
728 static char commandBuf[128] = "OutputString \"";
734 if ((sLength = strlen(s)) == 0)
735 return 0; /* no need to call CheckOutputBuffer() */
738 if (sLength > 100/2) { /* assume that every char must be escaped */
739 pBuff = malloc(sLength + 14 + 1);
741 strcpy(pBuff, commandBuf);
747 switch (*ps) { /* escape those things that throw off Tcl */
761 result = Tcl_Eval(interpreter, pBuff);
763 if (result != TCL_OK) {
765 "interpreter error \"%s\" at line %d executing:\n",
767 interpreter->errorLine);
768 fprintf(stderr, "\"%s\"\n", pBuff);
769 if (pBuff != commandBuf) free(pBuff);
772 if (pBuff != commandBuf) free(pBuff);
776 if ((CheckOutputBuffer(sLength)) == 0)
777 return EOF; /* out of space and can't grow the buffer */
779 strncpy(outputBuffer.current, s, sLength);
780 outputBuffer.current += sLength;
782 return sLength; /* arbitrary non-negative number */
785 /* ______________________________________________________________________ */
786 /* Figure out value of SDATA entity.
787 * We rememeber lookup hits in a "cache" (a shorter list), and look in
788 * cache before general list. Typically there will be LOTS of entries
789 * in the general list and only a handful in the hit list. Often, if an
790 * entity is used once, it'll be used again.
792 * Pointer to SDATA entity token in ESIS.
794 * Mapped value of the SDATA entity.
803 static Map_t *Hits; /* remember lookup hits */
805 /* SDL SDL SDL SDL --- special (i.e., hack); see below */
806 /* we're going to replace the "123456" below with the SDATA */
808 static char spcString[] = "<SPC NAME=\"[123456]\">\0";
809 static char spc[sizeof(spcString)];
812 /* If we have a hit list, check it. */
814 if ((v = FindMappingVal(Hits, s))) return v;
817 v = FindMappingVal(SDATAmap, s);
819 /* If mapping found, remember it, then return it. */
820 if ((v = FindMappingVal(SDATAmap, s))) {
821 if (!Hits) Hits = NewMap(IMS_sdatacache);
822 SetMappingNV(Hits, s, v);
826 /* SDL SDL SDL SDL --- special (i.e., hack)
827 Special case sdata values of six letters surrounded by square
828 brackets. Just convert them over to the SDL <SPC> stuff
830 if ((strlen(s) == 8) &&
833 if (strcmp(s, "[newlin]") == 0) {
836 strcpy(spc, spcString);
837 strncpy(spc+12, s+1, 6);
842 fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
846 /* ______________________________________________________________________ */
847 /* Add tag 'name' of length 'len' to list of tag names (if not there).
848 * This is a list of null-terminated strings so that we don't have to
849 * keep using the name length.
851 * Pointer to element name (GI) to remember.
853 * Pointer to the SAVED element name (GI).
862 static int n_alloc=0; /* number of slots allocated so far */
864 /* See if it's already in the list. */
865 for (i=0; i<nUsedElem; i++)
866 if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
869 /* Allocate slots in blocks of N, so we don't have to call malloc
872 n_alloc = IMS_elemnames;
873 Calloc(n_alloc, UsedElem, char *);
875 else if (nUsedElem >= n_alloc) {
876 n_alloc += IMS_elemnames;
877 Realloc(n_alloc, UsedElem, char *);
879 UsedElem[nUsedElem] = strdup(name);
880 return UsedElem[nUsedElem++];
882 /* ______________________________________________________________________ */
883 /* Add attrib name to list of attrib names (if not there).
884 * This is a list of null-terminated strings so that we don't have to
885 * keep using the name length.
887 * Pointer to attr name to remember.
889 * Pointer to the SAVED attr name.
898 static int n_alloc=0; /* number of slots allocated so far */
900 /* See if it's already in the list. */
901 for (i=0; i<nUsedAtt; i++)
902 if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
905 /* Allocate slots in blocks of N, so we don't have to call malloc
908 n_alloc = IMS_attnames;
909 Calloc(n_alloc, UsedAtt, char *);
911 else if (nUsedAtt >= n_alloc) {
912 n_alloc += IMS_attnames;
913 Realloc(n_alloc, UsedAtt, char *);
915 UsedAtt[nUsedAtt] = strdup(name);
916 return UsedAtt[nUsedAtt++];
919 /* ______________________________________________________________________ */
920 /* Find an element's attribute value given element pointer and attr name.
922 * a=FindAttByName("TYPE", t);
923 * do something with a->val;
925 * Pointer to element under consideration.
926 * Pointer to attribute name.
928 * Pointer to the value of the attribute.
940 for (i=0; i<e->natts; i++)
941 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
942 return &(e->atts[i]);
955 for (i=0; i<e->natts; i++)
956 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
957 return e->atts[i].sval;
961 /* ______________________________________________________________________ */
962 /* Find context of a tag, 'levels' levels up the tree.
963 * Space for string is passed by caller.
965 * Pointer to element under consideration.
966 * Number of levels to look up tree.
967 * String to write path into (provided by caller).
969 * Pointer to the provided string (for convenience of caller).
986 for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
987 if (i != 0) *s++ = ' ';
995 /* ______________________________________________________________________ */
996 /* Tests relationship (specified by argument/flag) between given element
997 * (structure pointer) and named element.
998 * Returns pointer to matching tag if found, null otherwise.
1000 * Pointer to element under consideration.
1001 * Pointer to name of elem whose relationsip we are trying to determine.
1002 * Relationship we are testing.
1004 * Pointer to the provided string (for convenience of caller).
1019 /* we'll call e the "given element" */
1023 if (!e->parent || !e->parent->gi) return 0;
1024 if (!strcmp(e->parent->gi, s)) return e->parent;
1027 for (i=0; i<e->necont; i++)
1028 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1031 if (!e->parent || !e->parent->gi) return 0;
1032 for (ep=e->parent; ep; ep=ep->parent)
1033 if (!strcmp(ep->gi, s)) return ep;
1035 case REL_Descendant:
1036 if (e->necont == 0) return 0;
1037 /* check immediate children first */
1038 for (i=0; i<e->necont; i++)
1039 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1040 /* then children's children (recursively) */
1041 for (i=0; i<e->necont; i++)
1042 if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
1046 if (!e->parent) return 0;
1048 for (i=0; i<ep->necont; i++)
1049 if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
1050 return ep->econt[i];
1053 if (!e->parent || e->my_eorder == 0) return 0;
1055 for (i=0; i<e->my_eorder; i++)
1056 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1058 case REL_ImmPreceding:
1059 if (!e->parent || e->my_eorder == 0) return 0;
1060 ep = e->parent->econt[e->my_eorder-1];
1061 if (!strcmp(s, ep->gi)) return ep;
1064 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1065 return 0; /* last? */
1067 for (i=(e->my_eorder+1); i<ep->necont; i++)
1068 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1070 case REL_ImmFollowing:
1071 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1072 return 0; /* last? */
1073 ep = e->parent->econt[e->my_eorder+1];
1074 if (!strcmp(s, ep->gi)) return ep;
1077 if (!e->parent) return 0;
1078 /* Now, see if element's parent has that thing as a child. */
1079 return QRelation(e->parent, s, REL_Child);
1081 case REL_Is1stContent:
1082 /* first confirm that our parent is an "s" */
1083 if (!(ep = QRelation(e, s, REL_Parent))) return 0;
1084 /* then check that we are the first content in that parent */
1085 if ((ep->cont->type == CMD_OPEN) && (ep->cont->ch.elem == e))
1088 case REL_HasOnlyContent:
1089 /* first confirm that we have a child of "s" */
1090 if (!(ep = QRelation(e, s, REL_Child))) return 0;
1091 /* then check that it is our only content */
1092 if (e->ncont == 1) return 0;
1096 fprintf(stderr, "You cannot query 'REL_None' or 'REL_Unknown'.\n");
1102 /* Given a relationship name (string), determine enum symbol for it.
1104 * Pointer to relationship name.
1113 if (!strcmp(relname, "?")) {
1114 fprintf(stderr, "Supported query/relationships %s\n%s\n%s.\n",
1115 "child, parent, ancestor, descendant,",
1116 "sibling, sibling+, sibling+1, sibling-, sibling-1,",
1117 "cousin, isfirstcon, hasonlycon");
1120 else if (StrEq(relname, "child")) return REL_Child;
1121 else if (StrEq(relname, "parent")) return REL_Parent;
1122 else if (StrEq(relname, "ancestor")) return REL_Ancestor;
1123 else if (StrEq(relname, "descendant")) return REL_Descendant;
1124 else if (StrEq(relname, "sibling")) return REL_Sibling;
1125 else if (StrEq(relname, "sibling-")) return REL_Preceding;
1126 else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
1127 else if (StrEq(relname, "sibling+")) return REL_Following;
1128 else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
1129 else if (StrEq(relname, "cousin")) return REL_Cousin;
1130 else if (StrEq(relname, "isfirstcon")) return REL_Is1stContent;
1131 else if (StrEq(relname, "hasonlycon")) return REL_HasOnlyContent;
1132 else fprintf(stderr, "Unknown relationship: %s\n", relname);
1136 /* ______________________________________________________________________ */
1137 /* This will descend the element tree in-order. (enter_f)() is called
1138 * upon entering the node. Then all children (data and child elements)
1139 * are operated on, calling either DescendTree() with a pointer to
1140 * the child element or (data_f)() for each non-element child node.
1141 * Before leaving the node (ascending), (leave_f)() is called. enter_f
1142 * and leave_f are passed a pointer to this node and data_f is passed
1143 * a pointer to the data/content (which includes the data itself and
1144 * type information). dp is an opaque pointer to any data the caller
1147 * Pointer to element under consideration.
1148 * Pointer to procedure to call when entering element.
1149 * Pointer to procedure to call when leaving element.
1150 * Pointer to procedure to call for each "chunk" of content data.
1151 * Void data pointer, passed to the avobe 3 procedures.
1164 if (enter_f) (enter_f)(e, dp);
1165 for (i=0; i<e->ncont; i++) {
1166 if (e->cont[i].type == CMD_OPEN)
1167 DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
1169 if (data_f) (data_f)(&e->cont[i], dp);
1171 if (leave_f) (leave_f)(e, dp);
1174 /* ______________________________________________________________________ */
1175 /* Add element, 'e', whose ID is 'idval', to a list of IDs.
1176 * This makes it easier to find an element by ID later.
1178 * Pointer to element under consideration.
1179 * Element's ID attribute value (a string).
1188 static ID_t *id_last;
1191 Malloc(1, id_last, ID_t);
1195 Malloc(1, id_last->next, ID_t);
1196 id_last = id_last->next;
1199 id_last->id = idval;
1202 /* ______________________________________________________________________ */
1203 /* Return pointer to element who's ID is given.
1205 * Element's ID attribute value (a string).
1207 * Pointer to element whose ID matches.
1216 for (id=IDList; id; id=id->next)
1217 if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
1221 /* ______________________________________________________________________ */