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