xref: /onnv-gate/usr/src/cmd/man/src/util/instant.src/tranvar.c (revision 0:68f95e015346)
1 /*
2  *  Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
3  *  All rights reserved.
4  */
5 /*
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7  * Copyright (c) 1994
8  * Open Software Foundation, Inc.
9  *
10  * Permission is hereby granted to use, copy, modify and freely distribute
11  * the software in this file and its documentation for any purpose without
12  * fee, provided that the above copyright notice appears in all copies and
13  * that both the copyright notice and this permission notice appear in
14  * supporting documentation.  Further, provided that the name of Open
15  * Software Foundation, Inc. ("OSF") not be used in advertising or
16  * publicity pertaining to distribution of the software without prior
17  * written permission from OSF.  OSF makes no representations about the
18  * suitability of this software for any purpose.  It is provided "as is"
19  * without express or implied warranty.
20  */
21 /*
22  * Copyright (c) 1996 X Consortium
23  * Copyright (c) 1995, 1996 Dalrymple Consulting
24  *
25  * Permission is hereby granted, free of charge, to any person obtaining a copy
26  * of this software and associated documentation files (the "Software"), to deal
27  * in the Software without restriction, including without limitation the rights
28  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29  * copies of the Software, and to permit persons to whom the Software is
30  * furnished to do so, subject to the following conditions:
31  *
32  * The above copyright notice and this permission notice shall be included in
33  * all copies or substantial portions of the Software.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
38  * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
39  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
40  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
41  * OTHER DEALINGS IN THE SOFTWARE.
42  *
43  * Except as contained in this notice, the names of the X Consortium and
44  * Dalrymple Consulting shall not be used in advertising or otherwise to
45  * promote the sale, use or other dealings in this Software without prior
46  * written authorization.
47  */
48 /* ________________________________________________________________________
49  *
50  *  instant - a program to manipulate SGML instances.
51  *
52  *  This module is for handling "special variables".  These act a lot like
53  *  procedure calls
54  * ________________________________________________________________________
55  */
56 
57 #ifndef lint
58 static char *RCSid =
59   "$Header: /usr/src/docbook-to-man/Instant/RCS/tranvar.c,v 1.8 1998/06/28 18:53:40 fld Exp $";
60 #endif
61 
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <ctype.h>
65 #include <string.h>
66 #include <memory.h>
67 #include <sys/types.h>
68 #include <errno.h>
69 
70 #include <tptregexp.h>
71 #include "general.h"
72 #include "translate.h"
73 
74 static char	**idrefs;		/* list of IDREF att names to follow */
75 static char	*def_idrefs[] = { "LINKEND", "LINKENDS", "IDREF", 0 };
76 static char	*each_A = 0;	/* last seen _eachatt */
77 static char	*each_C = 0;	/* last seen _eachcon */
78 
79 /* forward references */
80 void	ChaseIDRefs(Element_t *, char *, char *, FILE *);
81 void	Find(Element_t *, int, char **, FILE *);
82 void	GetIDREFnames();
83 
84 /* ______________________________________________________________________ */
85 /*  Handle "special" variable - read file, run command, do action, etc.
86  *  Arguments:
87  *	Name of special variable to expand.
88  *	Pointer to element under consideration.
89  *	FILE pointer to where to write output.
90  *	Flag saying whether to track the character position we're on
91  *	  (passed to OutputString).
92  */
93 
94 void
ExpandSpecialVar(char * name,Element_t * e,FILE * fp,int track_pos)95 ExpandSpecialVar(
96     char	*name,
97     Element_t	*e,
98     FILE	*fp,
99     int		track_pos
100 )
101 {
102     FILE	*infile;
103     char	buf[LINESIZE], *cp, *cp2, *atval;
104     char	**tok;
105     int		ntok, n, i, actioni;
106     char	*action, *action1;
107     Element_t	*ep;
108     Trans_t	*t, *tt;
109     Entity_t	*entp;
110 
111     /* Run a command.
112      * Format: _! command args ... */
113     if (*name == '!') {
114 	name++;
115 	if ((infile = popen(name, "r"))) {
116 	    while (fgets(buf, LINESIZE, infile)) fputs(buf, fp);
117 	    pclose(infile);
118 	    fflush(fp);
119 	}
120 	else {
121 	    fprintf(stderr, "Could not start program '%s': %s",
122 		name, strerror(errno));
123 	}
124 	return;
125     }
126 
127     /* See if caller wants one of the tokens from _eachatt or _eachcon.
128      * If so, output it and return.  (Yes, I admit that this is a hack.)
129      */
130     if (*name == 'A' && name[1] == EOS && each_A) {
131 	OutputString(each_A, fp, track_pos);
132 	return;
133     }
134     if (*name == 'C' && name[1] == EOS && each_C) {
135 	OutputString(each_C, fp, track_pos);
136 	return;
137     }
138 
139     ntok = 0;
140     tok = Split(name, &ntok, 0);
141 
142     /* Include another file.
143      * Format: _include filename */
144     if (StrEq(tok[0], "include")) {
145 	name = tok[1];
146 	if (ntok > 1 ) {
147 	    if ((infile=OpenFile(name)) == NULL) {
148 		sprintf(buf, "Can not open included file '%s'", name);
149 		perror(buf);
150 		return;
151 	    }
152 	    while (fgets(buf, LINESIZE, infile)) fputs(buf, fp);
153 	    fclose(infile);
154 	}
155 	else fprintf(stderr, "No file name specified for include\n");
156 	return;
157     }
158 
159     /* Print location (nearest title, line no, path).
160      * Format: _location */
161     else if (StrEq(tok[0], "location")) {
162 	PrintLocation(e, fp);
163     }
164 
165     /* Print path to this element.
166      * Format: _path */
167     else if (StrEq(tok[0], "path")) {
168 	(void)FindElementPath(e, buf);
169 	OutputString(buf, fp, track_pos);
170     }
171 
172     /* Print name of this element (gi).
173      * Format: _gi [M|L|U] */
174     else if (StrEq(tok[0], "gi")) {
175 	strcpy(buf, e->gi);
176 	if (ntok >= 2) {
177 	    if (*tok[1] == 'L' || *tok[1] == 'l' ||
178 		*tok[1] == 'M' || *tok[1] == 'm') {
179 		for (cp=buf; *cp; cp++)
180 		    if (isupper(*cp)) *cp = tolower(*cp);
181 	    }
182 	    if (*tok[1] == 'M' || *tok[1] == 'm')
183 		if (islower(buf[0])) buf[0] = toupper(buf[0]);
184 	}
185 	OutputString(buf, fp, track_pos);
186     }
187 
188     /* Print filename of this element's associated external entity.
189      * Format: _filename */
190     else if (StrEq(tok[0], "filename")) {
191     	if ( ntok >= 2 )	{
192 	    cp2 = FindAttValByName(e, tok[1]);
193 	    if ( ! (entp = FindEntity(cp2)) )	{
194 	    	fprintf(stderr, "Can't find entity named %s (via _filename expression):\n", tok[1]);
195 	    	PrintLocation(e, stderr);
196 	    	return;
197 	    }
198 	    OutputString(entp->sysid, fp, track_pos);
199 	} else	{
200 	    if (!e->entity) {
201 	    	fprintf(stderr, "Expected ext entity (element %s) - no ->entity (internal error? bug?):\n", e->gi);
202 	    	PrintLocation(e, stderr);
203 	    	return;
204 	    }
205 	    if (!e->entity->fname) {
206 	    	fprintf(stderr, "Expected filename (element %s) - no ->entity->fname (internal error? bug?):\n", e->gi);
207 	    	PrintLocation(e, stderr);
208 	    	return;
209 	    }
210 	    OutputString(e->entity->sysid, fp, track_pos);
211 	}
212     }
213 
214     /* Value of parent's attribute, by attr name.
215      * Format: _pattr attname */
216     else if (StrEq(tok[0], "pattr")) {
217 	ep = e->parent;
218 	if (!ep) {
219 	    fprintf(stderr, "Element does not have a parent:\n");
220 	    PrintLocation(ep, stderr);
221 	    return;
222 	}
223 	if ((atval = FindAttValByName(ep, tok[1]))) {
224 	    OutputString(atval, fp, track_pos);
225 	}
226     }
227 
228     /* Use an action, given transpec's SID.
229      * Format: _action action */
230     else if (StrEq(tok[0], "action")) {
231 	TranTByAction(e, tok[1], fp);
232     }
233 
234     /* Number of child elements of this element.
235      * Format: _nchild */
236     else if (StrEq(tok[0], "nchild")) {
237 	if (ntok > 1) {
238 	    for (n=0,i=0; i<e->necont; i++)
239 		if (StrEq(e->econt[i]->gi, tok[1])) n++;
240 	}
241 	else n = e->necont;
242 	sprintf(buf, "%d", n);
243 	OutputString(buf, fp, track_pos);
244     }
245 
246     /* number of 1st child's child elements (grandchildren from first child).
247      * Format: _n1gchild */
248     else if (StrEq(tok[0], "n1gchild")) {
249 	if (e->necont) {
250 	    sprintf(buf, "%d", e->econt[0]->necont);
251 	    OutputString(buf, fp, track_pos);
252 	}
253     }
254 
255     /* Chase this element's pointers until we hit the named GI.
256      * Do the action if it matches.
257      * Format: _chasetogi gi action */
258     else if (StrEq(tok[0], "chasetogi")) {
259 	if (ntok < 3) {
260 	    fprintf(stderr, "Error: Not enough args for _chasetogi.\n");
261 	    return;
262 	}
263 	actioni = atoi(tok[2]);
264 	if (actioni) ChaseIDRefs(e, tok[1], tok[2], fp);
265     }
266 
267     /* Follow link to element pointed to, then do action.
268      * Format: _followlink [attname] action. */
269     else if (StrEq(tok[0], "followlink")) {
270 	char **s;
271 	if (ntok > 2) {
272 	    if ((atval = FindAttValByName(e, tok[1]))) {
273 		if ((ep = FindElemByID(atval))) {
274 		    TranTByAction(ep, tok[2], fp);
275 		    return;
276 		}
277 	    }
278 	    else fprintf(stderr, "Error: Did not find attr: %s.\n", tok[1]);
279 	    return;
280 	}
281 	GetIDREFnames();
282 	for (s=idrefs; *s; s++) {
283 	    /* is this IDREF attr set? */
284 	    if ((atval = FindAttValByName(e, *s))) {
285 		ntok = 0;
286 		tok = Split(atval, &ntok, S_STRDUP);
287 		/* we'll follow the first one... */
288 		if ((ep = FindElemByID(tok[0]))) {
289 		    TranTByAction(ep, tok[1], fp);
290 		    return;
291 		}
292 		else fprintf(stderr, "Error: Can not find elem for ID: %s.\n",
293 			tok[0]);
294 	    }
295 	}
296 	fprintf(stderr, "Error: Element does not have IDREF attribute set:\n");
297 	PrintLocation(e, stderr);
298 	return;
299     }
300 
301     /* Starting at this element, decend tree (in-order), finding GI.
302      * Do the action if it matches.
303      * Format: _find args ... */
304     else if (StrEq(tok[0], "find")) {
305 	Find(e, ntok, tok, fp);
306     }
307 
308     /* Starting at this element's parent, decend tree (in-order), finding GI.
309      * Do the action if it matches.
310      * Format: _pfind args ... */
311     else if (StrEq(tok[0], "pfind")) {
312 	Find(e->parent ? e->parent : e, ntok, tok, fp);
313     }
314 
315     /* Content is supposed to be a list of IDREFs.  Follow each, doing action.
316      * If 2 actions are specified, use 1st for the 1st ID, 2nd for the rest.
317      * Format: _namelist action [action2] */
318     else if (StrEq(tok[0], "namelist")) {
319 	int id;
320 	action1 = tok[1];
321 	if (ntok > 2) action = tok[2];
322 	else action = action1;
323 	for (i=0; i<e->ndcont; i++) {
324 	    n = 0;
325 	    tok = Split(e->dcont[i], &n, S_STRDUP);
326 	    for (id=0; id<n; id++) {
327 		if (fold_case)
328 		    for (cp=tok[id]; *cp; cp++)
329 			if (islower(*cp)) *cp = toupper(*cp);
330 		if ((e = FindElemByID(tok[id]))) {
331 		    if (id) TranTByAction(e, action, fp);
332 		    else TranTByAction(e, action1, fp);	/* first one */
333 		}
334 		else fprintf(stderr, "Error: Can not find ID: %s.\n", tok[id]);
335 	    }
336 	}
337     }
338 
339     /* For each word in the element's content, do action.
340      * Format: _eachcon action [action] */
341     else if (StrEq(tok[0], "eachcon")) {
342 	int id;
343 	action1 = tok[1];
344 	if (ntok > 3) action = tok[2];
345 	else action = action1;
346 	for (i=0; i<e->ndcont; i++) {
347 	    n = 0;
348 	    tok = Split(e->dcont[i], &n, S_STRDUP|S_ALVEC);
349 	    for (id=0; id<n; id++) {
350 		each_C = tok[id];
351 		TranTByAction(e, action, fp);
352 	    }
353 	    free(*tok);
354 	}
355     }
356     /* For each word in the given attribute's value, do action.
357      * Format: _eachatt attname action [action] */
358     else if (StrEq(tok[0], "eachatt")) {
359 	int id;
360 	action1 = tok[2];
361 	if (ntok > 3) action = tok[3];
362 	else action = action1;
363 	if ((atval = FindAttValByName(e, tok[1]))) {
364 	    n = 0;
365 	    tok = Split(atval, &n, S_STRDUP|S_ALVEC);
366 	    for (id=0; id<n; id++) {
367 		each_A = tok[id];
368 		if (id) TranTByAction(e, action, fp);
369 		else TranTByAction(e, action1, fp);	/* first one */
370 	    }
371 	    free(*tok);
372 	}
373     }
374 
375     /* Do action on this element if element has [relationship] with gi.
376      * Format: _relation relationship gi action [action] */
377     else if (StrEq(tok[0], "relation")) {
378 	if (ntok >= 4) {
379 	    if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Current)) {
380 		/* action not done, see if alt action specified */
381 		if (ntok >= 5)
382 		    TranTByAction(e, tok[4], fp);
383 	    }
384 	}
385     }
386 
387     /* Do action on followed element if element has [relationship] with gi.
388      * Format: _followrel relationship gi action */
389     else if (StrEq(tok[0], "followrel")) {
390 	if (ntok >= 4)
391 	    (void)CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Related);
392     }
393 
394     /* Find element with matching ID and do action.  If action not specified,
395      * choose the right one appropriate for its context.
396      * Format: _id id [action] */
397     else if (StrEq(tok[0], "id")) {
398 	if ((ep = FindElemByID(tok[1]))) {
399 	    if (ntok > 2) TranTByAction(ep, tok[2], fp);
400 	    else {
401 		t = FindTrans(ep, 0);
402 		TransElement(ep, fp, t);
403 	    }
404 	}
405     }
406 
407     /* Set variable to value.
408      * Format: _set name value */
409     else if (StrEq(tok[0], "set")) {
410 	SetMappingNV(Variables, tok[1], tok[2]);
411     }
412 
413     /* Do action if variable is set, optionally to value.
414      * If not set, do nothing.
415      * Format: _isset varname [value] action
416      * Format: _issete varname [value] action  --  expands value */
417     else if (StrEq(tok[0], "isset") || StrEq(tok[0], "issete")) {
418 	if ((cp = FindMappingVal(Variables, tok[1]))) {
419 	    if (ntok == 3) TranTByAction(e, tok[2], fp);
420 	    else
421 	    if (ntok > 3)	{
422 	    	if ( StrEq(tok[0], "issete") )	{
423 			ExpandVariables(tok[2], buf, e);
424 			cp2 = buf;
425 		} else
426 			cp2 = tok[2];
427 	    	if ( !strcmp(cp, cp2))
428 			TranTByAction(e, tok[3], fp);
429 	    }
430 	}
431     }
432 
433     /* Insert a node into the tree at start/end, pointing to action to perform.
434      * Format: _insertnode S|E action */
435     else if (StrEq(tok[0], "insertnode")) {
436 	actioni = atoi(tok[2]);
437 	if (*tok[1] == 'S') e->gen_trans[0] = actioni;
438 	else if (*tok[1] == 'E') e->gen_trans[1] = actioni;
439     }
440 
441     /* Do an CALS DTD table spec for TeX or troff.  Looks through attributes
442      * and determines what to output. "check" means to check consistency,
443      * and print error messages.
444      * This is (hopefully) the only hard-coded part of instant.
445      *
446      * This was originally written for the OSF DTDs and recoded by FLD for
447      * CALS tables (since no one will ever use the OSF tables).  Although
448      * TeX was addressed first, it seems that a fresh approach was required,
449      * and so, tbl is the first to be really *fixed*.  Once tbl is stable,
450      * and there is a need for TeX again, that part will be recoded.
451      *
452      * *Obsolete* form (viz, for TeX):
453      *    Format: _calstable [clear|check|tex]
454      *			  [cellstart|cellend|rowstart|rowend|top|bottom]
455      *
456      * New, good form:
457      *
458      *    Format: _calstable [tbl]
459      *			  [tablestart|tableend|tablegroup|tablefoot|rowstart|
460      *			   rowend|entrystart|entryend]
461      */
462 
463     else if (StrEq(tok[0], "calstable")) {
464 	CALStable(e, fp, tok, ntok);
465     }
466 
467     /* Do action if element's attr is set, optionally to value.
468      * If not set, do nothing.
469      * Format: _attval att [value] action */
470     else if (StrEq(tok[0], "attval")) {
471 	if ((atval = FindAttValByName(e, tok[1]))) {
472 	    if (ntok == 3) TranTByAction(e, tok[2], fp);
473 	    else if (ntok > 3 && !strcmp(atval, tok[2]))
474 		TranTByAction(e, tok[3], fp);
475 	}
476     }
477     /* Same thing, but look at parent */
478     else if (StrEq(tok[0], "pattval")) {
479 	if ((atval = FindAttValByName(e->parent, tok[1]))) {
480 	    if (ntok == 3) {
481 		TranTByAction(e, tok[2], fp);
482 	    }
483 	    if (ntok > 3 && !strcmp(atval, tok[2]))
484 		TranTByAction(e, tok[3], fp);
485 	}
486     }
487 
488     /* Print each attribute and value for the current element, hopefully
489      * in a legal sgml form: <elem-name att1="value1" att2="value2:> .
490      * Format: _allatts */
491     else if (StrEq(tok[0], "allatts")) {
492 	for (i=0; i<e->natts; i++) {
493 	    if (i != 0) putc(' ', fp);
494 	    fputs(e->atts[i].name, fp);
495 	    fputs("=\"", fp);
496 	    fputs(e->atts[i].sval, fp);
497 	    putc('"', fp);
498 	}
499     }
500 
501     /* Print the element's input filename, and optionally, the line number.
502      * Format: _infile [line] */
503     else if (StrEq(tok[0], "infile")) {
504 	if (e->infile) {
505 	    if (ntok > 1 && !strcmp(tok[1], "root")) {
506 		strcpy(buf, e->infile);
507 		if ((cp = strrchr(buf, '.'))) *cp = EOS;
508 		fputs(buf, fp);
509 	    }
510 	    else {
511 		fputs(e->infile, fp);
512 		if (ntok > 1 && !strcmp(tok[1], "line"))
513 		    fprintf(fp, " %d", e->lineno);
514 	    }
515 	    return;
516 	}
517 	else fputs("input-file??", fp);
518     }
519 
520     /* Get value of an environement variable */
521     else if (StrEq(tok[0], "env")) {
522 	if (ntok > 1 && (cp = getenv(tok[1]))) {
523 	    OutputString(cp, fp, track_pos);
524 	}
525     }
526 
527     /* Something unknown */
528     else {
529 	fprintf(stderr, "Unknown special variable: %s\n", tok[0]);
530 	tt = e->trans;
531 	if (tt && tt->lineno)
532 	    fprintf(stderr, "Used in transpec, line %d\n", tt->lineno);
533     }
534     return;
535 }
536 
537 /* ______________________________________________________________________ */
538 /*  return the value for the special variables _A (last processed _eachatt)
539  *  and _C (last processed _eachcon)
540  */
541 
542 char *
Get_A_C_value(char * name)543 Get_A_C_value(char * name)
544 {
545     if ( !strcmp(name, "each_A") )	{
546 	if ( each_A )	{
547 	    return each_A;
548 	} else	{
549 	    fprintf(stderr, "Requested value for unset _A variable\n");
550 	}
551     } else
552     if ( !strcmp(name, "each_C") )	{
553 	if ( each_C )	{
554 	    return each_C;
555 	} else	{
556 	    fprintf(stderr, "Requested value for unset _C variable\n");
557 	}
558     } else	{
559 	fprintf(stderr, "Requested value for unknown special variable '%s'\n",
560 				name);
561     }
562     return "";
563 }
564 
565 /* ______________________________________________________________________ */
566 /*  Chase IDs until we find an element whose GI matches.  We also check
567  *  child element names, not just the names of elements directly pointed
568  *  at (by IDREF attributes).
569  */
570 
571 void
GetIDREFnames()572 GetIDREFnames()
573 {
574     char	*cp;
575 
576     if (!idrefs) {
577 	/* did user or transpec set the variable */
578 	if ((cp = FindMappingVal(Variables, "link_atts")))
579 	    idrefs = Split(cp, 0, S_STRDUP|S_ALVEC);
580 	else
581 	    idrefs = def_idrefs;
582     }
583 }
584 
585 /* ______________________________________________________________________ */
586 /*  Chase ID references - follow IDREF(s) attributes until we find
587  *  a GI named 'gi', then perform given action on that GI.
588  *  Arguments:
589  *	Pointer to element under consideration.
590  *	Name of GI we're looking for.
591  *	Spec ID of action to take.
592  *	FILE pointer to where to write output.
593  */
594 void
ChaseIDRefs(Element_t * e,char * gi,char * action,FILE * fp)595 ChaseIDRefs(
596     Element_t	*e,
597     char	*gi,
598     char *	action,
599     FILE	*fp
600 )
601 {
602     int		ntok, i, ei;
603     char	**tok, **s, *atval;
604 
605     /* First, see if we got what we came for with this element */
606     if (StrEq(e->gi, gi)) {
607 	TranTByAction(e, action, fp);
608 	return;
609     }
610     GetIDREFnames();
611 
612     /* loop for each attribute of type IDREF(s) */
613     for (s=idrefs; *s; s++) {
614 	/* is this IDREF attr set? */
615 	if ((atval = FindAttValByName(e, *s))) {
616 	    ntok = 0;
617 	    tok = Split(atval, &ntok, 0);
618 	    for (i=0; i<ntok; i++) {
619 		/* get element pointed to */
620 		if ((e = FindElemByID(tok[i]))) {
621 		    /* OK, we found a matching GI name */
622 		    if (StrEq(e->gi, gi)) {
623 			/* process using named action */
624 			TranTByAction(e, action, fp);
625 			return;
626 		    }
627 		    else {
628 			/* this elem itself did not match, try its children */
629 			for (ei=0; ei<e->necont; ei++) {
630 			    if (StrEq(e->econt[ei]->gi, gi)) {
631 				TranTByAction(e->econt[ei], action, fp);
632 				return;
633 			    }
634 			}
635 			/* try this elem's IDREF attributes */
636 			ChaseIDRefs(e, gi, action, fp);
637 			return;
638 		    }
639 		}
640 		else {
641 		    /* should not happen, since parser checks ID/IDREFs */
642 		    fprintf(stderr, "Error: Could not find ID %s\n", atval);
643 		}
644 	    }
645 	}
646     }
647     /* if the pointers didn't lead to the GI, give error */
648     if (!s)
649 	fprintf(stderr, "Error: Could not find '%s'\n", gi);
650 }
651 
652 /* ______________________________________________________________________ */
653 
654 /* state to pass to recursive routines - so we don't have to use
655  * global variables. */
656 typedef struct {
657     char	*gi;
658     char	*gi2;
659     char	action[10];
660     Element_t	*elem;
661     FILE	*fp;
662 } Descent_t;
663 
664 static void
tr_find_gi(Element_t * e,Descent_t * ds)665 tr_find_gi(
666     Element_t	*e,
667     Descent_t	*ds
668 )
669 {
670     if (StrEq(ds->gi, e->gi))
671 	if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
672 }
673 
674 static void
tr_find_gipar(Element_t * e,Descent_t * ds)675 tr_find_gipar(
676     Element_t	*e,
677     Descent_t	*ds
678 )
679 {
680     if (StrEq(ds->gi, e->gi) && e->parent &&
681 		StrEq(ds->gi2, e->parent->gi))
682 	if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
683 }
684 
685 static void
tr_find_attr(Element_t * e,Descent_t * ds)686 tr_find_attr(
687     Element_t	*e,
688     Descent_t	*ds
689 )
690 {
691     char	*atval;
692     if ((atval = FindAttValByName(e, ds->gi)) && StrEq(ds->gi2, atval))
693 	TranTByAction(e, ds->action, ds->fp);
694 }
695 
696 static void
tr_find_parent(Element_t * e,Descent_t * ds)697 tr_find_parent(
698     Element_t	*e,
699     Descent_t	*ds
700 )
701 {
702     if (QRelation(e, ds->gi, REL_Parent)) {
703 	if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
704     }
705 }
706 
707 /* ______________________________________________________________________ */
708 /*  Descend tree, finding elements that match criteria, then perform
709  *  given action.
710  *  Arguments:
711  *	Pointer to element under consideration.
712  *	Number of tokens in special variable.
713  *	Vector of tokens in special variable (eg, "find" "gi" "TITLE")
714  *	FILE pointer to where to write output.
715  */
716 void
Find(Element_t * e,int ac,char ** av,FILE * fp)717 Find(
718     Element_t	*e,
719     int		ac,
720     char	**av,
721     FILE	*fp
722 )
723 {
724     Descent_t	DS;		/* state passed to recursive routine */
725 
726     memset(&DS, 0, sizeof(Descent_t));
727     DS.elem = e;
728     DS.fp   = fp;
729 
730     /* see if we should start at the top of instance tree */
731     if (StrEq(av[1], "top")) {
732 	av++;
733 	ac--;
734 	e = DocTree;
735     }
736     if (ac < 4) {
737 	fprintf(stderr, "Bad '_find' specification - missing args.\n");
738 	return;
739     }
740     /* Find elem whose GI is av[2] */
741     if (StrEq(av[1], "gi")) {
742 	DS.gi     = av[2];
743 	strcpy(DS.action, av[3]);
744 	DescendTree(e, tr_find_gi, 0, 0, &DS);
745     }
746     /* Find elem whose GI is av[2] and whose parent GI is av[3] */
747     else if (StrEq(av[1], "gi-parent")) {
748 	DS.gi     = av[2];
749 	DS.gi2    = av[3];
750 	strcpy(DS.action, av[4]);
751 	DescendTree(e, tr_find_gipar, 0, 0, &DS);
752     }
753     /* Find elem whose parent GI is av[2] */
754     else if (StrEq(av[0], "parent")) {
755 	DS.gi     = av[2];
756 	strcpy(DS.action, av[3]);
757 	DescendTree(e, tr_find_parent, 0, 0, &DS);
758     }
759     /* Find elem whose attribute av[2] has value av[3] */
760     else if (StrEq(av[0], "attr")) {
761 	DS.gi     = av[2];
762 	DS.gi2    = av[3];
763 	strcpy(DS.action, av[4]);
764 	DescendTree(e, tr_find_attr, 0, 0, &DS);
765     }
766 }
767 
768 /* ______________________________________________________________________ */
769 
770