/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts. * All rights reserved. */ /* * 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. */ /* ________________________________________________________________________ * * instant - a program to manipulate SGML instances. * * This module is for handling "special variables". These act a lot like * procedure calls * ________________________________________________________________________ */ #ifndef lint static char *RCSid = "$XConsortium: tranvar.c /main/7 1996/08/08 14:42:09 cde-hp $"; #endif #include #include #include #include #include #include #include #include #include "general.h" #include "translate.h" static char **idrefs; /* list of IDREF att names to follow */ static char *def_idrefs[] = { "LINKEND", "LINKENDS", "IDREF", 0 }; /* forward references */ void ChaseIDRefs(Element_t *, char *, int, FILE *); void Find(Element_t *, int, char **, FILE *); void GetIDREFnames(); static void OutputCDATA(Content_t *cp, void *client_data); typedef struct _cdata_info { int track_pos; FILE *fp; } OutputCDATA_info_t; /* ______________________________________________________________________ */ /* Handle "special" variable - read file, run command, do action, etc. * Arguments: * Name of special variable to expand. * 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). */ ContParse_t ExpandSpecialVar( char *name, Element_t *e, FILE *fp, int track_pos ) { FILE *infile; char buf[LINESIZE], tempbuf[LINESIZE], *cp, *atval, letter; char **tok; int ntok, n, i, action, action1, number; Element_t *ep; Trans_t *t, *tt; static char *s_A, *s_C; /* Run a command. * Format: _! command args ... */ if (*name == '!') { name++; if ((infile = popen(name, "r"))) { while (fgets(buf, LINESIZE, infile)) FPuts(buf, fp); pclose(infile); FFlush(fp); } else { fprintf(stderr, "Could not start program '%s': %s", name, strerror(errno)); } return CONT_CONTINUE; } /* See if caller wants one of the tokens from _eachatt or _eachcon. * If so, output it and return. (Yes, I admit that this is a hack.) */ if (*name == 'A' && name[1] == EOS && s_A) { OutputString(s_A, fp, track_pos); return CONT_CONTINUE; } if (*name == 'C' && name[1] == EOS && s_C) { OutputString(s_C, fp, track_pos); return CONT_CONTINUE; } ntok = 0; tok = Split(name, &ntok, 0); /* Include another file. * Format: _include filename */ if (StrEq(tok[0], "include")) { name = tok[1]; if (ntok > 1 ) { if ((infile=OpenFile(name)) == NULL) { sprintf(buf, "Can not open included file '%s'", name); perror(buf); return CONT_CONTINUE; } while (fgets(buf, LINESIZE, infile)) FPuts(buf, fp); fclose(infile); } else fprintf(stderr, "No file name specified for include\n"); return CONT_CONTINUE; } /* Print location (nearest title, line no, path). * Format: _location */ else if (StrEq(tok[0], "location")) { PrintLocation(e, fp); } /* Print path to this element. * Format: _path */ else if (StrEq(tok[0], "path")) { (void)FindElementPath(e, buf); OutputString(buf, fp, track_pos); } /* Print name of this element (gi). * Format: _gi [M|L|U] */ else if (StrEq(tok[0], "gi")) { strcpy(buf, e->gi); if (ntok >= 2) { if (*tok[1] == 'L' || *tok[1] == 'l' || *tok[1] == 'M' || *tok[1] == 'm') { for (cp=buf; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); } if (*tok[1] == 'M' || *tok[1] == 'm') if (islower(buf[0])) buf[0] = toupper(buf[0]); } OutputString(buf, fp, track_pos); } /* Print name of the parent of this element (parent). * If a parent number is given, go that far up the parent tree * (e.g., "_parent 1 U" returns the parent in upper case * "_parent 2 L" returns the grandparent in lower case * "_parent 0" is equivalent to "_gi" * "_parent" is equivalent to "_parent 1") * Format: _parent [] [M|L|U] */ else if (StrEq(tok[0], "parent")) { number = 1; letter = 'U'; if (ntok >= 2) { if (isdigit(*tok[1])) { number = atoi(tok[1]); if (ntok >= 3) { letter = *tok[2]; } } else { letter = *tok[1]; } } ep = e; while (--number >= 0) { if (ep) ep = ep->parent; } if (ep) { strcpy(buf, ep->gi); } else { *buf = 0; } if (letter == 'L' || letter == 'l' || letter == 'M' || letter == 'm') { for (cp=buf; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); } if (letter == 'M' || letter == 'm') if (islower(buf[0])) buf[0] = toupper(buf[0]); OutputString(buf, fp, track_pos); } /* Print filename of this element's associated external entity. * Format: _filename */ else if (StrEq(tok[0], "filename")) { if (!e->entity) { fprintf(stderr, "Expected ext entity (internal error? bug?):\n"); PrintLocation(e, stderr); return CONT_CONTINUE; } if (!e->entity->fname) { fprintf(stderr, "Expected filename "); if (e->entity->sysid) { fprintf(stderr, "(could not find \"%s\"):\n", e->entity->sysid); } else if (e->entity->pubid) { fprintf(stderr, "(could not resolve \"%s\"):\n", e->entity->pubid); } else { fprintf(stderr, "(internal error? bug?):\n"); } PrintLocation(e, stderr); return CONT_CONTINUE; } OutputString(e->entity->fname, fp, track_pos); } /* Value of parent's attribute, by attr name. * Format: _pattr attname */ else if (StrEq(tok[0], "pattr")) { ep = e->parent; if (!ep) { fprintf(stderr, "Element does not have a parent:\n"); PrintLocation(ep, stderr); return CONT_CONTINUE; } if ((atval = FindAttValByName(ep, tok[1]))) { OutputString(atval, fp, track_pos); } } /* Use an action, given transpec's SID. * Format: _action action */ else if (StrEq(tok[0], "action")) { action = atoi(tok[1]); if (action) TranByAction(e, action, fp); } /* Number of child elements of this element. * Format: _nchild */ else if (StrEq(tok[0], "nchild")) { if (ntok > 1) { for (n=0,i=0; inecont; i++) if (StrEq(e->econt[i]->gi, tok[1])) n++; } else n = e->necont; sprintf(buf, "%d", n); OutputString(buf, fp, track_pos); } /* number of 1st child's child elements (grandchildren from first child). * Format: _n1gchild */ else if (StrEq(tok[0], "n1gchild")) { if (e->necont) { sprintf(buf, "%d", e->econt[0]->necont); OutputString(buf, fp, track_pos); } } /* Chase this element's pointers until we hit the named GI. * Do the action if it matches. * Format: _chasetogi gi action */ else if (StrEq(tok[0], "chasetogi")) { if (ntok < 3) { fprintf(stderr, "Error: Not enough args for _chasetogi.\n"); return CONT_CONTINUE; } action = atoi(tok[2]); if (action) ChaseIDRefs(e, tok[1], action, fp); } /* Follow link to element pointed to, then do action. * Format: _followlink [attname] action. */ else if (StrEq(tok[0], "followlink")) { char **s; if (ntok > 2) { action = atoi(tok[2]); if ((atval = FindAttValByName(e, tok[1]))) { if ((ep = FindElemByID(atval))) { TranByAction(ep, action, fp); return CONT_CONTINUE; } } else fprintf(stderr, "Error: Did not find attr: %s.\n", tok[1]); return CONT_CONTINUE; } else action = atoi(tok[1]); GetIDREFnames(); for (s=idrefs; *s; s++) { /* is this IDREF attr set? */ if ((atval = FindAttValByName(e, *s))) { ntok = 0; tok = Split(atval, &ntok, S_STRDUP); /* we'll follow the first one... */ if ((ep = FindElemByID(tok[0]))) { TranByAction(ep, action, fp); return CONT_CONTINUE; } else fprintf(stderr, "Error: Can not find elem for ID: %s.\n", tok[0]); } } fprintf(stderr, "Error: Element does not have IDREF attribute set:\n"); PrintLocation(e, stderr); return CONT_CONTINUE; } /* Starting at this element, decend tree (in-order), finding GI. * Do the action if it matches. * Format: _find args ... */ else if (StrEq(tok[0], "find")) { Find(e, ntok, tok, fp); } /* Starting at this element's parent, decend tree (in-order), finding GI. * Do the action if it matches. * Format: _pfind args ... */ else if (StrEq(tok[0], "pfind")) { Find(e->parent ? e->parent : e, ntok, tok, fp); } /* Content is supposed to be a list of IDREFs. Follow each, doing action. * If 2 actions are specified, use 1st for the 1st ID, 2nd for the rest. * Format: _namelist action [action2] */ else if (StrEq(tok[0], "namelist")) { int id; action1 = atoi(tok[1]); if (ntok > 2) action = atoi(tok[2]); else action = action1; for (i=0; indcont; i++) { n = 0; tok = Split(e->dcont[i], &n, S_STRDUP); for (id=0; id 3) action = atoi(tok[2]); else action = action1; for (i=0; indcont; i++) { n = 0; tok = Split(e->dcont[i], &n, S_STRDUP|S_ALVEC); for (id=0; id 3) action = atoi(tok[3]); else action = action1; if ((atval = FindAttValByName(e, tok[1]))) { n = 0; tok = Split(atval, &n, S_STRDUP|S_ALVEC); for (id=0; id= 4) { if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Current)) { /* action not done, see if alt action specified */ if (ntok >= 5) TranByAction(e, atoi(tok[4]), fp); } } } /* Do action on followed element if element has [relationship] with gi. * Format: _followrel relationship gi action */ else if (StrEq(tok[0], "followrel")) { if (ntok >= 4) (void)CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Related); } /* Find element with matching ID and do action. If action not specified, * choose the right one appropriate for its context. * Format: _id id [action] */ else if (StrEq(tok[0], "id")) { if (ntok > 2) action = atoi(tok[2]); else action = 0; if ((ep = FindElemByID(tok[1]))) { if (action) TranByAction(ep, action, fp); else { t = FindTrans(ep); TransElement(ep, fp, t); } } } /* Set variable to value. * Format: _set name value */ else if (StrEq(tok[0], "set")) { SetMappingNV(Variables, tok[1], tok[2]); } /* Do action if variable is set, optionally to value. * If not set, do nothing. * Format: _isset varname [value] action */ else if (StrEq(tok[0], "isset")) { if ((cp = FindMappingVal(Variables, tok[1]))) { if (ntok == 3) TranByAction(e, atoi(tok[2]), fp); else if (ntok > 3 && !strcmp(cp, tok[2])) TranByAction(e, atoi(tok[3]), fp); } } /* If variable is unset or not set to optional value, return an * indication that the parsing of this specification should * continue; otherwise, return an indication that the parse should * quit. */ else if (StrEq(tok[0], "break")) { if ((cp = FindMappingVal(Variables, tok[1]))) { if ((ntok <= 2) || (strcmp(cp, tok[2]) == 0)) return CONT_BREAK; } return CONT_CONTINUE; } /* Insert a node into the tree at start/end, pointing to action to perform. * Format: _insertnode S|E action */ else if (StrEq(tok[0], "insertnode")) { action = atoi(tok[2]); if (*tok[1] == 'S') e->gen_trans[0] = action; else if (*tok[1] == 'E') e->gen_trans[1] = action; } /* Do an OSF DTD table spec for TeX or troff. Looks through attributes * and determines what to output. "check" means to check consistency, * and print error messages. * This is (hopefully) the only hard-coded part of the program. * Format: _osftable [tex|roff|check] [cell|top|bottom|rowend] */ else if (StrEq(tok[0], "osftable")) { OSFtable(e, fp, tok, ntok); } /* Do action if element's attr is set, optionally to value. * If not set, do nothing. * Format: _attval att [value] action */ else if (StrEq(tok[0], "attval")) { if ((atval = FindAttValByName(e, tok[1]))) { if (ntok == 3) TranByAction(e, atoi(tok[2]), fp); else if (ntok > 3 && !strcmp(atval, tok[2])) TranByAction(e, atoi(tok[3]), fp); } } /* Same thing, but look at parent */ else if (StrEq(tok[0], "pattval")) { if ((atval = FindAttValByName(e->parent, tok[1]))) { if (ntok == 3) { TranByAction(e, atoi(tok[2]), fp); } if (ntok > 3 && !strcmp(atval, tok[2])) TranByAction(e, atoi(tok[3]), fp); } } /* Print each attribute and value for the current element, hopefully * in a legal sgml form: natts; i++) { if (i != 0) Putc(' ', fp); FPuts(e->atts[i].name, fp); FPuts("=\"", fp); FPuts(e->atts[i].sval, fp); Putc('"', fp); } } /* Print the element's input filename, and optionally, the line number. * Format: _infile [line] */ else if (StrEq(tok[0], "infile")) { if (e->infile) { if (ntok > 1 && !strcmp(tok[1], "root")) { strcpy(buf, e->infile); if ((cp = strrchr(buf, '.'))) *cp = EOS; FPuts(buf, fp); } else { FPuts(e->infile, fp); if (ntok > 1 && !strcmp(tok[1], "line")) { sprintf(tempbuf, " %d", e->lineno); FPuts(tempbuf, fp); } } return CONT_CONTINUE; } else FPuts("input-file??", fp); } /* Get value of an environement variable */ else if (StrEq(tok[0], "env")) { if (ntok > 1 && (cp = getenv(tok[1]))) { OutputString(cp, fp, track_pos); } } /* Get the cdata content of the node (and descendents) */ else if (StrEq(tok[0], "cdata")) { OutputCDATA_info_t client_data; client_data.track_pos = track_pos; client_data.fp = fp; DescendTree(e, 0, 0, OutputCDATA, (void *) &client_data); } /* Something unknown */ else { fprintf(stderr, "Unknown special variable: %s\n", tok[0]); tt = e->trans; if (tt && tt->lineno) fprintf(stderr, "Used in transpec, line %d\n", tt->lineno); } return CONT_CONTINUE; } /* ______________________________________________________________________ */ /* A routine to pass to DescendTree(). This routine will be called * on each data node in the tree from the current element (e) down - * putting any cdata on the output stream. * Arguments: * Pointer to content of the node * Client data - holds fp and track_pos from ExpandSpecialVariable() */ static void OutputCDATA(Content_t *cp, void *client_data) { OutputCDATA_info_t *pInfo = (OutputCDATA_info_t *) client_data; if (cp->type == CMD_DATA) OutputString(cp->ch.data, pInfo->fp, pInfo->track_pos); } /* ______________________________________________________________________ */ /* Chase IDs until we find an element whose GI matches. We also check * child element names, not just the names of elements directly pointed * at (by IDREF attributes). */ void GetIDREFnames(void) { char *cp; if (!idrefs) { /* did user or transpec set the variable */ if ((cp = FindMappingVal(Variables, "link_atts"))) idrefs = Split(cp, 0, S_STRDUP|S_ALVEC); else idrefs = def_idrefs; } } /* ______________________________________________________________________ */ /* Chase ID references - follow IDREF(s) attributes until we find * a GI named 'gi', then perform given action on that GI. * Arguments: * Pointer to element under consideration. * Name of GI we're looking for. * Spec ID of action to take. * FILE pointer to where to write output. */ void ChaseIDRefs( Element_t *e, char *gi, int action, FILE *fp ) { int ntok, i, ei; char **tok, **s, *atval; /* First, see if we got what we came for with this element */ if (StrEq(e->gi, gi)) { TranByAction(e, action, fp); return; } GetIDREFnames(); /* loop for each attribute of type IDREF(s) */ for (s=idrefs; *s; s++) { /* is this IDREF attr set? */ if ((atval = FindAttValByName(e, *s))) { ntok = 0; tok = Split(atval, &ntok, 0); for (i=0; igi, gi)) { /* process using named action */ TranByAction(e, action, fp); return; } else { /* this elem itself did not match, try its children */ for (ei=0; einecont; ei++) { if (StrEq(e->econt[ei]->gi, gi)) { TranByAction(e->econt[ei], action, fp); return; } } /* try this elem's IDREF attributes */ ChaseIDRefs(e, gi, action, fp); return; } } else { /* should not happen, since parser checks ID/IDREFs */ fprintf(stderr, "Error: Could not find ID %s\n", atval); return; } } } } /* if the pointers didn't lead to the GI, give error */ if (!s) fprintf(stderr, "Error: Could not find '%s'\n", gi); } /* ______________________________________________________________________ */ /* state to pass to recursive routines - so we don't have to use * global variables. */ typedef struct { char *gi; char *gi2; int action; Element_t *elem; FILE *fp; } Descent_t; static void tr_find_gi( Element_t *e, Descent_t *ds ) { if (StrEq(ds->gi, e->gi)) if (ds->action) TranByAction(e, ds->action, ds->fp); } static void tr_find_gipar( Element_t *e, Descent_t *ds ) { if (StrEq(ds->gi, e->gi) && e->parent && StrEq(ds->gi2, e->parent->gi)) if (ds->action) TranByAction(e, ds->action, ds->fp); } static void tr_find_attr( Element_t *e, Descent_t *ds ) { char *atval; if ((atval = FindAttValByName(e, ds->gi)) && StrEq(ds->gi2, atval)) TranByAction(e, ds->action, ds->fp); } static void tr_find_parent( Element_t *e, Descent_t *ds ) { if (QRelation(e, ds->gi, REL_Parent)) { if (ds->action) TranByAction(e, ds->action, ds->fp); } } /* ______________________________________________________________________ */ /* Descend tree, finding elements that match criteria, then perform * given action. * Arguments: * Pointer to element under consideration. * Number of tokens in special variable. * Vector of tokens in special variable (eg, "find" "gi" "TITLE") * FILE pointer to where to write output. */ void Find( Element_t *e, int ac, char **av, FILE *fp ) { Descent_t DS; /* state passed to recursive routine */ memset(&DS, 0, sizeof(Descent_t)); DS.elem = e; DS.fp = fp; /* see if we should start at the top of instance tree */ if (StrEq(av[1], "top")) { av++; ac--; e = DocTree; } if (ac < 4) { fprintf(stderr, "Bad '_find' specification - missing args.\n"); return; } /* Find elem whose GI is av[2] */ if (StrEq(av[1], "gi")) { DS.gi = av[2]; DS.action = atoi(av[3]); DescendTree(e, tr_find_gi, 0, 0, &DS); } /* Find elem whose GI is av[2] and whose parent GI is av[3] */ else if (StrEq(av[1], "gi-parent")) { DS.gi = av[2]; DS.gi2 = av[3]; DS.action = atoi(av[4]); DescendTree(e, tr_find_gipar, 0, 0, &DS); } /* Find elem whose parent GI is av[2] */ else if (StrEq(av[0], "parent")) { DS.gi = av[2]; DS.action = atoi(av[3]); DescendTree(e, tr_find_parent, 0, 0, &DS); } /* Find elem whose attribute av[2] has value av[3] */ else if (StrEq(av[0], "attr")) { DS.gi = av[2]; DS.gi2 = av[3]; DS.action = atoi(av[4]); DescendTree(e, tr_find_attr, 0, 0, &DS); } } /* ______________________________________________________________________ */