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