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