1 /*
2 * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
3 * All rights reserved.
4 */
5 /*
6 * Copyright (c) 1994
7 * Open Software Foundation, Inc.
8 *
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.
19 */
20 /*
21 * Copyright (c) 1996 X Consortium
22 * Copyright (c) 1995, 1996 Dalrymple Consulting
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
38 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40 * OTHER DEALINGS IN THE SOFTWARE.
41 *
42 * Except as contained in this notice, the names of the X Consortium and
43 * Dalrymple Consulting shall not be used in advertising or otherwise to
44 * promote the sale, use or other dealings in this Software without prior
45 * written authorization.
46 */
47 /* ________________________________________________________________________
48 *
49 * General utility functions for 'instant' program. These are used
50 * throughout the rest of the program.
51 *
52 * Entry points for this module:
53 * Split(s, &n, flags) split string into n tokens
54 * NewMap(slot_incr) create a new mapping structure
55 * FindMapping(map, name) find mapping by name; return mapping
56 * FindMappingVal(map, name) find mapping by name; return value
57 * SetMapping(map, s) set mapping based on string
58 * OpenFile(filename) open file, looking in inst path
59 * FilePath(filename) find path to a file
60 * FindElementPath(elem, s) find path to element
61 * PrintLocation(ele, fp) print location of element in tree
62 * NearestOlderElem(elem, name) find prev elem up tree with name
63 * OutputString(s, fp, track_pos) output string
64 * AddElemName(name) add elem to list of known elements
65 * AddAttName(name) add att name to list of known atts
66 * FindAttByName(elem, name) find an elem's att by name
67 * FindContext(elem, lev, context) find context of elem
68 * QRelation(elem, name, rel_flag) find relation elem has to named elem
69 * DescendTree(elem, enter_f, leave_f, data_f, dp) descend doc tree,
70 * calling functions for each elem/node
71 * ________________________________________________________________________
72 */
73
74 #ifndef lint
75 static char *RCSid =
76 "$Header: /home/ncvs/src/usr.bin/sgmls/instant/util.c,v 1.1.1.1 1996/09/08 01:55:10 jfieber Exp $";
77 #endif
78
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <ctype.h>
82 #include <string.h>
83 #include <memory.h>
84 #include <sys/types.h>
85 #include <sys/stat.h>
86 #include <sys/file.h>
87 #include <errno.h>
88 #include <regex.h>
89 /* CSS don't have it and I don't see where it's used
90 #include <values.h>
91 */
92
93 #include "general.h"
94 #include "translate.h"
95
96 /* ______________________________________________________________________ */
97 /* "Split" a string into tokens. Given a string that has space-separated
98 * (space/tab) tokens, return a pointer to an array of pointers to the
99 * tokens. Like what the shell does with *argv[]. The array can be is
100 * static or allocated. Space can be allocated for string, or allocated.
101 * Arguments:
102 * Pointer to string to pick apart.
103 * Pointer to max number of tokens to find; actual number found is
104 * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
105 * code). If more tokens than the number specified, make last token be
106 * a single string composed of the rest of the tokens (includes spaces).
107 * Flag. Bit 0 says whether to make a copy of input string (since we'll
108 * clobber parts of it). To free the string, use the pointer to
109 * the first token returned by the function (or *ret_value).
110 * Bit 1 says whether to allocate the vector itself. If not, use
111 * (and return) a static vector.
112 * Return:
113 * Pointer to the provided string (for convenience of caller).
114 */
115
116 char **
Split(char * s,int * ntok,int flag)117 Split(
118 char *s, /* input string */
119 int *ntok, /* # of tokens desired (input)/found (return) */
120 int flag /* dup string? allocate a vector? */
121 )
122 {
123 int maxnt, i=0;
124 int n_alloc;
125 char **tokens;
126 static char *local_tokens[100];
127
128 /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
129 if (ntok == NULL)
130 maxnt = 100;
131 else {
132 if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
133 else maxnt = *ntok;
134 *ntok = 0;
135 }
136
137 if (!s) return 0; /* no string */
138
139 /* Point to 1st token (there may be initial space) */
140 while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
141 if (*s == EOS) return 0; /* none found? */
142
143 /* See if caller wants us to copy the input string. */
144 if (flag & S_STRDUP) s = strdup(s);
145
146 /* See if caller wants us to allocate the returned vector. */
147 if (flag & S_ALVEC) {
148 n_alloc = 20;
149 Malloc(n_alloc, tokens, char *);
150 /* if caller did not specify max tokens to find, set to more than
151 * there will possibly ever be */
152 if (!ntok || !(*ntok)) maxnt = 10000;
153 }
154 else tokens = local_tokens;
155
156 i = 0; /* index into vector */
157 tokens[0] = s; /* s already points to 1st token */
158 while (i<maxnt) {
159 tokens[i] = s; /* point vector member at start of token */
160 i++;
161 /* If we allocated vector, see if we need more space. */
162 if ((flag & S_ALVEC) && i >= n_alloc) {
163 n_alloc += 20;
164 Realloc(n_alloc, tokens, char *);
165 }
166 if (i >= maxnt) break; /* is this the last one? */
167 while (*s && !IsWhite(*s)) s++; /* skip past end of token */
168 if (*s == EOS) break; /* at end of input string? */
169 if (*s) *s++ = EOS; /* terminate token string */
170 while (*s && IsWhite(*s)) s++; /* skip space - to next token */
171 }
172 if (ntok) *ntok = i; /* return number of tokens found */
173 tokens[i] = 0; /* null-terminate vector */
174 return tokens;
175 }
176
177 /* ______________________________________________________________________ */
178 /* Mapping routines. These are used for name-value pairs, like attributes,
179 * variables, and counters. A "Map" is an opaque data structure used
180 * internally by these routines. The caller gets one when creating a new
181 * map, then hands it to other routines that need it. A "Mapping" is a
182 * name/value pair. The user has access to this.
183 * Here's some sample usage:
184 *
185 * Map *V;
186 * V = NewMap(20);
187 * SetMappingNV(V, "home", "/users/bowe");
188 * printf("Home: %s\n", FindMappingVal(V, "home");
189 */
190
191 /* Allocate new map structure. Only done once for each map/variable list.
192 * Arg:
193 * Number of initial slots to allocate space for. This is also the
194 * "chunk size" - how much to allocate when we use up the given space.
195 * Return:
196 * Pointer to the (opaque) map structure. (User passes this to other
197 * mapping routines.)
198 */
199 Map_t *
NewMap(int slot_increment)200 NewMap(
201 int slot_increment
202 )
203 {
204 Map_t *M;
205 Calloc(1, M, Map_t);
206 /* should really do the memset's in Calloc/Malloc/Realloc
207 macros, but that will have to wait until time permits -CSS */
208 memset((char *)M, 0, sizeof(Map_t));
209 if (!slot_increment) slot_increment = 1;
210 M->slot_incr = slot_increment;
211 return M;
212 }
213
214 /* Given pointer to a Map and a name, find the mapping.
215 * Arguments:
216 * Pointer to map structure (as returned by NewMap().
217 * Variable name.
218 * Return:
219 * Pointer to the matching mapping structure, or null if not found.
220 */
221 Mapping_t *
FindMapping(Map_t * M,const char * name)222 FindMapping(
223 Map_t *M,
224 const char *name
225 )
226 {
227 int i;
228 Mapping_t *m;
229
230 if (!M || M->n_used == 0) return NULL;
231 for (m=M->maps,i=0; i<M->n_used; i++)
232 if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
233 return NULL;
234
235 }
236
237 /* Given pointer to a Map and a name, return string value of the mapping.
238 * Arguments:
239 * Pointer to map structure (as returned by NewMap().
240 * Variable name.
241 * Return:
242 * Pointer to the value (string), or null if not found.
243 */
244 char *
FindMappingVal(Map_t * M,const char * name)245 FindMappingVal(
246 Map_t *M,
247 const char *name
248 )
249 {
250 Mapping_t *m;
251
252 if ( !strcmp(name, "each_A") || !strcmp(name, "each_C") ) {
253 return Get_A_C_value(name);
254 }
255
256 /*
257 if (!M || M->n_used == 0) return NULL;
258 if ((m = FindMapping(M, name))) return m->sval;
259 return NULL;
260 */
261 if (!M || M->n_used == 0) {
262 return NULL;
263 }
264 if ((m = FindMapping(M, name))) {
265 return m->sval;
266 }
267 return NULL;
268
269 }
270
271 /* Set a mapping/variable in Map M. Input string is a name-value pair where
272 * there is some amount of space after the name. The correct mapping is done.
273 * Arguments:
274 * Pointer to map structure (as returned by NewMap().
275 * Pointer to variable name (string).
276 * Pointer to variable value (string).
277 */
278 void
SetMappingNV(Map_t * M,const char * name,const char * value)279 SetMappingNV(
280 Map_t *M,
281 const char *name,
282 const char *value
283 )
284 {
285 FILE *pp;
286 char buf[LINESIZE], *cp;
287 int i;
288 Mapping_t *m;
289
290 /* First, look to see if it's a "well-known" variable. */
291 if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
292 if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
293 if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
294
295 m = FindMapping(M, name); /* find existing mapping (if set) */
296
297 /* OK, we have a string mapping */
298 if (m) { /* exists - just replace value */
299 free(m->sval);
300 if (value) m->sval = strdup(value);
301 else m->sval = NULL;
302 }
303 else {
304 if (name) { /* just in case */
305 /* Need more slots for mapping structures? Allocate in clumps. */
306 if (M->n_used == 0) {
307 M->n_alloc = M->slot_incr;
308 Malloc(M->n_alloc, M->maps, Mapping_t);
309 }
310 else if (M->n_used >= M->n_alloc) {
311 M->n_alloc += M->slot_incr;
312 Realloc(M->n_alloc, M->maps, Mapping_t);
313 }
314
315 m = &M->maps[M->n_used];
316 M->n_used++;
317 m->name = strdup(name);
318 if (value) m->sval = strdup(value);
319 else m->sval = NULL;
320 }
321 }
322
323 if (value)
324 {
325 /* See if the value is a command to run. If so, run the command
326 * and replace the value with the output.
327 */
328 if (*value == '!') {
329 if ((pp = popen(value+1, "r"))) { /* run cmd, read its output */
330 i = 0;
331 cp = buf;
332 while (fgets(cp, LINESIZE-i, pp)) {
333 i += strlen(cp);
334 cp = &buf[i];
335 if (i >= LINESIZE) {
336 fprintf(stderr,
337 "Prog execution of variable '%s' too long.\n",
338 m->name);
339 break;
340 }
341 }
342 free(m->sval);
343 stripNL(buf);
344 m->sval = strdup(buf);
345 pclose(pp);
346 }
347 else {
348 sprintf(buf, "Could not start program '%s'", value+1);
349 perror(buf);
350 }
351 }
352 }
353 }
354
355 /* Separate name and value from input string, then pass to SetMappingNV.
356 * Arguments:
357 * Pointer to map structure (as returned by NewMap().
358 * Pointer to variable name and value (string), in form "name value".
359 */
360 void
SetMapping(Map_t * M,const char * s)361 SetMapping(
362 Map_t *M,
363 const char *s
364 )
365 {
366 char buf[LINESIZE];
367 char *name, *val;
368
369 if (!M) {
370 fprintf(stderr, "SetMapping: Map not initialized.\n");
371 return;
372 }
373 strcpy(buf, s);
374 name = val = buf;
375 while (*val && !IsWhite(*val)) val++; /* point past end of name */
376 if (*val) {
377 *val++ = EOS; /* terminate name */
378 while (*val && IsWhite(*val)) val++; /* point to value */
379 }
380 if (name) SetMappingNV(M, name, val);
381 }
382
383 /* ______________________________________________________________________ */
384 /* Opens a file for reading. If not found in current directory, try
385 * lib directories (from TPT_LIB env variable, or -l option).
386 * Arguments:
387 * Filename (string).
388 * Return:
389 * FILE pointer to open file, or null if it not found or can't open.
390 */
391
392 FILE *
OpenFile(char * filename)393 OpenFile(
394 char *filename
395 )
396 {
397 FILE *fp;
398
399 filename = FilePath(filename);
400 if ((fp=fopen(filename, "r"))) return fp;
401 return NULL;
402 }
403
404 /* ______________________________________________________________________ */
405 /* Opens a file for reading. If not found in current directory, try
406 * lib directories (from TPT_LIB env variable, or -l option).
407 * Arguments:
408 * Filename (string).
409 * Return:
410 * FILE pointer to open file, or null if it not found or can't open.
411 */
412
413 char *
FilePath(char * filename)414 FilePath(
415 char *filename
416 )
417 {
418 FILE *fp;
419 static char buf[LINESIZE];
420 int i;
421 static char **libdirs;
422 static int nlibdirs = -1;
423
424 if ((fp=fopen(filename, "r")))
425 {
426 fclose(fp);
427 strncpy(buf, filename, LINESIZE);
428 return buf;
429 }
430
431 if (*filename == '/') return NULL; /* full path specified? */
432
433 if (nlibdirs < 0) {
434 char *cp, *s;
435 if (tpt_lib) {
436 s = strdup(tpt_lib);
437 for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
438 nlibdirs = 0;
439 libdirs = Split(s, &nlibdirs, S_ALVEC);
440 }
441 else nlibdirs = 0;
442 }
443 for (i=0; i<nlibdirs; i++) {
444 sprintf(buf, "%s/%s", libdirs[i], filename);
445 if ((fp=fopen(buf, "r")))
446 {
447 fclose(fp);
448 return buf;
449 }
450 }
451 return NULL;
452 }
453
454 /* ______________________________________________________________________ */
455 /* This will find the path to an tag. The format is the:
456 * tag1(n1):tag2(n2):tag3
457 * where the tags are going down the tree and the numbers indicate which
458 * child (the first is numbered 1) the next tag is.
459 * Returns pointer to the string just written to (so you can use this
460 * function as a printf arg).
461 * Arguments:
462 * Pointer to element under consideration.
463 * String to write path into (provided by caller).
464 * Return:
465 * Pointer to the provided string (for convenience of caller).
466 */
467 char *
FindElementPath(Element_t * e,char * s)468 FindElementPath(
469 Element_t *e,
470 char *s
471 )
472 {
473 Element_t *ep;
474 int i, e_path[MAX_DEPTH];
475 char *cp;
476
477 /* Move up the tree, noting "birth order" of each element encountered */
478 for (ep=e; ep; ep=ep->parent)
479 e_path[ep->depth-1] = ep->my_eorder;
480 /* Move down the tree, printing the element names to the string. */
481 for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
482 sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
483 cp += strlen(cp);
484 }
485 sprintf(cp, "%s", e->gi);
486 return s;
487 }
488
489 /* ______________________________________________________________________ */
490 /* Print some location info about a tag. Helps user locate error.
491 * Messages are indented 2 spaces (convention for multi-line messages).
492 * Arguments:
493 * Pointer to element under consideration.
494 * FILE pointer of where to print.
495 */
496
497 void
PrintLocation(Element_t * e,FILE * fp)498 PrintLocation(
499 Element_t *e,
500 FILE *fp
501 )
502 {
503 char *s, buf[LINESIZE];
504
505 if (!e || !fp) return;
506 fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
507 if ((s=NearestOlderElem(e, "TITLE")))
508 fprintf(fp, " Position hint: TITLE='%s'\n", s);
509 if (e->lineno) {
510 if (e->infile)
511 fprintf(fp, " At or near instance file: %s, line: %d\n",
512 e->infile, e->lineno);
513 else
514 fprintf(fp, " At or near instance line: %d\n", e->lineno);
515 }
516 if (e->id)
517 fprintf(fp, " ID: %s\n", e->id);
518 }
519
520 /* ______________________________________________________________________ */
521 /* Finds the data part of the nearest "older" tag (up the tree, and
522 * preceding) whose tag name matches the argument, or "TITLE", if null.
523 * Returns a pointer to the first chunk of character data.
524 * Arguments:
525 * Pointer to element under consideration.
526 * Name (GI) of element we'll return data from.
527 * Return:
528 * Pointer to that element's data content.
529 */
530 char *
NearestOlderElem(Element_t * e,char * name)531 NearestOlderElem(
532 Element_t *e,
533 char *name
534 )
535 {
536 int i;
537 Element_t *ep;
538
539 if (!e) return 0;
540 if (!name) name = "TITLE"; /* useful default */
541
542 for (; e->parent; e=e->parent) /* move up tree */
543 for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
544 ep = e->parent;
545 if (!strcmp(name, ep->econt[i]->gi))
546 return ep->econt[i]->ndcont ?
547 ep->econt[i]->dcont[0] : "-empty-";
548 }
549
550 return NULL;
551 }
552
553 /* ______________________________________________________________________ */
554 /* Expands escaped strings in the input buffer (things like tabs, newlines,
555 * octal characters - using C style escapes).
556 */
557
ExpandString(char * s)558 char *ExpandString(
559 char *s
560 )
561 {
562 char c, *sdata, *cp, *ns;
563 int len, pos, addn;
564
565 if (!s) return s;
566
567 len = strlen(s);
568 pos = 0;
569 Malloc(len + 1, ns, char);
570 ns[pos] = EOS;
571
572 for ( ; *s; s++) {
573 c = *s;
574 cp = NULL;
575
576 /* Check for escaped characters from sgmls. */
577 if (*s == '\\') {
578 s++;
579 switch (*s) {
580 case 'n':
581 c = NL;
582 break;
583
584 case '\\':
585 c = '\\';
586 break;
587
588 case '0': case '1': case '2': case '3':
589 case '4': case '5': case '6': case '7':
590 /* for octal numbers (C style) of the form \012 */
591 c = *s++ - '0';
592 if (*s >= '0' && *s <= '7') {
593 c = c * 8 + (*s++ - '0');
594 if (*s >= '0' && *s <= '7')
595 c = c * 8 + (*s - '0');
596 }
597 break;
598
599 case '|': /* SDATA */
600 s++; /* point past \| */
601 sdata = s;
602 /* find matching/closing \| */
603 cp = s;
604 while (*cp && *cp != '\\' && cp[1] != '|')
605 cp++;
606 if (!*cp)
607 break;
608
609 *cp = EOS; /* terminate sdata string */
610 cp++;
611 s = cp; /* s now points to | */
612
613 cp = LookupSDATA(sdata);
614 if (!cp)
615 cp = sdata;
616 c = 0;
617 break;
618
619 /* This shouldn't happen. */
620 default:
621 s--;
622 break;
623 }
624 }
625
626 /* Check for character re-mappings. */
627 if (nCharMap && c) {
628 int i;
629
630 for (i = 0; i < nCharMap; i++) {
631 if (c != CharMap[i].name[0])
632 continue;
633 cp = CharMap[i].sval;
634 c = 0;
635 break;
636 }
637 }
638
639 /* See if there is enough space for the data. */
640 /* XXX this should be MUCH smarter about predicting
641 how much extra memory it should allocate */
642 if (c)
643 addn = 1;
644 else
645 addn = strlen(cp);
646
647 /* If not, make some. */
648 if (addn > len - pos) {
649 len += addn - (len - pos);
650 Realloc(len + 1, ns, char);
651 }
652
653 /* Then copy the data. */
654 if (c)
655 ns[pos] = c;
656 else
657 strcpy(&ns[pos], cp);
658
659 pos += addn;
660 ns[pos] = EOS;
661 }
662 return(ns);
663 }
664
665 /* ______________________________________________________________________ */
666 /* Expands escaped strings in the input buffer (things like tabs, newlines,
667 * octal characters - using C style escapes) and outputs buffer to specified
668 * fp. The hat/anchor character forces that position to appear at the
669 * beginning of a line. The cursor position is kept track of (optionally)
670 * so that this can be done.
671 * Arguments:
672 * Pointer to element under consideration.
673 * FILE pointer of where to print.
674 * Flag saying whether or not to keep track of our position in the output
675 * stream. (We want to when writing to a file, but not for stderr.)
676 */
677
678 void
OutputString(char * s,FILE * fp,int track_pos)679 OutputString(
680 char *s,
681 FILE *fp,
682 int track_pos
683 )
684 {
685 char c;
686 static int char_pos = 0; /* remembers our character position */
687 char *p;
688
689 if (!fp) return;
690 if (!s) s = "^"; /* no string - go to start of line */
691
692 for (p = s; *p; p++) {
693 c = *p;
694 /* If caller wants us to track position, see if it's an anchor
695 * (ie, align at a newline). */
696 if (track_pos) {
697 if (c == ANCHOR && (p == s || *(p + 1) == EOS)) {
698 /* If we're already at the start of a line, don't do
699 * another newline. */
700 if (char_pos != 0) c = NL;
701 else c = 0;
702 }
703 else char_pos++;
704 if (c == NL) char_pos = 0;
705 }
706 else if (c == ANCHOR && (p == s || *(p + 1) == EOS)) c = NL;
707 if (c) putc(c, fp);
708 }
709 }
710
711 /* ______________________________________________________________________ */
712 /* Figure out value of SDATA entity.
713 * We rememeber lookup hits in a "cache" (a shorter list), and look in
714 * cache before general list. Typically there will be LOTS of entries
715 * in the general list and only a handful in the hit list. Often, if an
716 * entity is used once, it'll be used again.
717 * Arguments:
718 * Pointer to SDATA entity token in ESIS.
719 * Return:
720 * Mapped value of the SDATA entity.
721 */
722
723 char *
LookupSDATA(char * s)724 LookupSDATA(
725 char *s
726 )
727 {
728 char *v;
729 static Map_t *Hits; /* remember lookup hits */
730
731 /* If we have a hit list, check it. */
732 if (Hits) {
733 if ((v = FindMappingVal(Hits, s))) return v;
734 }
735
736 v = FindMappingVal(SDATAmap, s);
737
738 /* If mapping found, remember it, then return it. */
739 if ((v = FindMappingVal(SDATAmap, s))) {
740 if (!Hits) Hits = NewMap(IMS_sdatacache);
741 SetMappingNV(Hits, s, v);
742 return v;
743 }
744
745 fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
746 return NULL;
747 }
748
749 /* ______________________________________________________________________ */
750 /* Add tag 'name' of length 'len' to list of tag names (if not there).
751 * This is a list of null-terminated strings so that we don't have to
752 * keep using the name length.
753 * Arguments:
754 * Pointer to element name (GI) to remember.
755 * Return:
756 * Pointer to the SAVED element name (GI).
757 */
758
759 char *
AddElemName(char * name)760 AddElemName(
761 char *name
762 )
763 {
764 int i;
765 static int n_alloc=0; /* number of slots allocated so far */
766
767 /* See if it's already in the list. */
768 for (i=0; i<nUsedElem; i++)
769 if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
770 return UsedElem[i];
771
772 /* Allocate slots in blocks of N, so we don't have to call malloc
773 * so many times. */
774 if (n_alloc == 0) {
775 n_alloc = IMS_elemnames;
776 Calloc(n_alloc, UsedElem, char *);
777 }
778 else if (nUsedElem >= n_alloc) {
779 n_alloc += IMS_elemnames;
780 Realloc(n_alloc, UsedElem, char *);
781 }
782 UsedElem[nUsedElem] = strdup(name);
783 return UsedElem[nUsedElem++];
784 }
785 /* ______________________________________________________________________ */
786 /* Add attrib name to list of attrib names (if not there).
787 * This is a list of null-terminated strings so that we don't have to
788 * keep using the name length.
789 * Arguments:
790 * Pointer to attr name to remember.
791 * Return:
792 * Pointer to the SAVED attr name.
793 */
794
795 char *
AddAttName(char * name)796 AddAttName(
797 char *name
798 )
799 {
800 int i;
801 static int n_alloc=0; /* number of slots allocated so far */
802
803 /* See if it's already in the list. */
804 for (i=0; i<nUsedAtt; i++)
805 if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
806 return UsedAtt[i];
807
808 /* Allocate slots in blocks of N, so we don't have to call malloc
809 * so many times. */
810 if (n_alloc == 0) {
811 n_alloc = IMS_attnames;
812 Calloc(n_alloc, UsedAtt, char *);
813 }
814 else if (nUsedAtt >= n_alloc) {
815 n_alloc += IMS_attnames;
816 Realloc(n_alloc, UsedAtt, char *);
817 }
818 UsedAtt[nUsedAtt] = strdup(name);
819 return UsedAtt[nUsedAtt++];
820 }
821
822 /* ______________________________________________________________________ */
823 /* Find an element's attribute value given element pointer and attr name.
824 * Typical use:
825 * a=FindAttByName("TYPE", t);
826 * do something with a->val;
827 * Arguments:
828 * Pointer to element under consideration.
829 * Pointer to attribute name.
830 * Return:
831 * Pointer to the value of the attribute.
832 */
833
834 /*
835 Mapping_t *
836 FindAttByName(
837 Element_t *e,
838 char *name
839 )
840 {
841 int i;
842 if (!e) return NULL;
843 for (i=0; i<e->natts; i++)
844 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
845 return &(e->atts[i]);
846 return NULL;
847 }
848 */
849
850 char *
FindAttValByName(Element_t * e,char * name)851 FindAttValByName(
852 Element_t *e,
853 char *name
854 )
855 {
856 int i;
857 if (!e) return NULL;
858 for (i=0; i<e->natts; i++)
859 if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
860 return e->atts[i].sval;
861 return NULL;
862 }
863
864 /* ______________________________________________________________________ */
865 /* Find context of a tag, 'levels' levels up the tree.
866 * Space for string is passed by caller.
867 * Arguments:
868 * Pointer to element under consideration.
869 * Number of levels to look up tree.
870 * String to write path into (provided by caller).
871 * Return:
872 * Pointer to the provided string (for convenience of caller).
873 */
874
875 char *
FindContext(Element_t * e,int levels,char * con)876 FindContext(
877 Element_t *e,
878 int levels,
879 char *con
880 )
881 {
882 char *s;
883 Element_t *ep;
884 int i;
885
886 if (!e) return NULL;
887 s = con;
888 *s = EOS;
889 for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
890 if (i != 0) *s++ = ' ';
891 strcpy(s, ep->gi);
892 s += strlen(s);
893 }
894 return con;
895 }
896
897
898 /* ______________________________________________________________________ */
899 /* Tests relationship (specified by argument/flag) between given element
900 * (structure pointer) and named element.
901 * Returns pointer to matching tag if found, null otherwise.
902 * Arguments:
903 * Pointer to element under consideration.
904 * Pointer to name of elem whose relationsip we are trying to determine.
905 * Relationship we are testing.
906 * Return:
907 * Pointer to the provided string (for convenience of caller).
908 */
909
910 Element_t *
QRelation(Element_t * e,char * s,Relation_t rel)911 QRelation(
912 Element_t *e,
913 char *s,
914 Relation_t rel
915 )
916 {
917 int i;
918 Element_t *ep;
919
920 if (!e) return 0;
921
922 /* we'll call e the "given element" */
923 switch (rel)
924 {
925 case REL_Parent:
926 if (!e->parent || !e->parent->gi) return 0;
927 if (!strcmp(e->parent->gi, s)) return e->parent;
928 break;
929 case REL_Child:
930 for (i=0; i<e->necont; i++)
931 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
932 break;
933 case REL_Ancestor:
934 if (!e->parent || !e->parent->gi) return 0;
935 for (ep=e->parent; ep; ep=ep->parent)
936 if (!strcmp(ep->gi, s)) return ep;
937 break;
938 case REL_Descendant:
939 if (e->necont == 0) return 0;
940 /* check immediate children first */
941 for (i=0; i<e->necont; i++)
942 if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
943 /* then children's children (recursively) */
944 for (i=0; i<e->necont; i++)
945 if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
946 return ep;
947 break;
948 case REL_Sibling:
949 if (!e->parent) return 0;
950 ep = e->parent;
951 for (i=0; i<ep->necont; i++)
952 if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
953 return ep->econt[i];
954 break;
955 case REL_Preceding:
956 if (!e->parent || e->my_eorder == 0) return 0;
957 ep = e->parent;
958 for (i=0; i<e->my_eorder; i++)
959 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
960 break;
961 case REL_ImmPreceding:
962 if (!e->parent || e->my_eorder == 0) return 0;
963 ep = e->parent->econt[e->my_eorder-1];
964 if (!strcmp(s, ep->gi)) return ep;
965 break;
966 case REL_Following:
967 if (!e->parent || e->my_eorder == (e->parent->necont-1))
968 return 0; /* last? */
969 ep = e->parent;
970 for (i=(e->my_eorder+1); i<ep->necont; i++)
971 if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
972 break;
973 case REL_ImmFollowing:
974 if (!e->parent || e->my_eorder == (e->parent->necont-1))
975 return 0; /* last? */
976 ep = e->parent->econt[e->my_eorder+1];
977 if (!strcmp(s, ep->gi)) return ep;
978 break;
979 case REL_Cousin:
980 if (!e->parent) return 0;
981 /* Now, see if element's parent has that thing as a child. */
982 return QRelation(e->parent, s, REL_Child);
983 break;
984 case REL_None:
985 case REL_Unknown:
986 fprintf(stderr, "You can not query 'REL_None' or 'REL_Unknown'.\n");
987 break;
988 }
989 return NULL;
990 }
991
992 /* Given a relationship name (string), determine enum symbol for it.
993 * Arguments:
994 * Pointer to relationship name.
995 * Return:
996 * Relation_t enum.
997 */
998 Relation_t
FindRelByName(char * relname)999 FindRelByName(
1000 char *relname
1001 )
1002 {
1003 if (!strcmp(relname, "?")) {
1004 fprintf(stderr, "Supported query/relationships %s\n%s.\n",
1005 "child, parent, ancestor, descendant,",
1006 "sibling, sibling+, sibling+1, sibling-, sibling-1");
1007 return REL_None;
1008 }
1009 else if (StrEq(relname, "child")) return REL_Child;
1010 else if (StrEq(relname, "parent")) return REL_Parent;
1011 else if (StrEq(relname, "ancestor")) return REL_Ancestor;
1012 else if (StrEq(relname, "descendant")) return REL_Descendant;
1013 else if (StrEq(relname, "sibling")) return REL_Sibling;
1014 else if (StrEq(relname, "sibling-")) return REL_Preceding;
1015 else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
1016 else if (StrEq(relname, "sibling+")) return REL_Following;
1017 else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
1018 else if (StrEq(relname, "cousin")) return REL_Cousin;
1019 else fprintf(stderr, "Unknown relationship: %s\n", relname);
1020 return REL_Unknown;
1021 }
1022
1023 /* ______________________________________________________________________ */
1024 /* This will descend the element tree in-order. (enter_f)() is called
1025 * upon entering the node. Then all children (data and child elements)
1026 * are operated on, calling either DescendTree() with a pointer to
1027 * the child element or (data_f)() for each non-element child node.
1028 * Before leaving the node (ascending), (leave_f)() is called. enter_f
1029 * and leave_f are passed a pointer to this node and data_f is passed
1030 * a pointer to the data/content (which includes the data itself and
1031 * type information). dp is an opaque pointer to any data the caller
1032 * wants to pass.
1033 * Arguments:
1034 * Pointer to element under consideration.
1035 * Pointer to procedure to call when entering element.
1036 * Pointer to procedure to call when leaving element.
1037 * Pointer to procedure to call for each "chunk" of content data.
1038 * Void data pointer, passed to the avobe 3 procedures.
1039 */
1040
1041 void
DescendTree(Element_t * e,void (* enter_f)(),void (* leave_f)(),void (* data_f)(),void * dp)1042 DescendTree(
1043 Element_t *e,
1044 void (*enter_f)(),
1045 void (*leave_f)(),
1046 void (*data_f)(),
1047 void *dp
1048 )
1049 {
1050 int i;
1051 if (enter_f) (enter_f)(e, dp);
1052 for (i=0; i<e->ncont; i++) {
1053 if (e->cont[i].type == CMD_OPEN)
1054 DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
1055 else
1056 if (data_f) (data_f)(&e->cont[i], dp);
1057 }
1058 if (leave_f) (leave_f)(e, dp);
1059 }
1060
1061 /* ______________________________________________________________________ */
1062 /* Add element, 'e', whose ID is 'idval', to a list of IDs.
1063 * This makes it easier to find an element by ID later.
1064 * Arguments:
1065 * Pointer to element under consideration.
1066 * Element's ID attribute value (a string).
1067 */
1068
1069 void
AddID(Element_t * e,char * idval)1070 AddID(
1071 Element_t *e,
1072 char *idval
1073 )
1074 {
1075 static ID_t *id_last;
1076
1077 if (!IDList) {
1078 Malloc(1, id_last, ID_t);
1079 IDList = id_last;
1080 }
1081 else {
1082 Malloc(1, id_last->next, ID_t);
1083 id_last = id_last->next;
1084 }
1085 id_last->elem = e;
1086 id_last->id = idval;
1087 }
1088
1089 /* ______________________________________________________________________ */
1090 /* Return pointer to element who's ID is given.
1091 * Arguments:
1092 * Element's ID attribute value (a string).
1093 * Return:
1094 * Pointer to element whose ID matches.
1095 */
1096
1097 Element_t *
FindElemByID(char * idval)1098 FindElemByID(
1099 char *idval
1100 )
1101 {
1102 ID_t *id;
1103 for (id=IDList; id; id=id->next)
1104 if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
1105 return 0;
1106 }
1107
1108 /* ______________________________________________________________________ */
1109
1110