xref: /onnv-gate/usr/src/cmd/man/src/util/instant.src/util.c (revision 425:fbaa857e997e)
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