1 /*
2  *  Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
3  *  All rights reserved.
4  */
5 /*
6  * Copyright (c) 1994
7  * Open Software Foundation, Inc.
8  *
9  * Permission is hereby granted to use, copy, modify and freely distribute
10  * the software in this file and its documentation for any purpose without
11  * fee, provided that the above copyright notice appears in all copies and
12  * that both the copyright notice and this permission notice appear in
13  * supporting documentation.  Further, provided that the name of Open
14  * Software Foundation, Inc. ("OSF") not be used in advertising or
15  * publicity pertaining to distribution of the software without prior
16  * written permission from OSF.  OSF makes no representations about the
17  * suitability of this software for any purpose.  It is provided "as is"
18  * without express or implied warranty.
19  */
20 /*
21  * Copyright (c) 1996 X Consortium
22  * Copyright (c) 1995, 1996 Dalrymple Consulting
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy
25  * of this software and associated documentation files (the "Software"), to deal
26  * in the Software without restriction, including without limitation the rights
27  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28  * copies of the Software, and to permit persons to whom the Software is
29  * furnished to do so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in
32  * all copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
37  * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
38  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40  * OTHER DEALINGS IN THE SOFTWARE.
41  *
42  * Except as contained in this notice, the names of the X Consortium and
43  * Dalrymple Consulting shall not be used in advertising or otherwise to
44  * promote the sale, use or other dealings in this Software without prior
45  * written authorization.
46  */
47 /* ________________________________________________________________________
48  *
49  *  Program to manipulate SGML instances.
50  *
51  *  This module is for "translating" an instance to another form, usually
52  *  suitable for a formatting application.
53  *
54  *  Entry points for this module:
55  *	DoTranslate(elem, fp)
56  *      ExpandVariables(in, out, e)
57  * ________________________________________________________________________
58  */
59 
60 #ifndef lint
61 static char *RCSid =
62   "$Header: /home/ncvs/src/usr.bin/sgmls/instant/translate.c,v 1.1.1.1 1996/09/08 01:55:10 jfieber Exp $";
63 #endif
64 
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <ctype.h>
68 #include <string.h>
69 #include <memory.h>
70 #include <sys/types.h>
71 #include <errno.h>
72 #include <regex.h>
73 
74 #include "general.h"
75 #define STORAGE
76 #include "translate.h"
77 
78 static Trans_t	NullTrans;		/* an empty one */
79 
80 /* forward references */
81 void	ProcesOutputSpec(char *, Element_t *, FILE *, int);
82 static void	WasProcessed(Element_t *);
83 
84 /* ______________________________________________________________________ */
85 /* minimal compatibility wrapper for UNIX V8 regexp, match only
86  */
87 
v8_regexec(const regex_t * re,const char * string)88 static int v8_regexec(const regex_t *re, const char *string)
89 {
90 	if (re == NULL)
91 		return 0;
92 	return !regexec(re, string, 0, NULL, 0);
93 }
94 #define regexec	v8_regexec
95 
96 /* ______________________________________________________________________ */
97 /*  Translate the subtree starting at 'e'. Output goes to 'fp'.
98  *  This is the entry point for translating an instance.
99  *  Arguments:
100  *	Pointer to element under consideration.
101  *	FILE pointer to where to write output.
102  */
103 
104 void
DoTranslate(Element_t * e,FILE * fp)105 DoTranslate(
106     Element_t	*e,
107     FILE	*fp
108 )
109 {
110     Trans_t	*t, *tn;
111 
112     /* Find transpec for each node. */
113     DescendTree(e, PrepTranspecs, 0, 0, 0);
114 
115     /* Stuff to do at start of processing */
116     if ((t = FindTransByName("_Start"))) {
117 	if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
118 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
119 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
120 	if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
121     }
122 
123     /* Translate topmost/first element.  This is recursive. */
124     TransElement(e, fp, NULL);
125 
126     /* Stuff to do at end of processing */
127     if ((t = FindTransByName("_End"))) {
128 	if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
129 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
130 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
131 	if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
132     }
133 
134     /* Warn about unprocessed elements in this doc tree, if verbose mode. */
135     if (verbose)
136 	DescendTree(e, WasProcessed, 0, 0, 0);
137 
138     /* Clean up. This is not yet complete, which is no big deal (since the
139      * program is normally done at this point anyway.  */
140     for (t=TrSpecs; t; ) {
141 	tn = t->next;
142 	/* free the contents of t here ... */
143 	(void)free((void* )t);
144 	t = tn;
145     }
146     TrSpecs = 0;
147 }
148 
149 /* ______________________________________________________________________ */
150 /*  Print warning about unprocessed elements in this doc tree (if they
151  *  were not explicitely ignored).
152  *  Arguments:
153  *	Pointer to element under consideration.
154  */
155 static void
WasProcessed(Element_t * e)156 WasProcessed(
157     Element_t	*e
158 )
159 {
160     Trans_t	*t;
161     t = e->trans;
162     if (!e->processed && (t && !t->ignore)) {
163 	fprintf(stderr, "Warning: element '%s' was not processed:\n", e->gi);
164 	PrintLocation(e, stderr);
165     }
166 }
167 
168 /* ______________________________________________________________________ */
169 /*  For each element find transpec.
170  *  Arguments:
171  *	Pointer to element under consideration.
172  */
173 void
PrepTranspecs(Element_t * e)174 PrepTranspecs(
175     Element_t	*e
176 )
177 {
178     Trans_t	*t;
179     t = FindTrans(e, 0);
180     e->trans = t;
181 }
182 
183 /* ______________________________________________________________________ */
184 /*  Copy a buffer/string into another, expanding regular variables and immediate
185  *  variables. (Special variables are done later.)
186  *  Arguments:
187  *	Pointer to string to expand.
188  *	Pointer to expanded string. (return)
189  *	Pointer to element under consideration.
190  */
191 void
ExpandVariables(char * in,char * out,Element_t * e)192 ExpandVariables(
193     char	*in,
194     char	*out,
195     Element_t	*e
196 )
197 {
198     register int i, j;
199     char	*ip, *vp, *op;
200     char	*def_val, *s, *atval, *modifier;
201     char	vbuf[500];
202     int		lev;
203 
204     ip = in;
205     op = out;
206     while (*ip) {
207 	/* start of regular variable? */
208 	if (*ip == '$' && *(ip+1) == L_CURLY && *(ip+2) != '_') {
209 	    ip++;
210 	    ip++;		/* point at variable name */
211 	    vp = vbuf;
212 	    /*	Look for matching (closing) curly. (watch for nesting)
213 	     *	We store the variable content in a tmp buffer, so we don't
214 	     *	clobber the input buffer.
215 	     */
216 	    lev = 0;
217 	    while (*ip) {
218 		if (*ip == L_CURLY) lev++;
219 		if (*ip == R_CURLY) {
220 		    if (lev == 0) {
221 			ip++;
222 			break;
223 		    }
224 		    else lev--;
225 		}
226 		*vp++ = *ip++;	/* copy to variable buffer */
227 	    }
228 	    *vp = EOS;
229 	    /* vbuf now contains the variable name (stuff between curlys). */
230 	    if (lev != 0) {
231 		fprintf(stderr, "Botched variable use: %s\n", in);
232 		/* copy rest of string if we can't recover  ?? */
233 		return;
234 	    }
235 	    /* Now, expand variable. */
236 	    vp = vbuf;
237 
238 	    /* Check for immediate variables -- like _special variables but
239 	     * interpreted right now.  These start with a "+" */
240 
241 	    if ( *vp == '+' )	{
242 
243 	    	if ( ! strcmp(vp, "+content") )	{
244 	    	    for ( i=0;  i<e->ncont; i++ )	{
245 	    	    	if ( IsContData(e, i) )	{
246 	    	    	    j = strlen(ContData(e,i));
247 	    	    	    memcpy(op, ContData(e,i), j);
248 	    	    	    op += j;
249 	    	    	} else	{
250 	    	    	    fprintf(stderr, "warning: ${+current} skipped element content\n");
251 	    	    	}
252 	    	    }
253 
254 	    	} else	{
255 	    	    fprintf(stderr, "unknown immediate variable: %s\n", vp);
256 	    	}
257 
258 	    } else	{
259 
260 		/* See if this variable has a default [ format: ${varname def} ] */
261 
262 		def_val = vp;
263 		while (*def_val && *def_val != ' ') def_val++;
264 		if (*def_val) *def_val++ = EOS;
265 		else def_val = 0;
266 		/* def_val now points to default, if it exists, null if not. */
267 
268 		modifier = vp;
269 		while (*modifier && *modifier != ':') modifier++;
270 		if (*modifier) *modifier++ = EOS;
271 		else modifier = 0;
272 		/* modifier now points to modifier if it exists, null if not. */
273 
274 		s = 0;
275 		/* if attribute of current elem with this name found, use value */
276 		if (e && (atval = FindAttValByName(e, vp)))
277 		    s = atval;
278 	   	 else	/* else try for (global) variable with this name */
279 		    s = FindMappingVal(Variables, vp);
280 
281 		/* If we found a value, copy it to the output buffer. */
282 
283 		if (s) {
284 		    if ( modifier && *modifier == 'l' ) {
285 			while (*s) {
286 			    *op = tolower(*s);
287 			    op++, *s++;
288 			}
289 		    } else
290 			while (*s) *op++ = *s++;
291 		} else
292 		if (def_val)	{
293 		    while (*def_val) *op++ = *def_val++;
294 		}
295 	    }
296 	    continue;
297 	}
298 	*op++ = *ip++;
299     }
300     *op = EOS;		/* terminate string */
301 }
302 
303 /* ______________________________________________________________________ */
304 /*  Process an "output" translation spec - one of StartText, EndText,
305  *  Replace, Message.  (These are the ones that produce output.)
306  *  Steps done:
307  *	Expand attributes and regular varaibles in input string.
308  *	Pass thru string, accumulating chars to be sent to output stream.
309  *	If we find the start of a special variable, output what we've
310  *	  accumulated, then find the special variable's "bounds" (ie, the
311  *	  stuff between the curly brackets), and expand that by passing to
312  *	  ExpandSpecialVar().  Continue until done the input string.
313  *  Arguments:
314  *	Input buffer (string) to be expanded and output.
315  *	Pointer to element under consideration.
316  *	FILE pointer to where to write output.
317  *	Flag saying whether to track the character position we're on
318  *	  (passed to OutputString).
319  */
320 void
ProcesOutputSpec(char * ib,Element_t * e,FILE * fp,int track_pos)321 ProcesOutputSpec(
322     char	*ib,
323     Element_t	*e,
324     FILE	*fp,
325     int		track_pos
326 )
327 {
328     char	obuf[LINESIZE];
329     char	vbuf[LINESIZE];
330     char	*dest, vname[LINESIZE], *cp;
331     int		esc;
332 
333     obuf[0] = EOS;			/* start with empty output buffer */
334 
335     ExpandVariables(ib, vbuf, e);	/* expand regular variables */
336     ib = vbuf;
337     dest = obuf;
338 
339     esc = 0;
340     while (*ib) {
341 	/* If not a $, it's a regular char.  Just copy it and go to next. */
342 	if (*ib != '$') {		/* look for att/variable marker */
343 	    *dest++ = *ib++;		/* it's not. just copy character */
344 	    continue;
345 	}
346 
347 	/* We have a $.  What we have must be a "special variable" since
348 	 * regular variables have already been expanded, or just a lone $. */
349 
350 	if (ib[1] != L_CURLY) {	/* just a stray dollar sign (no variable) */
351 	    *dest++ = *ib++;
352 	    continue;
353 	}
354 
355 	ib++;				/* point past $ */
356 
357 	/* Output what we have in buffer so far. */
358 	*dest = EOS;			/* terminate string */
359 	if (obuf[0]) OutputString(obuf, fp, track_pos);
360 	dest = obuf;			/* ready for new stuff in buffer */
361 
362 	if (!strchr(ib, R_CURLY)) {
363 	    fprintf(stderr, "Mismatched braces in TranSpec: %s\n", ib);
364 	    /* how do we recover from this? */
365 	}
366 	ib++;
367 	cp = vname;
368 	while (*ib && *ib != R_CURLY) *cp++ = *ib++;
369 	*cp = EOS;			/* terminate att/var name */
370 	ib++;				/* point past closing curly */
371 	/* we now have special variable name (stuff in curly {}'s) in vname */
372 	ExpandSpecialVar(&vname[1], e, fp, track_pos);
373     }
374     *dest = EOS;			/* terminate string in output buffer */
375 
376     if (obuf[0]) OutputString(obuf, fp, track_pos);
377 }
378 
379 /* ______________________________________________________________________ */
380 /*  Find the translation spec for the given tag.
381  *  Returns pointer to first spec that matches (name, depth, etc., of tag).
382  *  Arguments:
383  *	e -- Pointer to element under consideration.
384  *	specID -- name of specid that we're looking for
385  *  Return:
386  *	Pointer to translation spec that matches given element's context.
387  */
388 
389 Trans_t *
FindTrans(Element_t * e,int specID)390 FindTrans(
391     Element_t	*e,
392     int		specID
393 )
394 {
395     char	context[LINESIZE], buf[LINESIZE], *cp, **vec, *atval;
396     int		i, a, match;
397     Trans_t	*t, *tt;
398 
399     /* loop through all transpecs */
400     for (t=TrSpecs; t; t=t->next)
401     {
402 	/* Only one of gi or gilist will be set. */
403 	/* Check if elem name matches */
404 	if (t->gi && !StrEq(t->gi, e->gi) && !specID) continue;
405 
406 	/* test if we're looking for a specific specID and then if
407 	 * this is it.. */
408 	if (specID)
409 	    if (!t->my_id || (specID != t->my_id))
410 		continue;
411 
412 	/* Match one in the list of GIs? */
413 	if (t->gilist) {
414 	    for (match=0,vec=t->gilist; *vec; vec++) {
415 		if (StrEq(*vec, e->gi)) {
416 		    match = 1;
417 		    break;
418 		}
419 	    }
420 	    if (!match) continue;
421 	}
422 
423 	/* Check context */
424 
425 	/* Special case of context */
426 	if (t->parent)
427 	    if (!QRelation(e, t->parent, REL_Parent)) continue;
428 
429 	if (t->context) {	/* no context specified -> a match */
430 	    FindContext(e, t->depth, context);
431 
432 	    /* If reg expr set, do regex compare; else just string compare. */
433 	    if (t->context_re) {
434 		if (! regexec(t->context_re, context)) continue;
435 	    }
436 	    else {
437 		/* Is depth of spec deeper than element's depth? */
438 		if (t->depth > e->depth) continue;
439 
440 		/* See if context of element matches "context" of transpec */
441 		match = ( (t->context[0] == context[0]) &&
442 			    !strcmp(t->context, context) );
443 		if (!match) continue;
444 	    }
445 	}
446 
447 	/* Check attributes.  Loop through list, comparing each. */
448 	if (t->nattpairs) {	/* no att specified -> a match */
449 	    for (match=1,a=0; a<t->nattpairs; a++) {
450 		if (!(atval = FindAttValByName(e, t->attpair[a].name))) {
451 		    match = 0;
452 		    break;
453 		}
454 		if (!regexec(t->attpair[a].rex, atval)) match = 0;
455 	    }
456 	    if (!match) continue;
457 	}
458 
459 	/* Check relationships:  child, parent, ancestor, sib, ...  */
460 	if (t->relations) {
461 	    Mapping_t *r;
462 	    match = 1;
463 	    for (r=t->relations->maps,i=0; i<t->relations->n_used; i++) {
464 		if (!CheckRelation(e, r[i].name, r[i].sval, 0, 0, RA_Current)) {
465 		    match = 0;
466 		    break;
467 		}
468 	    }
469 	    if (!match) continue;
470 	}
471 
472 	/* check this element's parent's attribute */
473 	if (t->pattrset && e->parent) {
474 	    char *p, **tok;
475 
476 	    i = 2;
477 	    match = 1;
478 	    tok = Split(t->pattrset, &i, S_STRDUP);
479 	    if ( i == 2 ) {
480 		p = FindAttValByName(e->parent, tok[0]);
481 		ExpandVariables(tok[1], buf, 0);
482 		if ( !p || strcmp(p, buf) )
483 		    match = 0;
484 	    } else {
485 		if (!FindAttValByName(e->parent, t->pattrset))
486 		    match = 0;
487 	    }
488 	    free(tok[0]);
489 	    if (!match) continue;
490 	}
491 
492 	/* check this element's "birth order" */
493 	if (t->nth_child) {
494 	    /* First one is called "1" by the user.  Internally called "0". */
495 	    i = t->nth_child;
496 	    if (i > 0) {	/* positive # -- count from beginning */
497 		if (e->my_eorder != (i-1)) continue;
498 	    }
499 	    else {		/* negative # -- count from end */
500 		i = e->parent->necont - i;
501 		if (e->my_eorder != i) continue;
502 	    }
503 	}
504 
505 	/* check that variables match */
506 	if (t->var_name) {
507 	    cp = FindMappingVal(Variables, t->var_name);
508 	    if (!cp || strcmp(cp, t->var_value)) continue;
509 	}
510 
511 	/* check for variable regular expression match */
512 	if ( t->var_RE_name )	{
513 	    cp = FindMappingVal(Variables, t->var_RE_name);
514 	    if (!cp || !regexec(t->var_RE_value, cp)) continue;
515 	}
516 
517 	/* check content */
518 	if (t->content) {		/* no att specified -> a match */
519 	    for (match=0,i=0; i<e->ndcont; i++) {
520 		if (regexec(t->content_re, e->dcont[i])) {
521 		    match = 1;
522 		    break;
523 		}
524 	    }
525 	    if (!match) continue;
526 	}
527 
528 	/* -------- at this point we've passed all criteria -------- */
529 
530 	/* See if we should be using another transpec's actions. */
531 	if (t->use_id) {
532 	    if (t->use_id < 0) return &NullTrans;	/* missing? */
533 	    /* see if we have a pointer to that transpec */
534 	    if (t->use_trans) return t->use_trans;
535 	    for (tt=TrSpecs; tt; tt=tt->next) {
536 		if (t->use_id == tt->my_id) {
537 		    /* remember pointer for next time */
538 		    t->use_trans = tt;
539 		    return t->use_trans;
540 		}
541 	    }
542 	    t->use_id = -1;	/* flag it as missing */
543 	    fprintf(stderr, "Warning: transpec ID (%d) not found for %s.\n",
544 		t->use_id, e->gi);
545 	    return &NullTrans;
546 	}
547 
548 	return t;
549     }
550 
551     /* At this point, we have not found a matching spec.  See if there
552      * is a wildcard, and if so, use it. (Wildcard GI is named "*".) */
553     if ((t = FindTransByName("*"))) return t;
554 
555     if (warnings && !specID)
556 	fprintf(stderr, "Warning: transpec not found for %s\n", e->gi);
557 
558     /* default spec - pass character data and descend node */
559     return &NullTrans;
560 }
561 
562 /* ______________________________________________________________________ */
563 /*  Find translation spec by (GI) name.  Returns the first one that matches.
564  *  Arguments:
565  *	Pointer to name of transpec (the "gi" field of the Trans structure).
566  *  Return:
567  *	Pointer to translation spec that matches name.
568  */
569 
570 Trans_t *
FindTransByName(char * s)571 FindTransByName(
572     char *s
573 )
574 {
575     Trans_t *t;
576 
577     for (t=TrSpecs; t; t=t->next) {
578 	/* check if tag name matches (first check 1st char, for efficiency) */
579 	if (t->gi) {
580 	    if (*(t->gi) != *s) continue;	/* check 1st character */
581 	    if (!strcmp(t->gi, s)) return t;
582 	}
583     }
584     return NULL;
585 }
586 
587 /*  Find translation spec by its ID (SpecID).
588  *  Arguments:
589  *	Spec ID (an int).
590  *  Return:
591  *	Pointer to translation spec that matches name.
592  */
593 Trans_t *
FindTranByID(int n)594 FindTranByID(int n)
595 {
596     Trans_t	*t;
597 
598     for (t=TrSpecs; t; t=t->next)
599 	if (n == t->my_id) return t;
600     return NULL;
601 }
602 
603 /* ______________________________________________________________________ */
604 /*  Process a "chunk" of content data of an element.
605  *  Arguments:
606  *	Pointer to data content to process
607  *	FILE pointer to where to write output.
608  */
609 
610 void
DoData(char * data,FILE * fp,int verbatim)611 DoData(
612     char *data,
613     FILE *fp,
614     int  verbatim
615 )
616 {
617     if (!fp) return;
618     OutputString(data, fp, 1);
619 }
620 
621 /* ______________________________________________________________________ */
622 /*  Handle a processing instruction.  This is done similarly to elements,
623  *  where we find a transpec, then do what it says.  Differences: PI names
624  *  start with '_' in the spec file (if a GI does not start with '_', it
625  *  may be forced to upper case, sgmls keeps PIs as mixed case); the args
626  *  to the PI are treated as the data of an element.  Note that a PI wildcard
627  *  is "_*"
628  *  Arguments:
629  *	Pointer to the PI.
630  *	FILE pointer to where to write output.
631  */
632 
633 void
DoPI(char * pi,FILE * fp)634 DoPI(
635     char *pi,
636     FILE *fp
637 )
638 {
639     char	buf[250], **tok;
640     int		n;
641     Trans_t	*t;
642 
643     buf[0] = '_';
644     strcpy(&buf[1], pi);
645     n = 2;
646     tok = Split(buf, &n, 0);
647     if ((t = FindTransByName(tok[0])) ||
648         (t = FindTransByName("_*"))) {
649 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
650 	else {
651 	    if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
652 	    if (t->ignore != IGN_DATA)	/* skip data nodes? */
653 		if (n > 1) OutputString(tok[1], fp, 1);
654 	    if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
655 	}
656 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
657     }
658     else {
659 	/* If not found, just print the PI in square brackets, along
660 	 * with a warning message. */
661 	fprintf(fp, "[%s]", pi);
662 	if (warnings) fprintf(stderr, "Warning: Unrecognized PI: [%s]\n", pi);
663     }
664 }
665 
666 /* ______________________________________________________________________ */
667 /*  Set and increment variables, as appropriate, if the transpec says to.
668  *  Arguments:
669  *	Pointer to translation spec for current element.
670  */
671 
672 static void
set_and_increment(Trans_t * t,Element_t * e)673 set_and_increment(
674     Trans_t	*t,
675     Element_t	*e
676 )
677 {
678     Mapping_t	*m;
679     int		i, inc, n;
680     char	*cp, buf[50];
681     char	ebuf[500];
682 
683     /* set/reset variables */
684     if (t->set_var) {
685 	for (m=t->set_var->maps,i=0; i<t->set_var->n_used; i++)	{
686 	    ExpandVariables(m[i].sval, ebuf, e);	/* do some expansion */
687 	    SetMappingNV(Variables, m[i].name, ebuf);
688     	}
689     }
690 
691     /* increment counters */
692     if (t->incr_var) {
693 	for (m=t->incr_var->maps,i=0; i<t->incr_var->n_used; i++) {
694 	    cp = FindMappingVal(Variables, m[i].name);
695 	    /* if not set at all, set to 1 */
696 	    if (!cp) SetMappingNV(Variables, m[i].name, "1");
697 	    else {
698 		if (isdigit(*cp) || (*cp == '-' && isdigit(cp[1]))) {
699 		    n = atoi(cp);
700 		    if (m[i].sval && isdigit(*m[i].sval)) inc = atoi(m[i].sval);
701 		    else inc = 1;
702 		    sprintf(buf, "%d", (n + inc));
703 		    SetMappingNV(Variables, m[i].name, buf);
704 		} else
705 		if (!*(cp+1) && isalpha(*cp))	{
706 		    buf[0] = *cp + 1;
707 		    buf[1] = 0;
708 		    SetMappingNV(Variables, m[i].name, buf);
709 		}
710 	    }
711 	}
712     }
713 }
714 
715 /* ______________________________________________________________________ */
716 /*  Translate one element.
717  *  Arguments:
718  *	Pointer to element under consideration.
719  *	FILE pointer to where to write output.
720  *	Pointer to translation spec for current element, or null.
721  */
722 void
TransElement(Element_t * e,FILE * fp,Trans_t * t)723 TransElement(
724     Element_t	*e,
725     FILE	*fp,
726     Trans_t	*t
727 )
728 {
729     int		i;
730 
731     if (!t) t = ((e && e->trans) ? e->trans : &NullTrans);
732 
733     /* see if we should quit. */
734     if (t->quit) {
735 	fprintf(stderr, "Quitting at location:\n");
736 	PrintLocation(e, fp);
737 	fprintf(stderr, "%s\n", t->quit);
738 	exit(1);
739     }
740 
741     /* See if we want to replace subtree (do text, don't descend subtree) */
742     if (t->replace) {
743 	ProcesOutputSpec(t->replace, e, fp, 1);
744 	if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
745 	set_and_increment(t, e);	/* adjust variables, if appropriate */
746 	return;
747     }
748 
749     if (t->starttext) ProcesOutputSpec(t->starttext, e, fp, 1);
750     if (t->message)   ProcesOutputSpec(t->message, e, stderr, 0);
751 
752     /* Process data for this node and descend child elements/nodes. */
753     if (t->ignore != IGN_ALL) {
754 	/* Is there a "generated" node at the front of this one? */
755 	if (e->gen_trans[0]) {
756 	    Trans_t *tp;
757 	    if ((tp = FindTranByID(e->gen_trans[0]))) {
758 		if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
759 		if (tp->message)   ProcesOutputSpec(tp->message, e, stderr, 0);
760 		if (tp->endtext)   ProcesOutputSpec(tp->endtext, e, fp, 1);
761 	    }
762 	}
763 	/* Loop thruthe "nodes", whether data, child element, or PI. */
764 	for (i=0; i<e->ncont; i++) {
765 	    if (IsContElem(e,i)) {
766 		if (t->ignore != IGN_CHILDREN)	/* skip child nodes? */
767 		    TransElement(ContElem(e,i), fp, NULL);
768 	    }
769 	    else if (IsContData(e,i)) {
770 		if (t->ignore != IGN_DATA)	/* skip data nodes? */
771 		    DoData(ContData(e,i), fp, t->verbatim);
772 	    }
773 	    else if (IsContPI(e,i))
774 		DoPI(e->cont[i].ch.data, fp);
775 	}
776 	/* Is there a "generated" node at the end of this one? */
777 	if (e->gen_trans[1]) {
778 	    Trans_t *tp;
779 	    if ((tp = FindTranByID(e->gen_trans[1]))) {
780 		if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
781 		if (tp->message)   ProcesOutputSpec(tp->message, e, stderr, 0);
782 		if (tp->endtext)   ProcesOutputSpec(tp->endtext, e, fp, 1);
783 	    }
784 	}
785     }
786 
787     set_and_increment(t, e);		/* adjust variables, if appropriate */
788 
789     if (t->endtext) ProcesOutputSpec(t->endtext, e, fp, 1);
790 
791     e->processed = 1;
792 }
793 
794 /* ______________________________________________________________________ */
795 /* Check if element matches specified relationship, and, if it does, perform
796  * action on either current element or matching element (depends on flag).
797  *  Arguments:
798  *	Pointer to element under consideration.
799  *	Pointer to relationship name.
800  *	Pointer to related element name (GI).
801  *	Pointer to action to take (string - turned into an int).
802  *	FILE pointer to where to write output.
803  *	Flag saying whether to do action on related element (RA_Related)
804  *		or on current element (RA_Current).
805  *  Return:
806  *	Bool, saying whether (1) or not (0) relationship matches.
807  */
808 
809 int
CheckRelation(Element_t * e,char * relname,char * related,char * actname,FILE * fp,RelAction_t flag)810 CheckRelation(
811     Element_t	*e,
812     char	*relname,	/* relationship name */
813     char	*related,	/* related element */
814     char	*actname,	/* action to take */
815     FILE	*fp,
816     RelAction_t	flag
817 )
818 {
819     Element_t	*ep;
820     Relation_t	r;
821 
822     if ((r = FindRelByName(relname)) == REL_Unknown) return 0;
823     if (!(ep=QRelation(e, related, r)))	return 0;
824 
825     if (!actname) return 1;		/* no action - return what we found */
826 
827     switch (flag) {
828 	case RA_Related:	TranTByAction(ep, actname, fp);	break;
829 	case RA_Current:	TranTByAction(e, actname, fp);	break;
830     }
831     return 1;
832 }
833 
834 /* ______________________________________________________________________ */
835 /* Perform action given by a SpecID on the given element.
836  *  Arguments:
837  *	Pointer to element under consideration.
838  *	SpecID of action to perform.
839  *	FILE pointer to where to write output.
840  *
841  */
842 void
TranByAction(Element_t * e,int n,FILE * fp)843 TranByAction(
844     Element_t	*e,
845     int		n,
846     FILE	*fp
847 )
848 {
849     Trans_t	*t;
850 
851     t = FindTranByID(n);
852     if (!t) {
853 	fprintf(stderr, "Could not find named action for %d.\n", n);
854 	return;
855     }
856     TransElement(e, fp, t);
857 }
858 
859 /* ______________________________________________________________________ */
860 /* Perhaps perform action given by a SpecID on the given element.
861  *  Arguments:
862  *	Pointer to element under consideration.
863  *	SpecID of action to perform.  Unlike TranByAction, this is the argument
864  *	  as it occurred in the transpec (ASCII) and may end with the letter
865  *	  "t" which means that the transpec mustpass criteria selection.
866  *	FILE pointer to where to write output.
867  */
868 void
TranTByAction(Element_t * e,char * strn,FILE * fp)869 TranTByAction(
870     Element_t	*e,
871     char	*strn,
872     FILE	*fp
873 )
874 {
875     int n;
876     Trans_t	*t;
877 
878     n = atoi(strn);
879     if ( strn[strlen(strn)-1] != 't' )	{
880     	t = FindTranByID(n);
881     	if (!t) {
882 	    fprintf(stderr, "Could not find named action for %d.\n", n);
883 	    return;
884     	}
885     } else	{
886 	t = FindTrans(e, n);
887 	if ( !t || !t->my_id )
888 	    return;
889     }
890     TransElement(e, fp, t);
891 }
892 
893 /* ______________________________________________________________________ */
894