2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
25 * All rights reserved.
29 * Open Software Foundation, Inc.
31 * Permission is hereby granted to use, copy, modify and freely distribute
32 * the software in this file and its documentation for any purpose without
33 * fee, provided that the above copyright notice appears in all copies and
34 * that both the copyright notice and this permission notice appear in
35 * supporting documentation. Further, provided that the name of Open
36 * Software Foundation, Inc. ("OSF") not be used in advertising or
37 * publicity pertaining to distribution of the software without prior
38 * written permission from OSF. OSF makes no representations about the
39 * suitability of this software for any purpose. It is provided "as is"
40 * without express or implied warranty.
42 /* ________________________________________________________________________
44 * General utility functions for 'instant' program. These are used
45 * throughout the rest of the program.
47 * Entry points for this module:
48 * Split(s, &n, flags) split string into n tokens
49 * NewMap(slot_incr) create a new mapping structure
50 * FindMapping(map, name) find mapping by name; return mapping
51 * FindMappingVal(map, name) find mapping by name; return value
52 * SetMapping(map, s) set mapping based on string
53 * OpenFile(filename) open file, looking in inst path
54 * FindElementPath(elem, s) find path to element
55 * PrintLocation(ele, fp) print location of element in tree
56 * NearestOlderElem(elem, name) find prev elem up tree with name
57 * OutputString(s, fp, track_pos) output string
58 * AddElemName(name) add elem to list of known elements
59 * AddAttName(name) add att name to list of known atts
60 * FindAttByName(elem, name) find an elem's att by name
61 * FindContext(elem, lev, context) find context of elem
62 * QRelation(elem, name, rel_flag) find relation elem has to named elem
63 * DescendTree(elem, enter_f, leave_f, data_f, dp) descend doc tree,
64 * calling functions for each elem/node
65 * ________________________________________________________________________
70 "$TOG: util.c /main/13 1997/10/09 16:09:50 bill $";
78 #include <sys/types.h>
85 /* forward references */
86 static char *LookupSDATA(char *);
87 static int CheckOutputBuffer(int length);
89 static OutputBuffer_t outputBuffer; /* init'd to all 0 by compiler */
91 /* ______________________________________________________________________ */
92 /* "Split" a string into tokens. Given a string that has space-separated
93 * (space/tab) tokens, return a pointer to an array of pointers to the
94 * tokens. Like what the shell does with *argv[]. The array can be is
95 * static or allocated. Space can be allocated for string, or allocated.
97 * Pointer to string to pick apart.
98 * Pointer to max number of tokens to find; actual number found is
99 * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
100 * code). If more tokens than the number specified, make last token be
101 * a single string composed of the rest of the tokens (includes spaces).
102 * Flag. Bit 0 says whether to make a copy of input string (since we'll
103 * clobber parts of it). To free the string, use the pointer to
104 * the first token returned by the function (or *ret_value).
105 * Bit 1 says whether to allocate the vector itself. If not, use
106 * (and return) a static vector.
108 * Pointer to the provided string (for convenience of caller).
113 char *s, /* input string */
114 int *ntok, /* # of tokens desired (input)/found (return) */
115 int flag /* dup string? allocate a vector? */
121 static char *local_tokens[100];
123 /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
127 if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
132 if (!s) return 0; /* no string */
134 /* Point to 1st token (there may be initial space) */
135 while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
136 if (*s == EOS) return 0; /* none found? */
138 /* See if caller wants us to copy the input string. */
139 if (flag & S_STRDUP) s = strdup(s);
141 /* See if caller wants us to allocate the returned vector. */
142 if (flag & S_ALVEC) {
144 Malloc(n_alloc, tokens, char *);
145 /* if caller did not specify max tokens to find, set to more than
146 * there will possibly ever be */
147 if (!ntok || !(*ntok)) maxnt = 10000;
149 else tokens = local_tokens;
151 i = 0; /* index into vector */
152 tokens[0] = s; /* s already points to 1st token */
154 tokens[i] = s; /* point vector member at start of token */
156 /* If we allocated vector, see if we need more space. */
157 if ((flag & S_ALVEC) && i >= n_alloc) {
159 Realloc(n_alloc, tokens, char *);
161 if (i >= maxnt) break; /* is this the last one? */
162 while (*s && !IsWhite(*s)) s++; /* skip past end of token */
163 if (*s == EOS) break; /* at end of input string? */
164 if (*s) *s++ = EOS; /* terminate token string */
165 while (*s && IsWhite(*s)) s++; /* skip space - to next token */
167 if (ntok) *ntok = i; /* return number of tokens found */
168 tokens[i] = 0; /* null-terminate vector */
172 /* ______________________________________________________________________ */
173 /* Mapping routines. These are used for name-value pairs, like attributes,
174 * variables, and counters. A "Map" is an opaque data structure used
175 * internally by these routines. The caller gets one when creating a new
176 * map, then hands it to other routines that need it. A "Mapping" is a
177 * name/value pair. The user has access to this.
178 * Here's some sample usage:
182 * SetMappingNV(V, "home", "/users/bowe");
183 * printf("Home: %s\n", FindMappingVal(V, "home");
186 /* Allocate new map structure. Only done once for each map/variable list.
188 * Number of initial slots to allocate space for. This is also the
189 * "chunk size" - how much to allocate when we use up the given space.
191 * Pointer to the (opaque) map structure. (User passes this to other
201 if (!slot_increment) slot_increment = 1;
202 M->slot_incr = slot_increment;
206 /* Given pointer to a Map and a name, find the mapping.
208 * Pointer to map structure (as returned by NewMap().
211 * Pointer to the matching mapping structure, or null if not found.
222 if (!M || M->n_used == 0) return NULL;
223 for (m=M->maps,i=0; i<M->n_used; i++)
224 if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
229 /* Given pointer to a Map and a name, return string value of the mapping.
231 * Pointer to map structure (as returned by NewMap().
234 * Pointer to the value (string), or null if not found.
243 if (!M || M->n_used == 0) return NULL;
244 if ((m = FindMapping(M, name))) return m->sval;
249 /* Set a mapping/variable in Map M. Input string is a name-value pair where
250 * there is some amount of space after the name. The correct mapping is done.
252 * Pointer to map structure (as returned by NewMap().
253 * Pointer to variable name (string).
254 * Pointer to variable value (string).
264 char buf[LINESIZE], *cp, *s;
268 /* First, look to see if it's a "well-known" variable. */
269 if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
270 if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
271 if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
273 m = FindMapping(M, name); /* find existing mapping (if set) */
275 /* Need more slots for mapping structures? Allocate in clumps. */
276 if (M->n_used == 0) {
277 M->n_alloc = M->slot_incr;
278 Malloc(M->n_alloc, M->maps, Mapping_t);
280 else if (M->n_used >= M->n_alloc) {
281 M->n_alloc += M->slot_incr;
282 Realloc(M->n_alloc, M->maps, Mapping_t);
285 /* OK, we have a string mapping */
286 if (m) { /* exists - just replace value */
288 m->sval = strdup(value);
289 if (value) m->sval = strdup(value);
293 if (name) { /* just in case */
294 m = &M->maps[M->n_used];
296 m->name = strdup(name);
297 if (value) m->sval = strdup(value);
304 /* See if the value is a command to run. If so, run the command
305 * and replace the value with the output.
309 s++; /* point to command */
310 if ((pp = popen(s, "r"))) { /* run cmd, read its output */
313 while (fgets(cp, LINESIZE-i, pp)) {
318 "Prog execution of variable '%s' too long.\n",
325 m->sval = strdup(buf);
329 sprintf(buf, "Could not start program '%s'", s);
336 /* Separate name and value from input string, then pass to SetMappingNV.
338 * Pointer to map structure (as returned by NewMap().
339 * Pointer to variable name and value (string), in form "name value".
351 fprintf(stderr, "SetMapping: Map not initialized.\n");
356 while (*val && !IsWhite(*val)) val++; /* point past end of name */
358 *val++ = EOS; /* terminate name */
359 while (*val && IsWhite(*val)) val++; /* point to value */
361 if (name) SetMappingNV(M, name, val);
364 /* ______________________________________________________________________ */
365 /* Opens a file for reading. If not found in current directory, try
366 * lib directories (from TPT_LIB env variable, or -l option).
370 * FILE pointer to open file, or null if it not found or can't open.
381 static char **libdirs;
382 static int nlibdirs = -1;
384 if ((fp=fopen(filename, "r"))) return fp;
386 if (*filename == '/') return NULL; /* full path specified? */
392 for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
394 libdirs = Split(s, &nlibdirs, S_ALVEC);
398 for (i=0; i<nlibdirs; i++) {
399 sprintf(buf, "%s/%s", libdirs[i], filename);
400 if ((fp=fopen(buf, "r"))) return fp;
405 /* ______________________________________________________________________ */
406 /* This will find the path to an tag. The format is the:
407 * tag1(n1):tag2(n2):tag3
408 * where the tags are going down the tree and the numbers indicate which
409 * child (the first is numbered 1) the next tag is.
410 * Returns pointer to the string just written to (so you can use this
411 * function as a printf arg).
413 * Pointer to element under consideration.
414 * String to write path into (provided by caller).
416 * Pointer to the provided string (for convenience of caller).
425 int i, e_path[MAX_DEPTH];
428 /* Move up the tree, noting "birth order" of each element encountered */
429 for (ep=e; ep->parent; ep=ep->parent)
430 e_path[ep->depth-1] = ep->my_eorder;
431 /* Move down the tree, printing the element names to the string. */
432 for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
433 sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
436 sprintf(cp, "%s", e->gi);
440 /* ______________________________________________________________________ */
441 /* Print some location info about a tag. Helps user locate error.
442 * Messages are indented 2 spaces (convention for multi-line messages).
444 * Pointer to element under consideration.
445 * FILE pointer of where to print.
454 char *s, buf[LINESIZE];
456 if (!e || !fp) return;
457 fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
458 if ((s=NearestOlderElem(e, "TITLE")))
459 fprintf(fp, " Position hint: TITLE='%s'\n", s);
462 fprintf(fp, " At or near instance file: %s, line: %d\n",
463 e->infile, e->lineno);
465 fprintf(fp, " At or near instance line: %d\n", e->lineno);
468 fprintf(fp, " ID: %s\n", e->id);
471 /* ______________________________________________________________________ */
472 /* Finds the data part of the nearest "older" tag (up the tree, and
473 * preceding) whose tag name matches the argument, or "TITLE", if null.
474 * Returns a pointer to the first chunk of character data.
476 * Pointer to element under consideration.
477 * Name (GI) of element we'll return data from.
479 * Pointer to that element's data content.
491 if (!name) name = "TITLE"; /* useful default */
493 for (; e->parent; e=e->parent) /* move up tree */
494 for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
496 if (!strcmp(name, ep->econt[i]->gi))
497 return ep->econt[i]->ndcont ?
498 ep->econt[i]->dcont[0] : "-empty-";
504 /* ______________________________________________________________________ */
505 /* Expands escaped strings in the input buffer (things like tabs, newlines,
506 * octal characters - using C style escapes) if outputting the buffer to
507 * the specified fp. If fp is NULL, we're only preparing the output
508 * for the interpreter so don't expand escaped strings. The hat/anchor
509 * character forces that position to appear at the beginning of a line.
510 * The cursor position is kept track of (optionally) so that this can be
513 * Pointer to element under consideration.
514 * FILE pointer of where to print.
515 * Flag saying whether or not to keep track of our position in the output
516 * stream. (We want to when writing to a file, but not for stderr.)
527 static int char_pos; /* remembers our character position */
528 static int interp_pos; /* like char_pos but when output is to interp */
529 int *ppos; /* points to appropriate line position var */
532 ppos = &char_pos; /* writing to file */
534 ppos = &interp_pos; /* buffer will be read by interpreter */
536 if (!s) s = "^"; /* no string - go to start of line */
539 if (fp && (*s == '\\')) { /* don't expand for interpreter */
541 if (track_pos) (*ppos)++;
543 default: c = *s; break;
545 case 's': c = ' '; break;
547 case 't': c = TAB; break;
549 case 'n': c = NL; *ppos = 0; break;
551 case 'r': c = CR; *ppos = 0; break;
553 case '0': case '1': case '2': case '3':
554 case '4': case '5': case '6': case '7':
555 /* for octal numbers (C style) of the form \012 */
557 for (s++; ((*s >= '0') && (*s <= '7')); s++)
558 c = (c << 3) + (*s - '0');
562 case '|': /* SDATA */
563 s++; /* point past \| */
565 /* find matching/closing \| */
567 while (*cp && *cp != '\\' && cp[1] != '|') cp++;
570 *cp = EOS; /* terminate sdata string */
572 s = cp; /* s now points to | */
574 cp = LookupSDATA(sdata);
575 if (cp) OutputString(cp, fp, track_pos);
577 /* not found - output sdata thing in brackets */
586 else { /* not escaped - just pass the character */
588 /* If caller wants us to track position, see if it's an anchor
589 * (ie, align at a newline). */
592 /* If we're already at the start of a line, don't do
593 * another newline. */
594 if (*ppos != 0) c = NL;
598 if (c == NL) *ppos = 0;
600 else if (c == ANCHOR) c = NL;
606 /* ______________________________________________________________________ */
607 /* resets the output buffer
609 void ClearOutputBuffer()
611 outputBuffer.current = outputBuffer.base;
614 /* ______________________________________________________________________ */
615 /* determines if there is already some text in the output buffer,
616 * returns 1 if so, else 0
618 int OutputBufferActive()
620 return (outputBuffer.current != outputBuffer.base);
623 /* ______________________________________________________________________ */
624 /* terminates output buffer with a null char and returns the buffer
626 char *GetOutputBuffer()
628 if (CheckOutputBuffer(1))
629 *(outputBuffer.current)++ = '\0';
631 return outputBuffer.base;
634 /* ______________________________________________________________________ */
635 /* insures that there's enough room in outputBuffer to hold a string
636 * of the given length.
637 * Arguments: the length of the string
639 static int CheckOutputBuffer(
644 int oldSize, incr = OBUF_INCR;
646 while (length > incr) incr += OBUF_INCR;
648 if ((outputBuffer.current - outputBuffer.base + length)
651 oldBase = outputBuffer.base;
652 oldSize = outputBuffer.size;
653 outputBuffer.size += incr;
656 realloc(outputBuffer.base, outputBuffer.size) :
657 malloc(outputBuffer.size);
658 if (outputBuffer.base == NULL) {
659 outputBuffer.base = oldBase;
660 outputBuffer.size = oldSize;
663 outputBuffer.current =
664 outputBuffer.base + (outputBuffer.current - oldBase);
671 /* ______________________________________________________________________ */
672 /* local version of fflush(3S)
674 * special cases a FILE of NULL to simply return success
677 int FFlush(FILE *stream)
679 if (stream) return fflush(stream);
684 /* ______________________________________________________________________ */
685 /* local version of putc(3S)
687 * special cases a FILE of NULL by working into a buffer for later
688 * use by the interpreter
690 * extra special hack: call Tcl interpreter with the character; worry
691 * about "stream" somo other time, we'll default to stdout
700 static char commandBuf[] = "OutputString \" ";
703 pc = &(commandBuf[14]);
704 switch (c) { /* escape those things that throw off tcl */
718 result = Tcl_Eval(interpreter, commandBuf);
720 if (result != TCL_OK) {
722 "interpreter error \"%s\" at line %d executing:\n",
724 interpreter->errorLine);
725 fprintf(stderr, "\"%s\"\n", commandBuf);
731 if ((CheckOutputBuffer(1)) == 0)
732 return EOF; /* out of space and can't grow the buffer */
734 *(outputBuffer.current)++ = (char) c;
739 /* ______________________________________________________________________ */
740 /* local version of fputs(3S)
742 * special cases a FILE of NULL by working into a buffer for later
743 * use by the interpreter
750 static char commandBuf[128] = "OutputString \"";
756 if ((sLength = strlen(s)) == 0)
757 return 0; /* no need to call CheckOutputBuffer() */
760 if (sLength > 100/2) { /* assume that every char must be escaped */
761 pBuff = malloc(sLength + 14 + 1);
763 strcpy(pBuff, commandBuf);
769 switch (*ps) { /* escape those things that throw off Tcl */
783 result = Tcl_Eval(interpreter, pBuff);
785 if (result != TCL_OK) {
787 "interpreter error \"%s\" at line %d executing:\n",
789 interpreter->errorLine);
790 fprintf(stderr, "\"%s\"\n", pBuff);
791 if (pBuff != commandBuf) free(pBuff);
794 if (pBuff != commandBuf) free(pBuff);
798 if ((CheckOutputBuffer(sLength)) == 0)
799 return EOF; /* out of space and can't grow the buffer */
801 strncpy(outputBuffer.current, s, sLength);
802 outputBuffer.current += sLength;
804 return sLength; /* arbitrary non-negative number */
807 /* ______________________________________________________________________ */
808 /* Figure out value of SDATA entity.
809 * We rememeber lookup hits in a "cache" (a shorter list), and look in
810 * cache before general list. Typically there will be LOTS of entries
811 * in the general list and only a handful in the hit list. Often, if an
812 * entity is used once, it'll be used again.
814 * Pointer to SDATA entity token in ESIS.
816 * Mapped value of the SDATA entity.
825 static Map_t *Hits; /* remember lookup hits */
827 /* SDL SDL SDL SDL --- special (i.e., hack); see below */
828 /* we're going to replace the "123456" below with the SDATA */
830 static char spcString[] = "<SPC NAME=\"[123456]\">\0";
831 static char spc[sizeof(spcString)];
834 /* If we have a hit list, check it. */
836 if ((v = FindMappingVal(Hits, s))) return v;
839 v = FindMappingVal(SDATAmap, s);
841 /* If mapping found, remember it, then return it. */
842 if ((v = FindMappingVal(SDATAmap, s))) {
843 if (!Hits) Hits = NewMap(IMS_sdatacache);
844 SetMappingNV(Hits, s, v);
848 /* SDL SDL SDL SDL --- special (i.e., hack)
849 Special case sdata values of six letters surrounded by square
850 brackets. Just convert them over to the SDL <SPC> stuff
852 if ((strlen(s) == 8) &&
855 if (strcmp(s, "[newlin]") == 0) {
858 strcpy(spc, spcString);
859 strncpy(spc+12, s+1, 6);
864 fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
868 /* ______________________________________________________________________ */
869 /* Add tag 'name' of length 'len' to list of tag names (if not there).
870 * This is a list of null-terminated strings so that we don't have to
871 * keep using the name length.
873 * Pointer to element name (GI) to remember.
875 * Pointer to the SAVED element name (GI).
884 static int n_alloc=0; /* number of slots allocated so far */
886 /* See if it's already in the list. */
887 for (i=0; i<nUsedElem; i++)
888 if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
891 /* Allocate slots in blocks of N, so we don't have to call malloc
894 n_alloc = IMS_elemnames;
895 Calloc(n_alloc, UsedElem, char *);
897 else if (nUsedElem >= n_alloc) {
898 n_alloc += IMS_elemnames;
899 Realloc(n_alloc, UsedElem, char *);
901 UsedElem[nUsedElem] = strdup(name);
902 return UsedElem[nUsedElem++];
904 /* ______________________________________________________________________ */
905 /* Add attrib name to list of attrib names (if not there).
906 * This is a list of null-terminated strings so that we don't have to
907 * keep using the name length.
909 * Pointer to attr name to remember.
911 * Pointer to the SAVED attr name.
920 static int n_alloc=0; /* number of slots allocated so far */
922 /* See if it's already in the list. */
923 for (i=0; i<nUsedAtt; i++)
924 if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
927 /* Allocate slots in blocks of N, so we don't have to call malloc
930 n_alloc = IMS_attnames;
931 Calloc(n_alloc, UsedAtt, char *);
933 else if (nUsedAtt >= n_alloc) {
934 n_alloc += IMS_attnames;
935 Realloc(n_alloc, UsedAtt, char *);
937 UsedAtt[nUsedAtt] = strdup(name);
938 return UsedAtt[nUsedAtt++];
941 /* ______________________________________________________________________ */
942 /* Find an element's attribute value given element pointer and attr name.
944 * a=FindAttByName("TYPE", t);
945 * do something with a->val;
947 * Pointer to element under consideration.
948 * Pointer to attribute name.
950 * Pointer to the value of the attribute.
962 for (i=0; i<e->natts; i++)
963 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
964 return &(e->atts[i]);
977 for (i=0; i<e->natts; i++)
978 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
979 return e->atts[i].sval;
983 /* ______________________________________________________________________ */
984 /* Find context of a tag, 'levels' levels up the tree.
985 * Space for string is passed by caller.
987 * Pointer to element under consideration.
988 * Number of levels to look up tree.
989 * String to write path into (provided by caller).
991 * Pointer to the provided string (for convenience of caller).
1005 if (!e) return NULL;
1008 for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
1009 if (i != 0) *s++ = ' ';
1017 /* ______________________________________________________________________ */
1018 /* Tests relationship (specified by argument/flag) between given element
1019 * (structure pointer) and named element.
1020 * Returns pointer to matching tag if found, null otherwise.
1022 * Pointer to element under consideration.
1023 * Pointer to name of elem whose relationsip we are trying to determine.
1024 * Relationship we are testing.
1026 * Pointer to the provided string (for convenience of caller).
1041 /* we'll call e the "given element" */
1045 if (!e->parent || !e->parent->gi) return 0;
1046 if (!strcmp(e->parent->gi, s)) return e->parent;
1049 for (i=0; i<e->necont; i++)
1050 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1053 if (!e->parent || !e->parent->gi) return 0;
1054 for (ep=e->parent; ep; ep=ep->parent)
1055 if (!strcmp(ep->gi, s)) return ep;
1057 case REL_Descendant:
1058 if (e->necont == 0) return 0;
1059 /* check immediate children first */
1060 for (i=0; i<e->necont; i++)
1061 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1062 /* then children's children (recursively) */
1063 for (i=0; i<e->necont; i++)
1064 if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
1068 if (!e->parent) return 0;
1070 for (i=0; i<ep->necont; i++)
1071 if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
1072 return ep->econt[i];
1075 if (!e->parent || e->my_eorder == 0) return 0;
1077 for (i=0; i<e->my_eorder; i++)
1078 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1080 case REL_ImmPreceding:
1081 if (!e->parent || e->my_eorder == 0) return 0;
1082 ep = e->parent->econt[e->my_eorder-1];
1083 if (!strcmp(s, ep->gi)) return ep;
1086 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1087 return 0; /* last? */
1089 for (i=(e->my_eorder+1); i<ep->necont; i++)
1090 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1092 case REL_ImmFollowing:
1093 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1094 return 0; /* last? */
1095 ep = e->parent->econt[e->my_eorder+1];
1096 if (!strcmp(s, ep->gi)) return ep;
1099 if (!e->parent) return 0;
1100 /* Now, see if element's parent has that thing as a child. */
1101 return QRelation(e->parent, s, REL_Child);
1103 case REL_Is1stContent:
1104 /* first confirm that our parent is an "s" */
1105 if (!(ep = QRelation(e, s, REL_Parent))) return 0;
1106 /* then check that we are the first content in that parent */
1107 if ((ep->cont->type == CMD_OPEN) && (ep->cont->ch.elem == e))
1110 case REL_HasOnlyContent:
1111 /* first confirm that we have a child of "s" */
1112 if (!(ep = QRelation(e, s, REL_Child))) return 0;
1113 /* then check that it is our only content */
1114 if (e->ncont == 1) return 0;
1118 fprintf(stderr, "You cannot query 'REL_None' or 'REL_Unknown'.\n");
1124 /* Given a relationship name (string), determine enum symbol for it.
1126 * Pointer to relationship name.
1135 if (!strcmp(relname, "?")) {
1136 fprintf(stderr, "Supported query/relationships %s\n%s\n%s.\n",
1137 "child, parent, ancestor, descendant,",
1138 "sibling, sibling+, sibling+1, sibling-, sibling-1,",
1139 "cousin, isfirstcon, hasonlycon");
1142 else if (StrEq(relname, "child")) return REL_Child;
1143 else if (StrEq(relname, "parent")) return REL_Parent;
1144 else if (StrEq(relname, "ancestor")) return REL_Ancestor;
1145 else if (StrEq(relname, "descendant")) return REL_Descendant;
1146 else if (StrEq(relname, "sibling")) return REL_Sibling;
1147 else if (StrEq(relname, "sibling-")) return REL_Preceding;
1148 else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
1149 else if (StrEq(relname, "sibling+")) return REL_Following;
1150 else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
1151 else if (StrEq(relname, "cousin")) return REL_Cousin;
1152 else if (StrEq(relname, "isfirstcon")) return REL_Is1stContent;
1153 else if (StrEq(relname, "hasonlycon")) return REL_HasOnlyContent;
1154 else fprintf(stderr, "Unknown relationship: %s\n", relname);
1158 /* ______________________________________________________________________ */
1159 /* This will descend the element tree in-order. (enter_f)() is called
1160 * upon entering the node. Then all children (data and child elements)
1161 * are operated on, calling either DescendTree() with a pointer to
1162 * the child element or (data_f)() for each non-element child node.
1163 * Before leaving the node (ascending), (leave_f)() is called. enter_f
1164 * and leave_f are passed a pointer to this node and data_f is passed
1165 * a pointer to the data/content (which includes the data itself and
1166 * type information). dp is an opaque pointer to any data the caller
1169 * Pointer to element under consideration.
1170 * Pointer to procedure to call when entering element.
1171 * Pointer to procedure to call when leaving element.
1172 * Pointer to procedure to call for each "chunk" of content data.
1173 * Void data pointer, passed to the avobe 3 procedures.
1186 if (enter_f) (enter_f)(e, dp);
1187 for (i=0; i<e->ncont; i++) {
1188 if (e->cont[i].type == CMD_OPEN)
1189 DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
1191 if (data_f) (data_f)(&e->cont[i], dp);
1193 if (leave_f) (leave_f)(e, dp);
1196 /* ______________________________________________________________________ */
1197 /* Add element, 'e', whose ID is 'idval', to a list of IDs.
1198 * This makes it easier to find an element by ID later.
1200 * Pointer to element under consideration.
1201 * Element's ID attribute value (a string).
1210 static ID_t *id_last;
1213 Malloc(1, id_last, ID_t);
1217 Malloc(1, id_last->next, ID_t);
1218 id_last = id_last->next;
1221 id_last->id = idval;
1224 /* ______________________________________________________________________ */
1225 /* Return pointer to element who's ID is given.
1227 * Element's ID attribute value (a string).
1229 * Pointer to element whose ID matches.
1238 for (id=IDList; id; id=id->next)
1239 if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
1243 /* ______________________________________________________________________ */