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>
81 #if !defined(CSRG_BASED)
87 /* forward references */
88 static char *LookupSDATA(char *);
89 static int CheckOutputBuffer(int length);
91 static OutputBuffer_t outputBuffer; /* init'd to all 0 by compiler */
93 /* ______________________________________________________________________ */
94 /* "Split" a string into tokens. Given a string that has space-separated
95 * (space/tab) tokens, return a pointer to an array of pointers to the
96 * tokens. Like what the shell does with *argv[]. The array can be is
97 * static or allocated. Space can be allocated for string, or allocated.
99 * Pointer to string to pick apart.
100 * Pointer to max number of tokens to find; actual number found is
101 * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
102 * code). If more tokens than the number specified, make last token be
103 * a single string composed of the rest of the tokens (includes spaces).
104 * Flag. Bit 0 says whether to make a copy of input string (since we'll
105 * clobber parts of it). To free the string, use the pointer to
106 * the first token returned by the function (or *ret_value).
107 * Bit 1 says whether to allocate the vector itself. If not, use
108 * (and return) a static vector.
110 * Pointer to the provided string (for convenience of caller).
115 char *s, /* input string */
116 int *ntok, /* # of tokens desired (input)/found (return) */
117 int flag /* dup string? allocate a vector? */
123 static char *local_tokens[100];
125 /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
129 if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
134 if (!s) return 0; /* no string */
136 /* Point to 1st token (there may be initial space) */
137 while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
138 if (*s == EOS) return 0; /* none found? */
140 /* See if caller wants us to copy the input string. */
141 if (flag & S_STRDUP) s = strdup(s);
143 /* See if caller wants us to allocate the returned vector. */
144 if (flag & S_ALVEC) {
146 Malloc(n_alloc, tokens, char *);
147 /* if caller did not specify max tokens to find, set to more than
148 * there will possibly ever be */
149 if (!ntok || !(*ntok)) maxnt = 10000;
151 else tokens = local_tokens;
153 i = 0; /* index into vector */
154 tokens[0] = s; /* s already points to 1st token */
156 tokens[i] = s; /* point vector member at start of token */
158 /* If we allocated vector, see if we need more space. */
159 if ((flag & S_ALVEC) && i >= n_alloc) {
161 Realloc(n_alloc, tokens, char *);
163 if (i >= maxnt) break; /* is this the last one? */
164 while (*s && !IsWhite(*s)) s++; /* skip past end of token */
165 if (*s == EOS) break; /* at end of input string? */
166 if (*s) *s++ = EOS; /* terminate token string */
167 while (*s && IsWhite(*s)) s++; /* skip space - to next token */
169 if (ntok) *ntok = i; /* return number of tokens found */
170 tokens[i] = 0; /* null-terminate vector */
174 /* ______________________________________________________________________ */
175 /* Mapping routines. These are used for name-value pairs, like attributes,
176 * variables, and counters. A "Map" is an opaque data structure used
177 * internally by these routines. The caller gets one when creating a new
178 * map, then hands it to other routines that need it. A "Mapping" is a
179 * name/value pair. The user has access to this.
180 * Here's some sample usage:
184 * SetMappingNV(V, "home", "/users/bowe");
185 * printf("Home: %s\n", FindMappingVal(V, "home");
188 /* Allocate new map structure. Only done once for each map/variable list.
190 * Number of initial slots to allocate space for. This is also the
191 * "chunk size" - how much to allocate when we use up the given space.
193 * Pointer to the (opaque) map structure. (User passes this to other
203 if (!slot_increment) slot_increment = 1;
204 M->slot_incr = slot_increment;
208 /* Given pointer to a Map and a name, find the mapping.
210 * Pointer to map structure (as returned by NewMap().
213 * Pointer to the matching mapping structure, or null if not found.
224 if (!M || M->n_used == 0) return NULL;
225 for (m=M->maps,i=0; i<M->n_used; i++)
226 if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
231 /* Given pointer to a Map and a name, return string value of the mapping.
233 * Pointer to map structure (as returned by NewMap().
236 * Pointer to the value (string), or null if not found.
245 if (!M || M->n_used == 0) return NULL;
246 if ((m = FindMapping(M, name))) return m->sval;
251 /* Set a mapping/variable in Map M. Input string is a name-value pair where
252 * there is some amount of space after the name. The correct mapping is done.
254 * Pointer to map structure (as returned by NewMap().
255 * Pointer to variable name (string).
256 * Pointer to variable value (string).
266 char buf[LINESIZE], *cp, *s;
270 /* First, look to see if it's a "well-known" variable. */
271 if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
272 if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
273 if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
275 m = FindMapping(M, name); /* find existing mapping (if set) */
277 /* Need more slots for mapping structures? Allocate in clumps. */
278 if (M->n_used == 0) {
279 M->n_alloc = M->slot_incr;
280 Malloc(M->n_alloc, M->maps, Mapping_t);
282 else if (M->n_used >= M->n_alloc) {
283 M->n_alloc += M->slot_incr;
284 Realloc(M->n_alloc, M->maps, Mapping_t);
287 /* OK, we have a string mapping */
288 if (m) { /* exists - just replace value */
290 m->sval = strdup(value);
291 if (value) m->sval = strdup(value);
295 if (name) { /* just in case */
296 m = &M->maps[M->n_used];
298 m->name = strdup(name);
299 if (value) m->sval = strdup(value);
306 /* See if the value is a command to run. If so, run the command
307 * and replace the value with the output.
311 s++; /* point to command */
312 if ((pp = popen(s, "r"))) { /* run cmd, read its output */
315 while (fgets(cp, LINESIZE-i, pp)) {
320 "Prog execution of variable '%s' too long.\n",
327 m->sval = strdup(buf);
331 sprintf(buf, "Could not start program '%s'", s);
338 /* Separate name and value from input string, then pass to SetMappingNV.
340 * Pointer to map structure (as returned by NewMap().
341 * Pointer to variable name and value (string), in form "name value".
353 fprintf(stderr, "SetMapping: Map not initialized.\n");
358 while (*val && !IsWhite(*val)) val++; /* point past end of name */
360 *val++ = EOS; /* terminate name */
361 while (*val && IsWhite(*val)) val++; /* point to value */
363 if (name) SetMappingNV(M, name, val);
366 /* ______________________________________________________________________ */
367 /* Opens a file for reading. If not found in current directory, try
368 * lib directories (from TPT_LIB env variable, or -l option).
372 * FILE pointer to open file, or null if it not found or can't open.
383 static char **libdirs;
384 static int nlibdirs = -1;
386 if ((fp=fopen(filename, "r"))) return fp;
388 if (*filename == '/') return NULL; /* full path specified? */
394 for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
396 libdirs = Split(s, &nlibdirs, S_ALVEC);
400 for (i=0; i<nlibdirs; i++) {
401 sprintf(buf, "%s/%s", libdirs[i], filename);
402 if ((fp=fopen(buf, "r"))) return fp;
407 /* ______________________________________________________________________ */
408 /* This will find the path to an tag. The format is the:
409 * tag1(n1):tag2(n2):tag3
410 * where the tags are going down the tree and the numbers indicate which
411 * child (the first is numbered 1) the next tag is.
412 * Returns pointer to the string just written to (so you can use this
413 * function as a printf arg).
415 * Pointer to element under consideration.
416 * String to write path into (provided by caller).
418 * Pointer to the provided string (for convenience of caller).
427 int i, e_path[MAX_DEPTH];
430 /* Move up the tree, noting "birth order" of each element encountered */
431 for (ep=e; ep->parent; ep=ep->parent)
432 e_path[ep->depth-1] = ep->my_eorder;
433 /* Move down the tree, printing the element names to the string. */
434 for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
435 sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
438 sprintf(cp, "%s", e->gi);
442 /* ______________________________________________________________________ */
443 /* Print some location info about a tag. Helps user locate error.
444 * Messages are indented 2 spaces (convention for multi-line messages).
446 * Pointer to element under consideration.
447 * FILE pointer of where to print.
456 char *s, buf[LINESIZE];
458 if (!e || !fp) return;
459 fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
460 if ((s=NearestOlderElem(e, "TITLE")))
461 fprintf(fp, " Position hint: TITLE='%s'\n", s);
464 fprintf(fp, " At or near instance file: %s, line: %d\n",
465 e->infile, e->lineno);
467 fprintf(fp, " At or near instance line: %d\n", e->lineno);
470 fprintf(fp, " ID: %s\n", e->id);
473 /* ______________________________________________________________________ */
474 /* Finds the data part of the nearest "older" tag (up the tree, and
475 * preceding) whose tag name matches the argument, or "TITLE", if null.
476 * Returns a pointer to the first chunk of character data.
478 * Pointer to element under consideration.
479 * Name (GI) of element we'll return data from.
481 * Pointer to that element's data content.
493 if (!name) name = "TITLE"; /* useful default */
495 for (; e->parent; e=e->parent) /* move up tree */
496 for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
498 if (!strcmp(name, ep->econt[i]->gi))
499 return ep->econt[i]->ndcont ?
500 ep->econt[i]->dcont[0] : "-empty-";
506 /* ______________________________________________________________________ */
507 /* Expands escaped strings in the input buffer (things like tabs, newlines,
508 * octal characters - using C style escapes) if outputting the buffer to
509 * the specified fp. If fp is NULL, we're only preparing the output
510 * for the interpreter so don't expand escaped strings. The hat/anchor
511 * character forces that position to appear at the beginning of a line.
512 * The cursor position is kept track of (optionally) so that this can be
515 * Pointer to element under consideration.
516 * FILE pointer of where to print.
517 * Flag saying whether or not to keep track of our position in the output
518 * stream. (We want to when writing to a file, but not for stderr.)
529 static int char_pos; /* remembers our character position */
530 static int interp_pos; /* like char_pos but when output is to interp */
531 int *ppos; /* points to appropriate line position var */
534 ppos = &char_pos; /* writing to file */
536 ppos = &interp_pos; /* buffer will be read by interpreter */
538 if (!s) s = "^"; /* no string - go to start of line */
541 if (fp && (*s == '\\')) { /* don't expand for interpreter */
543 if (track_pos) (*ppos)++;
545 default: c = *s; break;
547 case 's': c = ' '; break;
549 case 't': c = TAB; break;
551 case 'n': c = NL; *ppos = 0; break;
553 case 'r': c = CR; *ppos = 0; break;
555 case '0': case '1': case '2': case '3':
556 case '4': case '5': case '6': case '7':
557 /* for octal numbers (C style) of the form \012 */
559 for (s++; ((*s >= '0') && (*s <= '7')); s++)
560 c = (c << 3) + (*s - '0');
564 case '|': /* SDATA */
565 s++; /* point past \| */
567 /* find matching/closing \| */
569 while (*cp && *cp != '\\' && cp[1] != '|') cp++;
572 *cp = EOS; /* terminate sdata string */
574 s = cp; /* s now points to | */
576 cp = LookupSDATA(sdata);
577 if (cp) OutputString(cp, fp, track_pos);
579 /* not found - output sdata thing in brackets */
588 else { /* not escaped - just pass the character */
590 /* If caller wants us to track position, see if it's an anchor
591 * (ie, align at a newline). */
594 /* If we're already at the start of a line, don't do
595 * another newline. */
596 if (*ppos != 0) c = NL;
600 if (c == NL) *ppos = 0;
602 else if (c == ANCHOR) c = NL;
608 /* ______________________________________________________________________ */
609 /* resets the output buffer
611 void ClearOutputBuffer()
613 outputBuffer.current = outputBuffer.base;
616 /* ______________________________________________________________________ */
617 /* determines if there is already some text in the output buffer,
618 * returns 1 if so, else 0
620 int OutputBufferActive()
622 return (outputBuffer.current != outputBuffer.base);
625 /* ______________________________________________________________________ */
626 /* terminates output buffer with a null char and returns the buffer
628 char *GetOutputBuffer()
630 if (CheckOutputBuffer(1))
631 *(outputBuffer.current)++ = '\0';
633 return outputBuffer.base;
636 /* ______________________________________________________________________ */
637 /* insures that there's enough room in outputBuffer to hold a string
638 * of the given length.
639 * Arguments: the length of the string
641 static int CheckOutputBuffer(
646 int oldSize, incr = OBUF_INCR;
648 while (length > incr) incr += OBUF_INCR;
650 if ((outputBuffer.current - outputBuffer.base + length)
653 oldBase = outputBuffer.base;
654 oldSize = outputBuffer.size;
655 outputBuffer.size += incr;
658 realloc(outputBuffer.base, outputBuffer.size) :
659 malloc(outputBuffer.size);
660 if (outputBuffer.base == NULL) {
661 outputBuffer.base = oldBase;
662 outputBuffer.size = oldSize;
665 outputBuffer.current =
666 outputBuffer.base + (outputBuffer.current - oldBase);
673 /* ______________________________________________________________________ */
674 /* local version of fflush(3S)
676 * special cases a FILE of NULL to simply return success
679 int FFlush(FILE *stream)
681 if (stream) return fflush(stream);
686 /* ______________________________________________________________________ */
687 /* local version of putc(3S)
689 * special cases a FILE of NULL by working into a buffer for later
690 * use by the interpreter
692 * extra special hack: call Tcl interpreter with the character; worry
693 * about "stream" somo other time, we'll default to stdout
702 static char commandBuf[] = "OutputString \" ";
705 pc = &(commandBuf[14]);
706 switch (c) { /* escape those things that throw off tcl */
720 result = Tcl_Eval(interpreter, commandBuf);
722 if (result != TCL_OK) {
724 "interpreter error \"%s\" at line %d executing:\n",
726 interpreter->errorLine);
727 fprintf(stderr, "\"%s\"\n", commandBuf);
733 if ((CheckOutputBuffer(1)) == 0)
734 return EOF; /* out of space and can't grow the buffer */
736 *(outputBuffer.current)++ = (char) c;
741 /* ______________________________________________________________________ */
742 /* local version of fputs(3S)
744 * special cases a FILE of NULL by working into a buffer for later
745 * use by the interpreter
752 static char commandBuf[128] = "OutputString \"";
758 if ((sLength = strlen(s)) == 0)
759 return 0; /* no need to call CheckOutputBuffer() */
762 if (sLength > 100/2) { /* assume that every char must be escaped */
763 pBuff = malloc(sLength + 14 + 1);
765 strcpy(pBuff, commandBuf);
771 switch (*ps) { /* escape those things that throw off Tcl */
785 result = Tcl_Eval(interpreter, pBuff);
787 if (result != TCL_OK) {
789 "interpreter error \"%s\" at line %d executing:\n",
791 interpreter->errorLine);
792 fprintf(stderr, "\"%s\"\n", pBuff);
793 if (pBuff != commandBuf) free(pBuff);
796 if (pBuff != commandBuf) free(pBuff);
800 if ((CheckOutputBuffer(sLength)) == 0)
801 return EOF; /* out of space and can't grow the buffer */
803 strncpy(outputBuffer.current, s, sLength);
804 outputBuffer.current += sLength;
806 return sLength; /* arbitrary non-negative number */
809 /* ______________________________________________________________________ */
810 /* Figure out value of SDATA entity.
811 * We rememeber lookup hits in a "cache" (a shorter list), and look in
812 * cache before general list. Typically there will be LOTS of entries
813 * in the general list and only a handful in the hit list. Often, if an
814 * entity is used once, it'll be used again.
816 * Pointer to SDATA entity token in ESIS.
818 * Mapped value of the SDATA entity.
827 static Map_t *Hits; /* remember lookup hits */
829 /* SDL SDL SDL SDL --- special (i.e., hack); see below */
830 /* we're going to replace the "123456" below with the SDATA */
832 static char spcString[] = "<SPC NAME=\"[123456]\">\0";
833 static char spc[sizeof(spcString)];
836 /* If we have a hit list, check it. */
838 if ((v = FindMappingVal(Hits, s))) return v;
841 v = FindMappingVal(SDATAmap, s);
843 /* If mapping found, remember it, then return it. */
844 if ((v = FindMappingVal(SDATAmap, s))) {
845 if (!Hits) Hits = NewMap(IMS_sdatacache);
846 SetMappingNV(Hits, s, v);
850 /* SDL SDL SDL SDL --- special (i.e., hack)
851 Special case sdata values of six letters surrounded by square
852 brackets. Just convert them over to the SDL <SPC> stuff
854 if ((strlen(s) == 8) &&
857 if (strcmp(s, "[newlin]") == 0) {
860 strcpy(spc, spcString);
861 strncpy(spc+12, s+1, 6);
866 fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
870 /* ______________________________________________________________________ */
871 /* Add tag 'name' of length 'len' to list of tag names (if not there).
872 * This is a list of null-terminated strings so that we don't have to
873 * keep using the name length.
875 * Pointer to element name (GI) to remember.
877 * Pointer to the SAVED element name (GI).
886 static int n_alloc=0; /* number of slots allocated so far */
888 /* See if it's already in the list. */
889 for (i=0; i<nUsedElem; i++)
890 if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
893 /* Allocate slots in blocks of N, so we don't have to call malloc
896 n_alloc = IMS_elemnames;
897 Calloc(n_alloc, UsedElem, char *);
899 else if (nUsedElem >= n_alloc) {
900 n_alloc += IMS_elemnames;
901 Realloc(n_alloc, UsedElem, char *);
903 UsedElem[nUsedElem] = strdup(name);
904 return UsedElem[nUsedElem++];
906 /* ______________________________________________________________________ */
907 /* Add attrib name to list of attrib names (if not there).
908 * This is a list of null-terminated strings so that we don't have to
909 * keep using the name length.
911 * Pointer to attr name to remember.
913 * Pointer to the SAVED attr name.
922 static int n_alloc=0; /* number of slots allocated so far */
924 /* See if it's already in the list. */
925 for (i=0; i<nUsedAtt; i++)
926 if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
929 /* Allocate slots in blocks of N, so we don't have to call malloc
932 n_alloc = IMS_attnames;
933 Calloc(n_alloc, UsedAtt, char *);
935 else if (nUsedAtt >= n_alloc) {
936 n_alloc += IMS_attnames;
937 Realloc(n_alloc, UsedAtt, char *);
939 UsedAtt[nUsedAtt] = strdup(name);
940 return UsedAtt[nUsedAtt++];
943 /* ______________________________________________________________________ */
944 /* Find an element's attribute value given element pointer and attr name.
946 * a=FindAttByName("TYPE", t);
947 * do something with a->val;
949 * Pointer to element under consideration.
950 * Pointer to attribute name.
952 * Pointer to the value of the attribute.
964 for (i=0; i<e->natts; i++)
965 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
966 return &(e->atts[i]);
979 for (i=0; i<e->natts; i++)
980 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
981 return e->atts[i].sval;
985 /* ______________________________________________________________________ */
986 /* Find context of a tag, 'levels' levels up the tree.
987 * Space for string is passed by caller.
989 * Pointer to element under consideration.
990 * Number of levels to look up tree.
991 * String to write path into (provided by caller).
993 * Pointer to the provided string (for convenience of caller).
1007 if (!e) return NULL;
1010 for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
1011 if (i != 0) *s++ = ' ';
1019 /* ______________________________________________________________________ */
1020 /* Tests relationship (specified by argument/flag) between given element
1021 * (structure pointer) and named element.
1022 * Returns pointer to matching tag if found, null otherwise.
1024 * Pointer to element under consideration.
1025 * Pointer to name of elem whose relationsip we are trying to determine.
1026 * Relationship we are testing.
1028 * Pointer to the provided string (for convenience of caller).
1043 /* we'll call e the "given element" */
1047 if (!e->parent || !e->parent->gi) return 0;
1048 if (!strcmp(e->parent->gi, s)) return e->parent;
1051 for (i=0; i<e->necont; i++)
1052 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1055 if (!e->parent || !e->parent->gi) return 0;
1056 for (ep=e->parent; ep; ep=ep->parent)
1057 if (!strcmp(ep->gi, s)) return ep;
1059 case REL_Descendant:
1060 if (e->necont == 0) return 0;
1061 /* check immediate children first */
1062 for (i=0; i<e->necont; i++)
1063 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
1064 /* then children's children (recursively) */
1065 for (i=0; i<e->necont; i++)
1066 if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
1070 if (!e->parent) return 0;
1072 for (i=0; i<ep->necont; i++)
1073 if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
1074 return ep->econt[i];
1077 if (!e->parent || e->my_eorder == 0) return 0;
1079 for (i=0; i<e->my_eorder; i++)
1080 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1082 case REL_ImmPreceding:
1083 if (!e->parent || e->my_eorder == 0) return 0;
1084 ep = e->parent->econt[e->my_eorder-1];
1085 if (!strcmp(s, ep->gi)) return ep;
1088 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1089 return 0; /* last? */
1091 for (i=(e->my_eorder+1); i<ep->necont; i++)
1092 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
1094 case REL_ImmFollowing:
1095 if (!e->parent || e->my_eorder == (e->parent->necont-1))
1096 return 0; /* last? */
1097 ep = e->parent->econt[e->my_eorder+1];
1098 if (!strcmp(s, ep->gi)) return ep;
1101 if (!e->parent) return 0;
1102 /* Now, see if element's parent has that thing as a child. */
1103 return QRelation(e->parent, s, REL_Child);
1105 case REL_Is1stContent:
1106 /* first confirm that our parent is an "s" */
1107 if (!(ep = QRelation(e, s, REL_Parent))) return 0;
1108 /* then check that we are the first content in that parent */
1109 if ((ep->cont->type == CMD_OPEN) && (ep->cont->ch.elem == e))
1112 case REL_HasOnlyContent:
1113 /* first confirm that we have a child of "s" */
1114 if (!(ep = QRelation(e, s, REL_Child))) return 0;
1115 /* then check that it is our only content */
1116 if (e->ncont == 1) return 0;
1120 fprintf(stderr, "You cannot query 'REL_None' or 'REL_Unknown'.\n");
1126 /* Given a relationship name (string), determine enum symbol for it.
1128 * Pointer to relationship name.
1137 if (!strcmp(relname, "?")) {
1138 fprintf(stderr, "Supported query/relationships %s\n%s\n%s.\n",
1139 "child, parent, ancestor, descendant,",
1140 "sibling, sibling+, sibling+1, sibling-, sibling-1,",
1141 "cousin, isfirstcon, hasonlycon");
1144 else if (StrEq(relname, "child")) return REL_Child;
1145 else if (StrEq(relname, "parent")) return REL_Parent;
1146 else if (StrEq(relname, "ancestor")) return REL_Ancestor;
1147 else if (StrEq(relname, "descendant")) return REL_Descendant;
1148 else if (StrEq(relname, "sibling")) return REL_Sibling;
1149 else if (StrEq(relname, "sibling-")) return REL_Preceding;
1150 else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
1151 else if (StrEq(relname, "sibling+")) return REL_Following;
1152 else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
1153 else if (StrEq(relname, "cousin")) return REL_Cousin;
1154 else if (StrEq(relname, "isfirstcon")) return REL_Is1stContent;
1155 else if (StrEq(relname, "hasonlycon")) return REL_HasOnlyContent;
1156 else fprintf(stderr, "Unknown relationship: %s\n", relname);
1160 /* ______________________________________________________________________ */
1161 /* This will descend the element tree in-order. (enter_f)() is called
1162 * upon entering the node. Then all children (data and child elements)
1163 * are operated on, calling either DescendTree() with a pointer to
1164 * the child element or (data_f)() for each non-element child node.
1165 * Before leaving the node (ascending), (leave_f)() is called. enter_f
1166 * and leave_f are passed a pointer to this node and data_f is passed
1167 * a pointer to the data/content (which includes the data itself and
1168 * type information). dp is an opaque pointer to any data the caller
1171 * Pointer to element under consideration.
1172 * Pointer to procedure to call when entering element.
1173 * Pointer to procedure to call when leaving element.
1174 * Pointer to procedure to call for each "chunk" of content data.
1175 * Void data pointer, passed to the avobe 3 procedures.
1188 if (enter_f) (enter_f)(e, dp);
1189 for (i=0; i<e->ncont; i++) {
1190 if (e->cont[i].type == CMD_OPEN)
1191 DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
1193 if (data_f) (data_f)(&e->cont[i], dp);
1195 if (leave_f) (leave_f)(e, dp);
1198 /* ______________________________________________________________________ */
1199 /* Add element, 'e', whose ID is 'idval', to a list of IDs.
1200 * This makes it easier to find an element by ID later.
1202 * Pointer to element under consideration.
1203 * Element's ID attribute value (a string).
1212 static ID_t *id_last;
1215 Malloc(1, id_last, ID_t);
1219 Malloc(1, id_last->next, ID_t);
1220 id_last = id_last->next;
1223 id_last->id = idval;
1226 /* ______________________________________________________________________ */
1227 /* Return pointer to element who's ID is given.
1229 * Element's ID attribute value (a string).
1231 * Pointer to element whose ID matches.
1240 for (id=IDList; id; id=id->next)
1241 if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
1245 /* ______________________________________________________________________ */