translate.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
* All rights reserved.
*/
/*
#pragma ident "%Z%%M% %I% %E% SMI"
* Copyright (c) 1994
* Open Software Foundation, Inc.
*
* Permission is hereby granted to use, copy, modify and freely distribute
* the software in this file and its documentation for any purpose without
* fee, provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation. Further, provided that the name of Open
* Software Foundation, Inc. ("OSF") not be used in advertising or
* publicity pertaining to distribution of the software without prior
* written permission from OSF. OSF makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
/*
* Copyright (c) 1996 X Consortium
* Copyright (c) 1995, 1996 Dalrymple Consulting
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the names of the X Consortium and
* Dalrymple Consulting shall not be used in advertising or otherwise to
* promote the sale, use or other dealings in this Software without prior
* written authorization.
*/
/* ________________________________________________________________________
*
* Program to manipulate SGML instances.
*
* This module is for "translating" an instance to another form, usually
* suitable for a formatting application.
*
* Entry points for this module:
* DoTranslate(elem, transfile, mapfile, fp)
* ________________________________________________________________________
*/
#ifndef lint
static char *RCSid =
"$Header: /usr/src/docbook-to-man/Instant/RCS/translate.c,v 1.16 1998/06/29 04:13:40 fld Exp $";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <sys/types.h>
#include <errno.h>
#include <tptregexp.h>
#include "general.h"
#define STORAGE
#include "translate.h"
static Trans_t NullTrans; /* an empty one */
/* stack for nested Transpecs */
#define MAXTRANSPECDEPTH 500 /* max depth of transpec nesting */
static Trans_t *tsStack[MAXTRANSPECDEPTH];
static int tsStacki = -1; /* index into used stack */
/* forward references */
void ProcesOutputSpec(char *, Element_t *, FILE *, int);
static void WasProcessed(Element_t *);
/* ______________________________________________________________________ */
/* Translate the subtree starting at 'e'. Use 'transfile' for translation
* specs. Output goes to 'fp'. This is the entry point for translating
* an instance.
* Assumes you've read SDATA and CharMap files (optionally).
* Arguments:
* Pointer to element under consideration.
* Pointer to name of translation spec file.
* FILE pointer to where to write output.
*/
void
DoTranslate(
Element_t *e,
char *transfile,
FILE *fp
)
{
Trans_t *t, *tn;
if (!transfile) {
fprintf(stderr,
"Translation spec file not specified. Skipping translation.\n");
return;
}
ReadTransSpec(transfile);
/* Find transpec for each node. */
DescendTree(e, PrepTranspecs, 0, 0, 0);
/* Stuff to do at start of processing */
if ((t = FindTransByName("_Start"))) {
if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
}
/* Translate topmost/first element. This is recursive. */
TransElement(e, fp, NULL);
/* Stuff to do at end of processing */
if ((t = FindTransByName("_End"))) {
if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
}
/* Warn about unprocessed elements in this doc tree, if verbose mode. */
if (verbose)
DescendTree(e, WasProcessed, 0, 0, 0);
/* Clean up. This is not yet complete, which is no big deal (since the
* program is normally done at this point anyway. */
for (t=TrSpecs; t; ) {
tn = t->next;
/* free the contents of t here ... */
(void)free((void* )t);
t = tn;
}
TrSpecs = 0;
}
/* ______________________________________________________________________ */
/* Print warning about unprocessed elements in this doc tree (if they
* were not explicitely ignored).
* Arguments:
* Pointer to element under consideration.
*/
static void
WasProcessed(
Element_t *e
)
{
Trans_t *t;
t = e->trans;
if (!e->processed && (t && !t->ignore)) {
fprintf(stderr, "Warning: element '%s' was not processed:\n", e->gi);
PrintLocation(e, stderr);
}
}
/* ______________________________________________________________________ */
/* For each element find transpec.
* Arguments:
* Pointer to element under consideration.
*/
void
PrepTranspecs(
Element_t *e
)
{
Trans_t *t;
t = FindTrans(e, 0);
e->trans = t;
}
/* ______________________________________________________________________ */
/* Copy a buffer/string into another, expanding regular variables and immediate
* variables. (Special variables are done later.)
* Arguments:
* Pointer to string to expand.
* Pointer to expanded string. (return)
* Pointer to element under consideration.
*/
void
ExpandVariables(
char *in,
char *out,
Element_t *e
)
{
register int i, j, k;
char *ip, *vp, *op;
char *def_val, *s, *atval, *modifier;
char vbuf[500];
int lev;
ip = in;
op = out;
while (*ip) {
/* start of regular variable? */
if (*ip == '$' && *(ip+1) == L_CURLY && *(ip+2) != '_') {
ip++;
ip++; /* point at variable name */
vp = vbuf;
/* Look for matching (closing) curly. (watch for nesting)
* We store the variable content in a tmp buffer, so we don't
* clobber the input buffer.
*/
lev = 0;
while (*ip) {
if (*ip == L_CURLY) lev++;
if (*ip == R_CURLY) {
if (lev == 0) {
ip++;
break;
}
else lev--;
}
*vp++ = *ip++; /* copy to variable buffer */
}
*vp = EOS;
/* vbuf now contains the variable name (stuff between curlys). */
if (lev != 0) {
fprintf(stderr, "Botched variable use: %s\n", in);
/* copy rest of string if we can't recover ?? */
return;
}
/* Now, expand variable. */
vp = vbuf;
/* Check for immediate variables -- like _special variables but
* interpreted right now. These start with a "+" */
if ( *vp == '+' ) {
if ( ! strcmp(vp, "+content") ) {
for ( i=0; i<e->ncont; i++ ) {
if ( IsContData(e, i) ) {
j = strlen(ContData(e,i));
memcpy(op, ContData(e,i), j);
op += j;
} else {
if ( warnings )
if (! IsContPI(e, i) )
fprintf(stderr, "warning: ${+current} skipped element content\n");
}
}
} else
if ( ! strcmp(vp, "+caps") ) {
for ( i=k=0; i<e->ncont; i++ ) {
if ( IsContData(e, i) ) {
for ( j=0; ContData(e, i)[j]; j++ ) {
*op++ = toupper(ContData(e, i)[j]);
}
} else {
#if FALSE
if ( warnings )
fprintf(stderr, "warning: ${+caps} skipped element content\n");
#endif
}
}
*op = 0;
} else {
fprintf(stderr, "unknown immediate variable: %s\n", vp);
}
} else {
/* See if this variable has a default [ format: ${varname def} ] */
def_val = vp;
while (*def_val && *def_val != ' ') def_val++;
if (*def_val) *def_val++ = EOS;
else def_val = 0;
/* def_val now points to default, if it exists, null if not. */
modifier = vp;
while (*modifier && *modifier != ':') modifier++;
if (*modifier) *modifier++ = EOS;
else modifier = 0;
/* modifier now points to modifier if it exists, null if not. */
s = 0;
/* if attribute of current elem with this name found, use value */
if (e && (atval = FindAttValByName(e, vp)))
s = atval;
else /* else try for (global) variable with this name */
s = FindMappingVal(Variables, vp);
/* If we found a value, copy it to the output buffer. */
if (s) {
if ( modifier && *modifier == 'l' ) {
while (*s) {
*op = tolower(*s);
op++, *s++;
}
} else
while (*s) *op++ = *s++;
} else
if (def_val) {
while (*def_val) *op++ = *def_val++;
}
}
continue;
}
*op++ = *ip++;
}
*op = EOS; /* terminate string */
}
/* ______________________________________________________________________ */
/* Process an "output" translation spec - one of StartText, EndText,
* Replace, Message. (These are the ones that produce output.)
* Steps done:
* Expand attributes and regular varaibles in input string.
* Pass thru string, accumulating chars to be sent to output stream.
* If we find the start of a special variable, output what we've
* accumulated, then find the special variable's "bounds" (ie, the
* stuff between the curly brackets), and expand that by passing to
* ExpandSpecialVar(). Continue until done the input string.
* Arguments:
* Input buffer (string) to be expanded and output.
* Pointer to element under consideration.
* FILE pointer to where to write output.
* Flag saying whether to track the character position we're on
* (passed to OutputString).
*/
void
ProcesOutputSpec(
char *ib,
Element_t *e,
FILE *fp,
int track_pos
)
{
char obuf[LINESIZE];
char vbuf[LINESIZE];
char *dest, vname[LINESIZE], *cp;
int esc;
obuf[0] = EOS; /* start with empty output buffer */
ExpandVariables(ib, vbuf, e); /* expand regular variables */
ib = vbuf;
dest = obuf;
esc = 0;
while (*ib) {
/* Is esc-$ next? If so, just copy the '$'. */
if (*ib == '\\' && ib[1] == '$') {
ib++; /* skip esc */
*dest++ = *ib++; /* copy $ */
continue;
}
/* If not a $, it's a regular char. Just copy it and go to next. */
if (*ib != '$') { /* look for att/variable marker */
*dest++ = *ib++; /* it's not. just copy character */
continue;
}
/* We have a $. What we have must be a "special variable" since
* regular variables have already been expanded, or just a lone $. */
if (ib[1] != L_CURLY) { /* just a stray dollar sign (no variable) */
*dest++ = *ib++;
continue;
}
ib++; /* point past $ */
/* Output what we have in buffer so far. */
*dest = EOS; /* terminate string */
if (obuf[0]) OutputString(obuf, fp, track_pos);
dest = obuf; /* ready for new stuff in buffer */
if (!strchr(ib, R_CURLY)) {
fprintf(stderr, "Mismatched braces in TranSpec: %s\n", ib);
/* how do we recover from this? */
}
ib++;
cp = vname;
while (*ib && *ib != R_CURLY) *cp++ = *ib++;
*cp = EOS; /* terminate att/var name */
ib++; /* point past closing curly */
/* we now have special variable name (stuff in curly {}'s) in vname */
ExpandSpecialVar(&vname[1], e, fp, track_pos);
}
*dest = EOS; /* terminate string in output buffer */
if (obuf[0]) OutputString(obuf, fp, track_pos);
}
/* ______________________________________________________________________ */
/* Find the translation spec for the given tag.
* Returns pointer to first spec that matches (name, depth, etc., of tag).
* Arguments:
* e -- Pointer to element under consideration.
* specID -- name of specid that we're looking for
* Return:
* Pointer to translation spec that matches given element's context.
*/
Trans_t *
FindTrans(
Element_t *e,
int specID
)
{
char context[LINESIZE], buf[LINESIZE], *cp, **vec, *atval;
int i, a, match;
Trans_t *t, *tt;
/* loop through all transpecs */
for (t=TrSpecs; t; t=t->next)
{
/* Only one of gi or gilist will be set. */
/* Check if elem name matches */
if (t->gi && !StrEq(t->gi, e->gi) && !specID) continue;
/* test if we're looking for a specific specID and then if
* this is it.. */
if (specID)
if (!t->my_id || (specID != t->my_id))
continue;
/* Match one in the list of GIs? */
if (t->gilist) {
for (match=0,vec=t->gilist; *vec; vec++) {
if (StrEq(*vec, e->gi)) {
match = 1;
break;
}
}
if (!match) continue;
}
/* Check context */
/* Special case of context */
if (t->parent)
if (!QRelation(e, t->parent, REL_Parent)) continue;
if (t->context) { /* no context specified -> a match */
FindContext(e, t->depth, context);
/* If reg expr set, do regex compare; else just string compare. */
if (t->context_re) {
if (! tpt_regexec(t->context_re, context)) continue;
}
else {
/* Is depth of spec deeper than element's depth? */
if (t->depth > e->depth) continue;
/* See if context of element matches "context" of transpec */
match = ( (t->context[0] == context[0]) &&
!strcmp(t->context, context) );
if (!match) continue;
}
}
/* Check attributes. Loop through list, comparing each. */
if (t->nattpairs) { /* no att specified -> a match */
for (match=1,a=0; a<t->nattpairs; a++) {
if (!(atval = FindAttValByName(e, t->attpair[a].name))) {
match = 0;
break;
}
if (!tpt_regexec(t->attpair[a].rex, atval)) match = 0;
}
if (!match) continue;
}
/* Check relationships: child, parent, ancestor, sib, ... */
if (t->relations) {
Mapping_t *r;
match = 1;
for (r=t->relations->maps,i=0; i<t->relations->n_used; i++) {
if (!CheckRelation(e, r[i].name, r[i].sval, 0, 0, RA_Current)) {
match = 0;
break;
}
}
if (!match) continue;
}
/* check this element's parent's attribute */
if (t->pattrset && e->parent) {
char *p, **tok;
i = 2;
match = 1;
tok = Split(t->pattrset, &i, S_STRDUP);
if ( i == 2 ) {
p = FindAttValByName(e->parent, tok[0]);
ExpandVariables(tok[1], buf, 0);
if ( !p || strcmp(p, buf) )
match = 0;
} else {
if (!FindAttValByName(e->parent, t->pattrset))
match = 0;
}
free(tok[0]);
if (!match) continue;
}
/* check this element's "birth order" */
if (t->nth_child) {
/* First one is called "1" by the user. Internally called "0". */
i = t->nth_child;
if (i > 0) { /* positive # -- count from beginning */
if (e->my_eorder != (i-1)) continue;
}
else { /* negative # -- count from end */
i = e->parent->necont - i;
if (e->my_eorder != i) continue;
}
}
/* check that variables match */
if (t->var_name) {
cp = FindMappingVal(Variables, t->var_name);
if (!cp || strcmp(cp, t->var_value)) continue;
}
/* check for variable regular expression match */
if ( t->var_RE_name ) {
cp = FindMappingVal(Variables, t->var_RE_name);
if (!cp || !tpt_regexec(t->var_RE_value, cp)) continue;
}
/* check content */
if (t->content) { /* no att specified -> a match */
for (match=0,i=0; i<e->ndcont; i++) {
if (tpt_regexec(t->content_re, e->dcont[i])) {
match = 1;
break;
}
}
if (!match) continue;
}
/* -------- at this point we've passed all criteria -------- */
/* See if we should be using another transpec's actions. */
if (t->use_id) {
if (t->use_id < 0) return &NullTrans; /* missing? */
/* see if we have a pointer to that transpec */
if (t->use_trans) return t->use_trans;
for (tt=TrSpecs; tt; tt=tt->next) {
if (t->use_id == tt->my_id) {
/* remember pointer for next time */
t->use_trans = tt;
return t->use_trans;
}
}
t->use_id = -1; /* flag it as missing */
fprintf(stderr, "Warning: transpec ID (%d) not found for %s.\n",
t->use_id, e->gi);
return &NullTrans;
}
return t;
}
/* At this point, we have not found a matching spec. See if there
* is a wildcard, and if so, use it. (Wildcard GI is named "*".) */
if ((t = FindTransByName("*"))) return t;
if (warnings && !specID)
fprintf(stderr, "Warning: transpec not found for %s\n", e->gi);
/* default spec - pass character data and descend node */
return &NullTrans;
}
/* ______________________________________________________________________ */
/* Find translation spec by (GI) name. Returns the first one that matches.
* Arguments:
* Pointer to name of transpec (the "gi" field of the Trans structure).
* Return:
* Pointer to translation spec that matches name.
*/
Trans_t *
FindTransByName(
char *s
)
{
Trans_t *t;
for (t=TrSpecs; t; t=t->next) {
/* check if tag name matches (first check 1st char, for efficiency) */
if (t->gi) {
if (*(t->gi) != *s) continue; /* check 1st character */
if (!strcmp(t->gi, s)) return t;
}
}
return NULL;
}
/* Find translation spec by its ID (SpecID).
* Arguments:
* Spec ID (an int).
* Return:
* Pointer to translation spec that matches name.
*/
Trans_t *
FindTranByID(int n)
{
Trans_t *t;
for (t=TrSpecs; t; t=t->next)
if (n == t->my_id) return t;
return NULL;
}
/* ______________________________________________________________________ */
/* Process a "chunk" of content data of an element.
* Arguments:
* Pointer to data content to process
* FILE pointer to where to write output.
*/
void
DoData(
char *data,
FILE *fp,
Trans_t *t
)
{
char *cp, buf[LINESIZE], *dp, *sub, prev;
int i, j, mapped;
Mapping_t *m;
/* Worry about embedded newlines? */
if (!fp) return;
/* CLEANUP: this should really all be done in OutputString(). (I think) */
if (nCharMap) {
/* for each character, see if it's mapped to something else */
for (prev=0,cp=data,dp=buf; *cp; cp++) {
if (prev == '\\') {
*dp++ = *cp;
prev = *cp;
continue;
}
for (mapped=0,i=0; !t->verbatim && (i<nCharMap); i++) {
if ((*cp != CharMap[i].name[0]) ||
((*cp == '\\') && (*(cp+1) != '\\')) )
continue;
if ( *cp == '\\' )
*cp++;
sub = CharMap[i].sval;
while (*sub) *dp++ = *sub++;
mapped = 1;
break;
}
for ( j=tsStacki; j >= 0; j-- ) {
if (tsStack[j]->substitute) {
for (m=tsStack[j]->substitute->maps,i=0;
i<tsStack[j]->substitute->n_used; i++) {
if ( *cp == m[i].name[0] ) {
sub = m[i].sval;
while (*sub)
*dp++ = *sub++;
mapped = 2;
break;
}
}
if ( mapped == 2 )
break;
}
}
if ( *cp == -1 ) *cp = '';
if (!mapped && t->trim && (strchr(t->trim, *cp) != NULL)) {
continue;
}
if (!mapped) *dp++ = *cp;
prev = *cp;
}
*dp = EOS;
dp = buf;
}
else dp = data;
OutputString(dp, fp, 1);
}
/* ______________________________________________________________________ */
/* Handle a processing instruction. This is done similarly to elements,
* where we find a transpec, then do what it says. Differences: PI names
* start with '_' in the spec file (if a GI does not start with '_', it
* may be forced to upper case, sgmls keeps PIs as mixed case); the args
* to the PI are treated as the data of an element. Note that a PI wildcard
* is "_*"
* Arguments:
* Pointer to the PI.
* FILE pointer to where to write output.
*/
void
DoPI(
char *pi,
FILE *fp
)
{
char buf[250], **tok;
int n;
Trans_t *t;
buf[0] = '_';
strcpy(&buf[1], pi);
n = 2;
tok = Split(buf, &n, 0);
if ((t = FindTransByName(tok[0])) ||
(t = FindTransByName("_*"))) {
if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
else {
if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
if (t->ignore != IGN_DATA) /* skip data nodes? */
if (n > 1) OutputString(tok[1], fp, 1);
if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
}
if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
}
else {
/* If not found, just print the PI in square brackets, along
* with a warning message. */
fprintf(fp, "[%s]", pi);
if (warnings) fprintf(stderr, "Warning: Unrecognized PI: [%s]\n", pi);
}
}
/* ______________________________________________________________________ */
/* Set and increment variables, as appropriate, if the transpec says to.
* Arguments:
* Pointer to translation spec for current element.
*/
static void
set_and_increment(
Trans_t *t,
Element_t *e
)
{
Mapping_t *m;
int i, inc, n;
char *cp, buf[50];
char ebuf[5000];
/* set/reset variables */
if (t->set_var) {
for (m=t->set_var->maps,i=0; i<t->set_var->n_used; i++) {
ExpandVariables(m[i].sval, ebuf, e); /* do some expansion */
SetMappingNV(Variables, m[i].name, ebuf);
}
}
/* increment counters */
if (t->incr_var) {
for (m=t->incr_var->maps,i=0; i<t->incr_var->n_used; i++) {
cp = FindMappingVal(Variables, m[i].name);
/* if not set at all, set to 1 */
if (!cp) SetMappingNV(Variables, m[i].name, "1");
else {
if (isdigit(*cp) || (*cp == '-' && isdigit(cp[1]))) {
n = atoi(cp);
if (m[i].sval && isdigit(*m[i].sval)) inc = atoi(m[i].sval);
else inc = 1;
sprintf(buf, "%d", (n + inc));
SetMappingNV(Variables, m[i].name, buf);
} else
if (!*(cp+1) && isalpha(*cp)) {
buf[0] = *cp + 1;
buf[1] = 0;
SetMappingNV(Variables, m[i].name, buf);
}
}
}
}
}
/* ______________________________________________________________________ */
/* Translate one element.
* Arguments:
* Pointer to element under consideration.
* FILE pointer to where to write output.
* Pointer to translation spec for current element, or null.
*/
void
TransElement(
Element_t *e,
FILE *fp,
Trans_t *t
)
{
int i;
if (!t) t = ((e && e->trans) ? e->trans : &NullTrans);
/* see if we should quit. */
if (t->quit) {
fprintf(stderr, "Quitting at location:\n");
PrintLocation(e, fp);
fprintf(stderr, "%s\n", t->quit);
exit(1);
}
/* stack this element */
PushTranspecName(t);
/* See if we want to replace subtree (do text, don't descend subtree) */
if (t->replace) {
ProcesOutputSpec(t->replace, e, fp, 1);
if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
set_and_increment(t, e); /* adjust variables, if appropriate */
PopTranspecName();
return;
}
if (t->starttext) ProcesOutputSpec(t->starttext, e, fp, 1);
if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
/* Process data for this node and descend child elements/nodes. */
if (t->ignore != IGN_ALL) {
/* Is there a "generated" node at the front of this one? */
if (e->gen_trans[0]) {
Trans_t *tp;
if ((tp = FindTranByID(e->gen_trans[0]))) {
if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
if (tp->message) ProcesOutputSpec(tp->message, e, stderr, 0);
if (tp->endtext) ProcesOutputSpec(tp->endtext, e, fp, 1);
}
}
/* Loop thruthe "nodes", whether data, child element, or PI. */
for (i=0; i<e->ncont; i++) {
if (IsContElem(e,i)) {
if (t->ignore != IGN_CHILDREN) /* skip child nodes? */
TransElement(ContElem(e,i), fp, NULL);
}
else if (IsContData(e,i)) {
if (t->ignore != IGN_DATA) /* skip data nodes? */
DoData(ContData(e,i), fp, t);
}
else if (IsContPI(e,i))
DoPI(e->cont[i].ch.data, fp);
}
/* Is there a "generated" node at the end of this one? */
if (e->gen_trans[1]) {
Trans_t *tp;
if ((tp = FindTranByID(e->gen_trans[1]))) {
if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
if (tp->message) ProcesOutputSpec(tp->message, e, stderr, 0);
if (tp->endtext) ProcesOutputSpec(tp->endtext, e, fp, 1);
}
}
}
set_and_increment(t, e); /* adjust variables, if appropriate */
if (t->endtext) ProcesOutputSpec(t->endtext, e, fp, 1);
e->processed = 1;
PopTranspecName();
}
/* ______________________________________________________________________ */
/* Check if element matches specified relationship, and, if it does, perform
* action on either current element or matching element (depends on flag).
* Arguments:
* Pointer to element under consideration.
* Pointer to relationship name.
* Pointer to related element name (GI).
* Pointer to action to take (string - turned into an int).
* FILE pointer to where to write output.
* Flag saying whether to do action on related element (RA_Related)
* or on current element (RA_Current).
* Return:
* Bool, saying whether (1) or not (0) relationship matches.
*/
int
CheckRelation(
Element_t *e,
char *relname, /* relationship name */
char *related, /* related element */
char *actname, /* action to take */
FILE *fp,
RelAction_t flag
)
{
Element_t *ep;
Relation_t r;
if ((r = FindRelByName(relname)) == REL_Unknown) return 0;
if (!(ep=QRelation(e, related, r))) return 0;
if (!actname) return 1; /* no action - return what we found */
switch (flag) {
case RA_Related: TranTByAction(ep, actname, fp); break;
case RA_Current: TranTByAction(e, actname, fp); break;
}
return 1;
}
/* ______________________________________________________________________ */
/* Perform action given by a SpecID on the given element.
* Arguments:
* Pointer to element under consideration.
* SpecID of action to perform.
* FILE pointer to where to write output.
*
*/
void
TranByAction(
Element_t *e,
int n,
FILE *fp
)
{
Trans_t *t;
t = FindTranByID(n);
if (!t) {
fprintf(stderr, "Could not find named action for %d.\n", n);
return;
}
TransElement(e, fp, t);
}
/* ______________________________________________________________________ */
/* Perhaps perform action given by a SpecID on the given element.
* Arguments:
* Pointer to element under consideration.
* SpecID of action to perform. Unlike TranByAction, this is the argument
* as it occurred in the transpec (ASCII) and may end with the letter
* "t" which means that the transpec mustpass criteria selection.
* FILE pointer to where to write output.
*/
void
TranTByAction(
Element_t *e,
char *strn,
FILE *fp
)
{
int n;
Trans_t *t;
n = atoi(strn);
if ( strn[strlen(strn)-1] != 't' ) {
t = FindTranByID(n);
if (!t) {
fprintf(stderr, "Could not find named action for %d.\n", n);
return;
}
} else {
t = FindTrans(e, n);
if ( !t || !t->my_id )
return;
}
TransElement(e, fp, t);
}
/* ______________________________________________________________________ */
/* push the name of a transpec (the new active one) onto the stack
* Arguments:
* transpec name
*/
void
PushTranspecName(
Trans_t *t
)
{
if ( tsStacki >= MAXTRANSPECDEPTH ) {
fprintf(stderr, "Transpec stack overflow (%d)\n", MAXTRANSPECDEPTH);
exit(1);
}
tsStack[++tsStacki] = t;
}
/* ______________________________________________________________________ */
/* pop the top name of a transpec off the stack
*/
void
PopTranspecName(
Trans_t *t
)
{
if ( tsStacki < 0 ) {
fprintf(stderr, "Transpec stack underflow\n");
exit(1);
}
tsStacki--;
}