xref: /onnv-gate/usr/src/cmd/man/src/util/instant.src/translate.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  *  Program to manipulate SGML instances.
51  *
52  *  This module is for "translating" an instance to another form, usually
53  *  suitable for a formatting application.
54  *
55  *  Entry points for this module:
56  *	DoTranslate(elem, transfile, mapfile, fp)
57  * ________________________________________________________________________
58  */
59 
60 #ifndef lint
61 static char *RCSid =
62   "$Header: /usr/src/docbook-to-man/Instant/RCS/translate.c,v 1.16 1998/06/29 04:13:40 fld 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 
73 #include <tptregexp.h>
74 #include "general.h"
75 #define STORAGE
76 #include "translate.h"
77 
78 static Trans_t	NullTrans;		/* an empty one */
79 
80 /* stack for nested Transpecs */
81 
82 #define MAXTRANSPECDEPTH  500	/* max depth of transpec nesting */
83 
84 static Trans_t	*tsStack[MAXTRANSPECDEPTH];
85 static int	tsStacki = -1;	/* index into used stack */
86 
87 /* forward references */
88 void	ProcesOutputSpec(char *, Element_t *, FILE *, int);
89 static void	WasProcessed(Element_t *);
90 
91 /* ______________________________________________________________________ */
92 /*  Translate the subtree starting at 'e'.  Use 'transfile' for translation
93  *  specs.  Output goes to 'fp'.  This is the entry point for translating
94  *  an instance.
95  *  Assumes you've read SDATA and CharMap files (optionally).
96  *  Arguments:
97  *	Pointer to element under consideration.
98  *	Pointer to name of translation spec file.
99  *	FILE pointer to where to write output.
100  */
101 
102 void
DoTranslate(Element_t * e,char * transfile,FILE * fp)103 DoTranslate(
104     Element_t	*e,
105     char	*transfile,
106     FILE	*fp
107 )
108 {
109     Trans_t	*t, *tn;
110 
111     if (!transfile) {
112 	fprintf(stderr,
113 		"Translation spec file not specified. Skipping translation.\n");
114 	return;
115     }
116     ReadTransSpec(transfile);
117 
118     /* Find transpec for each node. */
119     DescendTree(e, PrepTranspecs, 0, 0, 0);
120 
121     /* Stuff to do at start of processing */
122     if ((t = FindTransByName("_Start"))) {
123 	if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
124 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
125 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
126 	if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
127     }
128 
129     /* Translate topmost/first element.  This is recursive. */
130     TransElement(e, fp, NULL);
131 
132     /* Stuff to do at end of processing */
133     if ((t = FindTransByName("_End"))) {
134 	if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
135 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
136 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
137 	if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
138     }
139 
140     /* Warn about unprocessed elements in this doc tree, if verbose mode. */
141     if (verbose)
142 	DescendTree(e, WasProcessed, 0, 0, 0);
143 
144     /* Clean up. This is not yet complete, which is no big deal (since the
145      * program is normally done at this point anyway.  */
146     for (t=TrSpecs; t; ) {
147 	tn = t->next;
148 	/* free the contents of t here ... */
149 	(void)free((void* )t);
150 	t = tn;
151     }
152     TrSpecs = 0;
153 }
154 
155 /* ______________________________________________________________________ */
156 /*  Print warning about unprocessed elements in this doc tree (if they
157  *  were not explicitely ignored).
158  *  Arguments:
159  *	Pointer to element under consideration.
160  */
161 static void
WasProcessed(Element_t * e)162 WasProcessed(
163     Element_t	*e
164 )
165 {
166     Trans_t	*t;
167     t = e->trans;
168     if (!e->processed && (t && !t->ignore)) {
169 	fprintf(stderr, "Warning: element '%s' was not processed:\n", e->gi);
170 	PrintLocation(e, stderr);
171     }
172 }
173 
174 /* ______________________________________________________________________ */
175 /*  For each element find transpec.
176  *  Arguments:
177  *	Pointer to element under consideration.
178  */
179 void
PrepTranspecs(Element_t * e)180 PrepTranspecs(
181     Element_t	*e
182 )
183 {
184     Trans_t	*t;
185     t = FindTrans(e, 0);
186     e->trans = t;
187 }
188 
189 /* ______________________________________________________________________ */
190 /*  Copy a buffer/string into another, expanding regular variables and immediate
191  *  variables. (Special variables are done later.)
192  *  Arguments:
193  *	Pointer to string to expand.
194  *	Pointer to expanded string. (return)
195  *	Pointer to element under consideration.
196  */
197 void
ExpandVariables(char * in,char * out,Element_t * e)198 ExpandVariables(
199     char	*in,
200     char	*out,
201     Element_t	*e
202 )
203 {
204     register int i, j, k;
205     char	*ip, *vp, *op;
206     char	*def_val, *s, *atval, *modifier;
207     char	vbuf[500];
208     int		lev;
209 
210     ip = in;
211     op = out;
212     while (*ip) {
213 	/* start of regular variable? */
214 	if (*ip == '$' && *(ip+1) == L_CURLY && *(ip+2) != '_') {
215 	    ip++;
216 	    ip++;		/* point at variable name */
217 	    vp = vbuf;
218 	    /*	Look for matching (closing) curly. (watch for nesting)
219 	     *	We store the variable content in a tmp buffer, so we don't
220 	     *	clobber the input buffer.
221 	     */
222 	    lev = 0;
223 	    while (*ip) {
224 		if (*ip == L_CURLY) lev++;
225 		if (*ip == R_CURLY) {
226 		    if (lev == 0) {
227 			ip++;
228 			break;
229 		    }
230 		    else lev--;
231 		}
232 		*vp++ = *ip++;	/* copy to variable buffer */
233 	    }
234 	    *vp = EOS;
235 	    /* vbuf now contains the variable name (stuff between curlys). */
236 	    if (lev != 0) {
237 		fprintf(stderr, "Botched variable use: %s\n", in);
238 		/* copy rest of string if we can't recover  ?? */
239 		return;
240 	    }
241 	    /* Now, expand variable. */
242 	    vp = vbuf;
243 
244 	    /* Check for immediate variables -- like _special variables but
245 	     * interpreted right now.  These start with a "+" */
246 	    if ( *vp == '+' )	{
247 
248 	    	if ( ! strcmp(vp, "+content") )	{
249 	    	    for ( i=0;  i<e->ncont; i++ )	{
250 	    	    	if ( IsContData(e, i) )	{
251 	    	    	    j = strlen(ContData(e,i));
252 	    	    	    memcpy(op, ContData(e,i), j);
253 	    	    	    op += j;
254 	    	    	} else	{
255 	    	    	    if ( warnings )
256 						if (! IsContPI(e, i) )
257 			    	fprintf(stderr, "warning: ${+current} skipped element content\n");
258 	    	    	}
259 	    	    }
260 
261 	    	} else
262 
263 	    	if ( ! strcmp(vp, "+caps") )	{
264 	    	    for ( i=k=0;  i<e->ncont; i++ )	{
265 	    	    	if ( IsContData(e, i) )	{
266 	    	    	    for ( j=0;  ContData(e, i)[j];  j++ )	{
267 	    	    	    	*op++ = toupper(ContData(e, i)[j]);
268 			    }
269 	    	    	} else	{
270 #if FALSE
271 	    	    	    if ( warnings )
272 			    	fprintf(stderr, "warning: ${+caps} skipped element content\n");
273 #endif
274 	    	    	}
275 	    	    }
276 	    	    *op = 0;
277 
278 	    	} else	{
279 	    	    fprintf(stderr, "unknown immediate variable: %s\n", vp);
280 	    	}
281 
282 	    } else	{
283 
284 		/* See if this variable has a default [ format: ${varname def} ] */
285 
286 		def_val = vp;
287 		while (*def_val && *def_val != ' ') def_val++;
288 		if (*def_val) *def_val++ = EOS;
289 		else def_val = 0;
290 		/* def_val now points to default, if it exists, null if not. */
291 
292 		modifier = vp;
293 		while (*modifier && *modifier != ':') modifier++;
294 		if (*modifier) *modifier++ = EOS;
295 		else modifier = 0;
296 		/* modifier now points to modifier if it exists, null if not. */
297 
298 		s = 0;
299 		/* if attribute of current elem with this name found, use value */
300 		if (e && (atval = FindAttValByName(e, vp)))
301 		    s = atval;
302 	   	 else	/* else try for (global) variable with this name */
303 		    s = FindMappingVal(Variables, vp);
304 
305 		/* If we found a value, copy it to the output buffer. */
306 
307 		if (s) {
308 		    if ( modifier && *modifier == 'l' ) {
309 			while (*s) {
310 			    *op = tolower(*s);
311 			    op++, *s++;
312 			}
313 		    } else
314 			while (*s) *op++ = *s++;
315 		} else
316 		if (def_val)	{
317 		    while (*def_val) *op++ = *def_val++;
318 		}
319 	    }
320 	    continue;
321 	}
322 	*op++ = *ip++;
323     }
324     *op = EOS;		/* terminate string */
325 }
326 
327 /* ______________________________________________________________________ */
328 /*  Process an "output" translation spec - one of StartText, EndText,
329  *  Replace, Message.  (These are the ones that produce output.)
330  *  Steps done:
331  *	Expand attributes and regular varaibles in input string.
332  *	Pass thru string, accumulating chars to be sent to output stream.
333  *	If we find the start of a special variable, output what we've
334  *	  accumulated, then find the special variable's "bounds" (ie, the
335  *	  stuff between the curly brackets), and expand that by passing to
336  *	  ExpandSpecialVar().  Continue until done the input string.
337  *  Arguments:
338  *	Input buffer (string) to be expanded and output.
339  *	Pointer to element under consideration.
340  *	FILE pointer to where to write output.
341  *	Flag saying whether to track the character position we're on
342  *	  (passed to OutputString).
343  */
344 void
ProcesOutputSpec(char * ib,Element_t * e,FILE * fp,int track_pos)345 ProcesOutputSpec(
346     char	*ib,
347     Element_t	*e,
348     FILE	*fp,
349     int		track_pos
350 )
351 {
352     char	obuf[LINESIZE];
353     char	vbuf[LINESIZE];
354     char	*dest, vname[LINESIZE], *cp;
355     int		esc;
356 
357     obuf[0] = EOS;			/* start with empty output buffer */
358 
359     ExpandVariables(ib, vbuf, e);	/* expand regular variables */
360     ib = vbuf;
361     dest = obuf;
362 
363     esc = 0;
364     while (*ib) {
365 	/* Is esc-$ next?  If so, just copy the '$'. */
366 	if (*ib == '\\' && ib[1] == '$') {
367 	    ib++;			/* skip esc */
368 	    *dest++ = *ib++;		/* copy $ */
369 	    continue;
370 	}
371 
372 	/* If not a $, it's a regular char.  Just copy it and go to next. */
373 	if (*ib != '$') {		/* look for att/variable marker */
374 	    *dest++ = *ib++;		/* it's not. just copy character */
375 	    continue;
376 	}
377 
378 	/* We have a $.  What we have must be a "special variable" since
379 	 * regular variables have already been expanded, or just a lone $. */
380 
381 	if (ib[1] != L_CURLY) {	/* just a stray dollar sign (no variable) */
382 	    *dest++ = *ib++;
383 	    continue;
384 	}
385 
386 	ib++;				/* point past $ */
387 
388 	/* Output what we have in buffer so far. */
389 	*dest = EOS;			/* terminate string */
390 	if (obuf[0]) OutputString(obuf, fp, track_pos);
391 	dest = obuf;			/* ready for new stuff in buffer */
392 
393 	if (!strchr(ib, R_CURLY)) {
394 	    fprintf(stderr, "Mismatched braces in TranSpec: %s\n", ib);
395 	    /* how do we recover from this? */
396 	}
397 	ib++;
398 	cp = vname;
399 	while (*ib && *ib != R_CURLY) *cp++ = *ib++;
400 	*cp = EOS;			/* terminate att/var name */
401 	ib++;				/* point past closing curly */
402 	/* we now have special variable name (stuff in curly {}'s) in vname */
403 	ExpandSpecialVar(&vname[1], e, fp, track_pos);
404     }
405     *dest = EOS;			/* terminate string in output buffer */
406 
407     if (obuf[0]) OutputString(obuf, fp, track_pos);
408 }
409 
410 /* ______________________________________________________________________ */
411 /*  Find the translation spec for the given tag.
412  *  Returns pointer to first spec that matches (name, depth, etc., of tag).
413  *  Arguments:
414  *	e -- Pointer to element under consideration.
415  *	specID -- name of specid that we're looking for
416  *  Return:
417  *	Pointer to translation spec that matches given element's context.
418  */
419 
420 Trans_t *
FindTrans(Element_t * e,int specID)421 FindTrans(
422     Element_t	*e,
423     int		specID
424 )
425 {
426     char	context[LINESIZE], buf[LINESIZE], *cp, **vec, *atval;
427     int		i, a, match;
428     Trans_t	*t, *tt;
429 
430     /* loop through all transpecs */
431     for (t=TrSpecs; t; t=t->next)
432     {
433 	/* Only one of gi or gilist will be set. */
434 	/* Check if elem name matches */
435 	if (t->gi && !StrEq(t->gi, e->gi) && !specID) continue;
436 
437 	/* test if we're looking for a specific specID and then if
438 	 * this is it.. */
439 	if (specID)
440 	    if (!t->my_id || (specID != t->my_id))
441 		continue;
442 
443 	/* Match one in the list of GIs? */
444 	if (t->gilist) {
445 	    for (match=0,vec=t->gilist; *vec; vec++) {
446 		if (StrEq(*vec, e->gi)) {
447 		    match = 1;
448 		    break;
449 		}
450 	    }
451 	    if (!match) continue;
452 	}
453 
454 	/* Check context */
455 
456 	/* Special case of context */
457 	if (t->parent)
458 	    if (!QRelation(e, t->parent, REL_Parent)) continue;
459 
460 	if (t->context) {	/* no context specified -> a match */
461 	    FindContext(e, t->depth, context);
462 
463 	    /* If reg expr set, do regex compare; else just string compare. */
464 	    if (t->context_re) {
465 		if (! tpt_regexec(t->context_re, context)) continue;
466 	    }
467 	    else {
468 		/* Is depth of spec deeper than element's depth? */
469 		if (t->depth > e->depth) continue;
470 
471 		/* See if context of element matches "context" of transpec */
472 		match = ( (t->context[0] == context[0]) &&
473 			    !strcmp(t->context, context) );
474 		if (!match) continue;
475 	    }
476 	}
477 
478 	/* Check attributes.  Loop through list, comparing each. */
479 	if (t->nattpairs) {	/* no att specified -> a match */
480 	    for (match=1,a=0; a<t->nattpairs; a++) {
481 		if (!(atval = FindAttValByName(e, t->attpair[a].name))) {
482 		    match = 0;
483 		    break;
484 		}
485 		if (!tpt_regexec(t->attpair[a].rex, atval)) match = 0;
486 	    }
487 	    if (!match) continue;
488 	}
489 
490 	/* Check relationships:  child, parent, ancestor, sib, ...  */
491 	if (t->relations) {
492 	    Mapping_t *r;
493 	    match = 1;
494 	    for (r=t->relations->maps,i=0; i<t->relations->n_used; i++) {
495 		if (!CheckRelation(e, r[i].name, r[i].sval, 0, 0, RA_Current)) {
496 		    match = 0;
497 		    break;
498 		}
499 	    }
500 	    if (!match) continue;
501 	}
502 
503 	/* check this element's parent's attribute */
504 	if (t->pattrset && e->parent) {
505 	    char *p, **tok;
506 
507 	    i = 2;
508 	    match = 1;
509 	    tok = Split(t->pattrset, &i, S_STRDUP);
510 	    if ( i == 2 ) {
511 		p = FindAttValByName(e->parent, tok[0]);
512 		ExpandVariables(tok[1], buf, 0);
513 		if ( !p || strcmp(p, buf) )
514 		    match = 0;
515 	    } else {
516 		if (!FindAttValByName(e->parent, t->pattrset))
517 		    match = 0;
518 	    }
519 	    free(tok[0]);
520 	    if (!match) continue;
521 	}
522 
523 	/* check this element's "birth order" */
524 	if (t->nth_child) {
525 	    /* First one is called "1" by the user.  Internally called "0". */
526 	    i = t->nth_child;
527 	    if (i > 0) {	/* positive # -- count from beginning */
528 		if (e->my_eorder != (i-1)) continue;
529 	    }
530 	    else {		/* negative # -- count from end */
531 		i = e->parent->necont - i;
532 		if (e->my_eorder != i) continue;
533 	    }
534 	}
535 
536 	/* check that variables match */
537 	if (t->var_name) {
538 	    cp = FindMappingVal(Variables, t->var_name);
539 	    if (!cp || strcmp(cp, t->var_value)) continue;
540 	}
541 
542 	/* check for variable regular expression match */
543 	if ( t->var_RE_name )	{
544 	    cp = FindMappingVal(Variables, t->var_RE_name);
545 	    if (!cp || !tpt_regexec(t->var_RE_value, cp)) continue;
546 	}
547 
548 	/* check content */
549 	if (t->content) {		/* no att specified -> a match */
550 	    for (match=0,i=0; i<e->ndcont; i++) {
551 		if (tpt_regexec(t->content_re, e->dcont[i])) {
552 		    match = 1;
553 		    break;
554 		}
555 	    }
556 	    if (!match) continue;
557 	}
558 
559 	/* -------- at this point we've passed all criteria -------- */
560 
561 	/* See if we should be using another transpec's actions. */
562 	if (t->use_id) {
563 	    if (t->use_id < 0) return &NullTrans;	/* missing? */
564 	    /* see if we have a pointer to that transpec */
565 	    if (t->use_trans) return t->use_trans;
566 	    for (tt=TrSpecs; tt; tt=tt->next) {
567 		if (t->use_id == tt->my_id) {
568 		    /* remember pointer for next time */
569 		    t->use_trans = tt;
570 		    return t->use_trans;
571 		}
572 	    }
573 	    t->use_id = -1;	/* flag it as missing */
574 	    fprintf(stderr, "Warning: transpec ID (%d) not found for %s.\n",
575 		t->use_id, e->gi);
576 	    return &NullTrans;
577 	}
578 
579 	return t;
580     }
581 
582     /* At this point, we have not found a matching spec.  See if there
583      * is a wildcard, and if so, use it. (Wildcard GI is named "*".) */
584     if ((t = FindTransByName("*"))) return t;
585 
586     if (warnings && !specID)
587 	fprintf(stderr, "Warning: transpec not found for %s\n", e->gi);
588 
589     /* default spec - pass character data and descend node */
590     return &NullTrans;
591 }
592 
593 /* ______________________________________________________________________ */
594 /*  Find translation spec by (GI) name.  Returns the first one that matches.
595  *  Arguments:
596  *	Pointer to name of transpec (the "gi" field of the Trans structure).
597  *  Return:
598  *	Pointer to translation spec that matches name.
599  */
600 
601 Trans_t *
FindTransByName(char * s)602 FindTransByName(
603     char *s
604 )
605 {
606     Trans_t *t;
607 
608     for (t=TrSpecs; t; t=t->next) {
609 	/* check if tag name matches (first check 1st char, for efficiency) */
610 	if (t->gi) {
611 	    if (*(t->gi) != *s) continue;	/* check 1st character */
612 	    if (!strcmp(t->gi, s)) return t;
613 	}
614     }
615     return NULL;
616 }
617 
618 /*  Find translation spec by its ID (SpecID).
619  *  Arguments:
620  *	Spec ID (an int).
621  *  Return:
622  *	Pointer to translation spec that matches name.
623  */
624 Trans_t *
FindTranByID(int n)625 FindTranByID(int n)
626 {
627     Trans_t	*t;
628 
629     for (t=TrSpecs; t; t=t->next)
630 	if (n == t->my_id) return t;
631     return NULL;
632 }
633 
634 /* ______________________________________________________________________ */
635 /*  Process a "chunk" of content data of an element.
636  *  Arguments:
637  *	Pointer to data content to process
638  *	FILE pointer to where to write output.
639  */
640 
641 void
DoData(char * data,FILE * fp,Trans_t * t)642 DoData(
643     char *data,
644     FILE *fp,
645     Trans_t	*t
646 )
647 {
648     char	*cp, buf[LINESIZE], *dp, *sub, prev;
649     int		i, j, mapped;
650     Mapping_t	*m;
651 
652     /*  Worry about embedded newlines? */
653 
654     if (!fp) return;
655 
656     /* CLEANUP: this should really all be done in OutputString(). (I think) */
657 
658     if (nCharMap) {
659 	/* for each character, see if it's mapped to something else */
660 	for (prev=0,cp=data,dp=buf; *cp; cp++) {
661 	    if (prev == '\\') {
662 		*dp++ = *cp;
663 		prev = *cp;
664 		continue;
665 	    }
666 	    for (mapped=0,i=0; !t->verbatim && (i<nCharMap); i++) {
667 		if ((*cp != CharMap[i].name[0]) ||
668 		    ((*cp == '\\') && (*(cp+1) != '\\')) )
669 		    	continue;
670 		if ( *cp == '\\' )
671 			*cp++;
672 		sub = CharMap[i].sval;
673 		while (*sub) *dp++ = *sub++;
674 		mapped = 1;
675 		break;
676 	    }
677 	    for ( j=tsStacki;  j >= 0;  j-- )	{
678 	        if (tsStack[j]->substitute) {
679 		    for (m=tsStack[j]->substitute->maps,i=0;
680 		    		i<tsStack[j]->substitute->n_used; i++) {
681 		        if ( *cp == m[i].name[0] )	{
682 		    	    sub = m[i].sval;
683 			    while (*sub)
684 				    *dp++ = *sub++;
685 			    mapped = 2;
686 			    break;
687 		        }
688 		    }
689 		    if ( mapped == 2 )
690 		    	break;
691 	        }
692 	    }
693 	    if ( *cp == -1 )	*cp = '';
694 	    if (!mapped && t->trim && (strchr(t->trim, *cp) != NULL))	{
695 	    	continue;
696 	    }
697 	    if (!mapped) *dp++ = *cp;
698 	    prev = *cp;
699 	}
700 	*dp = EOS;
701 	dp = buf;
702     }
703     else dp = data;
704     OutputString(dp, fp, 1);
705 }
706 
707 /* ______________________________________________________________________ */
708 /*  Handle a processing instruction.  This is done similarly to elements,
709  *  where we find a transpec, then do what it says.  Differences: PI names
710  *  start with '_' in the spec file (if a GI does not start with '_', it
711  *  may be forced to upper case, sgmls keeps PIs as mixed case); the args
712  *  to the PI are treated as the data of an element.  Note that a PI wildcard
713  *  is "_*"
714  *  Arguments:
715  *	Pointer to the PI.
716  *	FILE pointer to where to write output.
717  */
718 
719 void
DoPI(char * pi,FILE * fp)720 DoPI(
721     char *pi,
722     FILE *fp
723 )
724 {
725     char	buf[250], **tok;
726     int		n;
727     Trans_t	*t;
728 
729     buf[0] = '_';
730     strcpy(&buf[1], pi);
731     n = 2;
732     tok = Split(buf, &n, 0);
733     if ((t = FindTransByName(tok[0])) ||
734         (t = FindTransByName("_*"))) {
735 	if (t->replace)   ProcesOutputSpec(t->replace, 0, fp, 1);
736 	else {
737 	    if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
738 	    if (t->ignore != IGN_DATA)	/* skip data nodes? */
739 		if (n > 1) OutputString(tok[1], fp, 1);
740 	    if (t->endtext)   ProcesOutputSpec(t->endtext, 0, fp, 1);
741 	}
742 	if (t->message)   ProcesOutputSpec(t->message, 0, stderr, 0);
743     }
744     else {
745 	/* If not found, just print the PI in square brackets, along
746 	 * with a warning message. */
747 	fprintf(fp, "[%s]", pi);
748 	if (warnings) fprintf(stderr, "Warning: Unrecognized PI: [%s]\n", pi);
749     }
750 }
751 
752 /* ______________________________________________________________________ */
753 /*  Set and increment variables, as appropriate, if the transpec says to.
754  *  Arguments:
755  *	Pointer to translation spec for current element.
756  */
757 
758 static void
set_and_increment(Trans_t * t,Element_t * e)759 set_and_increment(
760     Trans_t	*t,
761     Element_t	*e
762 )
763 {
764     Mapping_t	*m;
765     int		i, inc, n;
766     char	*cp, buf[50];
767     char	ebuf[5000];
768 
769     /* set/reset variables */
770     if (t->set_var) {
771 	for (m=t->set_var->maps,i=0; i<t->set_var->n_used; i++)	{
772 	    ExpandVariables(m[i].sval, ebuf, e);	/* do some expansion */
773 	    SetMappingNV(Variables, m[i].name, ebuf);
774     	}
775     }
776 
777     /* increment counters */
778     if (t->incr_var) {
779 	for (m=t->incr_var->maps,i=0; i<t->incr_var->n_used; i++) {
780 	    cp = FindMappingVal(Variables, m[i].name);
781 	    /* if not set at all, set to 1 */
782 	    if (!cp) SetMappingNV(Variables, m[i].name, "1");
783 	    else {
784 		if (isdigit(*cp) || (*cp == '-' && isdigit(cp[1]))) {
785 		    n = atoi(cp);
786 		    if (m[i].sval && isdigit(*m[i].sval)) inc = atoi(m[i].sval);
787 		    else inc = 1;
788 		    sprintf(buf, "%d", (n + inc));
789 		    SetMappingNV(Variables, m[i].name, buf);
790 		} else
791 		if (!*(cp+1) && isalpha(*cp))	{
792 		    buf[0] = *cp + 1;
793 		    buf[1] = 0;
794 		    SetMappingNV(Variables, m[i].name, buf);
795 		}
796 	    }
797 	}
798     }
799 }
800 
801 /* ______________________________________________________________________ */
802 /*  Translate one element.
803  *  Arguments:
804  *	Pointer to element under consideration.
805  *	FILE pointer to where to write output.
806  *	Pointer to translation spec for current element, or null.
807  */
808 void
TransElement(Element_t * e,FILE * fp,Trans_t * t)809 TransElement(
810     Element_t	*e,
811     FILE	*fp,
812     Trans_t	*t
813 )
814 {
815     int		i;
816 
817     if (!t) t = ((e && e->trans) ? e->trans : &NullTrans);
818 
819     /* see if we should quit. */
820     if (t->quit) {
821 	fprintf(stderr, "Quitting at location:\n");
822 	PrintLocation(e, fp);
823 	fprintf(stderr, "%s\n", t->quit);
824 	exit(1);
825     }
826 
827     /* stack this element */
828     PushTranspecName(t);
829 
830     /* See if we want to replace subtree (do text, don't descend subtree) */
831     if (t->replace) {
832 	ProcesOutputSpec(t->replace, e, fp, 1);
833 	if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
834 	set_and_increment(t, e);	/* adjust variables, if appropriate */
835 	PopTranspecName();
836 	return;
837     }
838 
839     if (t->starttext) ProcesOutputSpec(t->starttext, e, fp, 1);
840     if (t->message)   ProcesOutputSpec(t->message, e, stderr, 0);
841 
842     /* Process data for this node and descend child elements/nodes. */
843     if (t->ignore != IGN_ALL) {
844 	/* Is there a "generated" node at the front of this one? */
845 	if (e->gen_trans[0]) {
846 	    Trans_t *tp;
847 	    if ((tp = FindTranByID(e->gen_trans[0]))) {
848 		if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
849 		if (tp->message)   ProcesOutputSpec(tp->message, e, stderr, 0);
850 		if (tp->endtext)   ProcesOutputSpec(tp->endtext, e, fp, 1);
851 	    }
852 	}
853 	/* Loop thruthe "nodes", whether data, child element, or PI. */
854 	for (i=0; i<e->ncont; i++) {
855 	    if (IsContElem(e,i)) {
856 		if (t->ignore != IGN_CHILDREN)	/* skip child nodes? */
857 		    TransElement(ContElem(e,i), fp, NULL);
858 	    }
859 	    else if (IsContData(e,i)) {
860 		if (t->ignore != IGN_DATA)	/* skip data nodes? */
861 		    DoData(ContData(e,i), fp, t);
862 	    }
863 	    else if (IsContPI(e,i))
864 		DoPI(e->cont[i].ch.data, fp);
865 	}
866 	/* Is there a "generated" node at the end of this one? */
867 	if (e->gen_trans[1]) {
868 	    Trans_t *tp;
869 	    if ((tp = FindTranByID(e->gen_trans[1]))) {
870 		if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
871 		if (tp->message)   ProcesOutputSpec(tp->message, e, stderr, 0);
872 		if (tp->endtext)   ProcesOutputSpec(tp->endtext, e, fp, 1);
873 	    }
874 	}
875     }
876 
877     set_and_increment(t, e);		/* adjust variables, if appropriate */
878 
879     if (t->endtext) ProcesOutputSpec(t->endtext, e, fp, 1);
880 
881     e->processed = 1;
882     PopTranspecName();
883 }
884 
885 /* ______________________________________________________________________ */
886 /* Check if element matches specified relationship, and, if it does, perform
887  * action on either current element or matching element (depends on flag).
888  *  Arguments:
889  *	Pointer to element under consideration.
890  *	Pointer to relationship name.
891  *	Pointer to related element name (GI).
892  *	Pointer to action to take (string - turned into an int).
893  *	FILE pointer to where to write output.
894  *	Flag saying whether to do action on related element (RA_Related)
895  *		or on current element (RA_Current).
896  *  Return:
897  *	Bool, saying whether (1) or not (0) relationship matches.
898  */
899 
900 int
CheckRelation(Element_t * e,char * relname,char * related,char * actname,FILE * fp,RelAction_t flag)901 CheckRelation(
902     Element_t	*e,
903     char	*relname,	/* relationship name */
904     char	*related,	/* related element */
905     char	*actname,	/* action to take */
906     FILE	*fp,
907     RelAction_t	flag
908 )
909 {
910     Element_t	*ep;
911     Relation_t	r;
912 
913     if ((r = FindRelByName(relname)) == REL_Unknown) return 0;
914     if (!(ep=QRelation(e, related, r)))	return 0;
915 
916     if (!actname) return 1;		/* no action - return what we found */
917 
918     switch (flag) {
919 	case RA_Related:	TranTByAction(ep, actname, fp);	break;
920 	case RA_Current:	TranTByAction(e, actname, fp);	break;
921     }
922     return 1;
923 }
924 
925 /* ______________________________________________________________________ */
926 /* Perform action given by a SpecID on the given element.
927  *  Arguments:
928  *	Pointer to element under consideration.
929  *	SpecID of action to perform.
930  *	FILE pointer to where to write output.
931  *
932  */
933 void
TranByAction(Element_t * e,int n,FILE * fp)934 TranByAction(
935     Element_t	*e,
936     int		n,
937     FILE	*fp
938 )
939 {
940     Trans_t	*t;
941 
942     t = FindTranByID(n);
943     if (!t) {
944 	fprintf(stderr, "Could not find named action for %d.\n", n);
945 	return;
946     }
947     TransElement(e, fp, t);
948 }
949 
950 /* ______________________________________________________________________ */
951 /* Perhaps perform action given by a SpecID on the given element.
952  *  Arguments:
953  *	Pointer to element under consideration.
954  *	SpecID of action to perform.  Unlike TranByAction, this is the argument
955  *	  as it occurred in the transpec (ASCII) and may end with the letter
956  *	  "t" which means that the transpec mustpass criteria selection.
957  *	FILE pointer to where to write output.
958  */
959 void
TranTByAction(Element_t * e,char * strn,FILE * fp)960 TranTByAction(
961     Element_t	*e,
962     char	*strn,
963     FILE	*fp
964 )
965 {
966     int n;
967     Trans_t	*t;
968 
969     n = atoi(strn);
970     if ( strn[strlen(strn)-1] != 't' )	{
971     	t = FindTranByID(n);
972     	if (!t) {
973 	    fprintf(stderr, "Could not find named action for %d.\n", n);
974 	    return;
975     	}
976     } else	{
977 	t = FindTrans(e, n);
978 	if ( !t || !t->my_id )
979 	    return;
980     }
981     TransElement(e, fp, t);
982 }
983 
984 /* ______________________________________________________________________ */
985 /*  push the name of a transpec (the new active one) onto the stack
986  *  Arguments:
987  *	transpec name
988  */
989 
990 void
PushTranspecName(Trans_t * t)991 PushTranspecName(
992     Trans_t     *t
993 )
994 {
995     if ( tsStacki >= MAXTRANSPECDEPTH )	{
996 	fprintf(stderr, "Transpec stack overflow (%d)\n", MAXTRANSPECDEPTH);
997 	exit(1);
998     }
999     tsStack[++tsStacki] = t;
1000 }
1001 
1002 /* ______________________________________________________________________ */
1003 /*  pop the top name of a transpec off the stack
1004  */
1005 
1006 void
PopTranspecName(Trans_t * t)1007 PopTranspecName(
1008     Trans_t     *t
1009 )
1010 {
1011     if ( tsStacki < 0 )	{
1012     	fprintf(stderr, "Transpec stack underflow\n");
1013 	exit(1);
1014     }
1015     tsStacki--;
1016 }
1017