2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
25 * All rights reserved.
29 * Open Software Foundation, Inc.
31 * Permission is hereby granted to use, copy, modify and freely distribute
32 * the software in this file and its documentation for any purpose without
33 * fee, provided that the above copyright notice appears in all copies and
34 * that both the copyright notice and this permission notice appear in
35 * supporting documentation. Further, provided that the name of Open
36 * Software Foundation, Inc. ("OSF") not be used in advertising or
37 * publicity pertaining to distribution of the software without prior
38 * written permission from OSF. OSF makes no representations about the
39 * suitability of this software for any purpose. It is provided "as is"
40 * without express or implied warranty.
42 /* ________________________________________________________________________
44 * instant - a program to manipulate SGML instances.
46 * This module is for handling "special variables". These act a lot like
48 * ________________________________________________________________________
53 "$XConsortium: tranvar.c /main/7 1996/08/08 14:42:09 cde-hp $";
61 #include <sys/types.h>
64 #include <tptregexp.h>
66 #include "translate.h"
68 static char **idrefs; /* list of IDREF att names to follow */
69 static char *def_idrefs[] = { "LINKEND", "LINKENDS", "IDREF", 0 };
71 /* forward references */
72 void ChaseIDRefs(Element_t *, char *, int, FILE *);
73 void Find(Element_t *, int, char **, FILE *);
76 static void OutputCDATA(Content_t *cp, void *client_data);
77 typedef struct _cdata_info {
83 /* ______________________________________________________________________ */
84 /* Handle "special" variable - read file, run command, do action, etc.
86 * Name of special variable to expand.
87 * Pointer to element under consideration.
88 * FILE pointer to where to write output.
89 * Flag saying whether to track the character position we're on
90 * (passed to OutputString).
102 char buf[LINESIZE], tempbuf[LINESIZE], *cp, *atval, letter;
104 int ntok, n, i, action, action1, number;
107 static char *s_A, *s_C;
110 * Format: _! command args ... */
113 if ((infile = popen(name, "r"))) {
114 while (fgets(buf, LINESIZE, infile)) FPuts(buf, fp);
119 fprintf(stderr, "Could not start program '%s': %s",
120 name, strerror(errno));
122 return CONT_CONTINUE;
125 /* See if caller wants one of the tokens from _eachatt or _eachcon.
126 * If so, output it and return. (Yes, I admit that this is a hack.)
128 if (*name == 'A' && name[1] == EOS && s_A) {
129 OutputString(s_A, fp, track_pos);
130 return CONT_CONTINUE;
132 if (*name == 'C' && name[1] == EOS && s_C) {
133 OutputString(s_C, fp, track_pos);
134 return CONT_CONTINUE;
138 tok = Split(name, &ntok, 0);
140 /* Include another file.
141 * Format: _include filename */
142 if (StrEq(tok[0], "include")) {
145 if ((infile=OpenFile(name)) == NULL) {
146 sprintf(buf, "Can not open included file '%s'", name);
148 return CONT_CONTINUE;
150 while (fgets(buf, LINESIZE, infile)) FPuts(buf, fp);
153 else fprintf(stderr, "No file name specified for include\n");
154 return CONT_CONTINUE;
157 /* Print location (nearest title, line no, path).
158 * Format: _location */
159 else if (StrEq(tok[0], "location")) {
160 PrintLocation(e, fp);
163 /* Print path to this element.
165 else if (StrEq(tok[0], "path")) {
166 (void)FindElementPath(e, buf);
167 OutputString(buf, fp, track_pos);
170 /* Print name of this element (gi).
171 * Format: _gi [M|L|U] */
172 else if (StrEq(tok[0], "gi")) {
175 if (*tok[1] == 'L' || *tok[1] == 'l' ||
176 *tok[1] == 'M' || *tok[1] == 'm') {
177 for (cp=buf; *cp; cp++)
178 if (isupper(*cp)) *cp = tolower(*cp);
180 if (*tok[1] == 'M' || *tok[1] == 'm')
181 if (islower(buf[0])) buf[0] = toupper(buf[0]);
183 OutputString(buf, fp, track_pos);
186 /* Print name of the parent of this element (parent).
187 * If a parent number is given, go that far up the parent tree
188 * (e.g., "_parent 1 U" returns the parent in upper case
189 * "_parent 2 L" returns the grandparent in lower case
190 * "_parent 0" is equivalent to "_gi"
191 * "_parent" is equivalent to "_parent 1")
192 * Format: _parent [<number>] [M|L|U] */
193 else if (StrEq(tok[0], "parent")) {
197 if (isdigit(*tok[1])) {
198 number = atoi(tok[1]);
208 while (--number >= 0) {
209 if (ep) ep = ep->parent;
217 if (letter == 'L' || letter == 'l' ||
218 letter == 'M' || letter == 'm') {
219 for (cp=buf; *cp; cp++)
220 if (isupper(*cp)) *cp = tolower(*cp);
222 if (letter == 'M' || letter == 'm')
223 if (islower(buf[0])) buf[0] = toupper(buf[0]);
225 OutputString(buf, fp, track_pos);
228 /* Print filename of this element's associated external entity.
229 * Format: _filename */
230 else if (StrEq(tok[0], "filename")) {
232 fprintf(stderr, "Expected ext entity (internal error? bug?):\n");
233 PrintLocation(e, stderr);
234 return CONT_CONTINUE;
236 if (!e->entity->fname) {
237 fprintf(stderr, "Expected filename ");
238 if (e->entity->sysid) {
240 "(could not find \"%s\"):\n",
242 } else if (e->entity->pubid) {
244 "(could not resolve \"%s\"):\n",
247 fprintf(stderr, "(internal error? bug?):\n");
249 PrintLocation(e, stderr);
250 return CONT_CONTINUE;
252 OutputString(e->entity->fname, fp, track_pos);
255 /* Value of parent's attribute, by attr name.
256 * Format: _pattr attname */
257 else if (StrEq(tok[0], "pattr")) {
260 fprintf(stderr, "Element does not have a parent:\n");
261 PrintLocation(ep, stderr);
262 return CONT_CONTINUE;
264 if ((atval = FindAttValByName(ep, tok[1]))) {
265 OutputString(atval, fp, track_pos);
269 /* Use an action, given transpec's SID.
270 * Format: _action action */
271 else if (StrEq(tok[0], "action")) {
272 action = atoi(tok[1]);
273 if (action) TranByAction(e, action, fp);
276 /* Number of child elements of this element.
278 else if (StrEq(tok[0], "nchild")) {
280 for (n=0,i=0; i<e->necont; i++)
281 if (StrEq(e->econt[i]->gi, tok[1])) n++;
284 sprintf(buf, "%d", n);
285 OutputString(buf, fp, track_pos);
288 /* number of 1st child's child elements (grandchildren from first child).
289 * Format: _n1gchild */
290 else if (StrEq(tok[0], "n1gchild")) {
292 sprintf(buf, "%d", e->econt[0]->necont);
293 OutputString(buf, fp, track_pos);
297 /* Chase this element's pointers until we hit the named GI.
298 * Do the action if it matches.
299 * Format: _chasetogi gi action */
300 else if (StrEq(tok[0], "chasetogi")) {
302 fprintf(stderr, "Error: Not enough args for _chasetogi.\n");
303 return CONT_CONTINUE;
305 action = atoi(tok[2]);
306 if (action) ChaseIDRefs(e, tok[1], action, fp);
309 /* Follow link to element pointed to, then do action.
310 * Format: _followlink [attname] action. */
311 else if (StrEq(tok[0], "followlink")) {
314 action = atoi(tok[2]);
315 if ((atval = FindAttValByName(e, tok[1]))) {
316 if ((ep = FindElemByID(atval))) {
317 TranByAction(ep, action, fp);
318 return CONT_CONTINUE;
321 else fprintf(stderr, "Error: Did not find attr: %s.\n", tok[1]);
322 return CONT_CONTINUE;
324 else action = atoi(tok[1]);
326 for (s=idrefs; *s; s++) {
327 /* is this IDREF attr set? */
328 if ((atval = FindAttValByName(e, *s))) {
330 tok = Split(atval, &ntok, S_STRDUP);
331 /* we'll follow the first one... */
332 if ((ep = FindElemByID(tok[0]))) {
333 TranByAction(ep, action, fp);
334 return CONT_CONTINUE;
336 else fprintf(stderr, "Error: Can not find elem for ID: %s.\n",
340 fprintf(stderr, "Error: Element does not have IDREF attribute set:\n");
341 PrintLocation(e, stderr);
342 return CONT_CONTINUE;
345 /* Starting at this element, decend tree (in-order), finding GI.
346 * Do the action if it matches.
347 * Format: _find args ... */
348 else if (StrEq(tok[0], "find")) {
349 Find(e, ntok, tok, fp);
352 /* Starting at this element's parent, decend tree (in-order), finding GI.
353 * Do the action if it matches.
354 * Format: _pfind args ... */
355 else if (StrEq(tok[0], "pfind")) {
356 Find(e->parent ? e->parent : e, ntok, tok, fp);
359 /* Content is supposed to be a list of IDREFs. Follow each, doing action.
360 * If 2 actions are specified, use 1st for the 1st ID, 2nd for the rest.
361 * Format: _namelist action [action2] */
362 else if (StrEq(tok[0], "namelist")) {
364 action1 = atoi(tok[1]);
365 if (ntok > 2) action = atoi(tok[2]);
366 else action = action1;
367 for (i=0; i<e->ndcont; i++) {
369 tok = Split(e->dcont[i], &n, S_STRDUP);
370 for (id=0; id<n; id++) {
372 for (cp=tok[id]; *cp; cp++)
373 if (islower(*cp)) *cp = toupper(*cp);
374 if ((e = FindElemByID(tok[id]))) {
375 if (id) TranByAction(e, action, fp);
376 else TranByAction(e, action1, fp); /* first one */
378 else fprintf(stderr, "Error: Can not find ID: %s.\n", tok[id]);
383 /* For each word in the element's content, do action.
384 * Format: _eachcon action [action] */
385 else if (StrEq(tok[0], "eachcon")) {
387 action1 = atoi(tok[1]);
388 if (ntok > 3) action = atoi(tok[2]);
389 else action = action1;
390 for (i=0; i<e->ndcont; i++) {
392 tok = Split(e->dcont[i], &n, S_STRDUP|S_ALVEC);
393 for (id=0; id<n; id++) {
395 TranByAction(e, action, fp);
400 /* For each word in the given attribute's value, do action.
401 * Format: _eachatt attname action [action] */
402 else if (StrEq(tok[0], "eachatt")) {
404 action1 = atoi(tok[2]);
405 if (ntok > 3) action = atoi(tok[3]);
406 else action = action1;
407 if ((atval = FindAttValByName(e, tok[1]))) {
409 tok = Split(atval, &n, S_STRDUP|S_ALVEC);
410 for (id=0; id<n; id++) {
412 if (id) TranByAction(e, action, fp);
413 else TranByAction(e, action1, fp); /* first one */
419 /* Do action on this element if element has [relationship] with gi.
420 * Format: _relation relationship gi action [action] */
421 else if (StrEq(tok[0], "relation")) {
423 if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Current)) {
424 /* action not done, see if alt action specified */
426 TranByAction(e, atoi(tok[4]), fp);
431 /* Do action on followed element if element has [relationship] with gi.
432 * Format: _followrel relationship gi action */
433 else if (StrEq(tok[0], "followrel")) {
435 (void)CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Related);
438 /* Find element with matching ID and do action. If action not specified,
439 * choose the right one appropriate for its context.
440 * Format: _id id [action] */
441 else if (StrEq(tok[0], "id")) {
442 if (ntok > 2) action = atoi(tok[2]);
444 if ((ep = FindElemByID(tok[1]))) {
445 if (action) TranByAction(ep, action, fp);
448 TransElement(ep, fp, t);
453 /* Set variable to value.
454 * Format: _set name value */
455 else if (StrEq(tok[0], "set")) {
456 SetMappingNV(Variables, tok[1], tok[2]);
459 /* Do action if variable is set, optionally to value.
460 * If not set, do nothing.
461 * Format: _isset varname [value] action */
462 else if (StrEq(tok[0], "isset")) {
463 if ((cp = FindMappingVal(Variables, tok[1]))) {
464 if (ntok == 3) TranByAction(e, atoi(tok[2]), fp);
465 else if (ntok > 3 && !strcmp(cp, tok[2]))
466 TranByAction(e, atoi(tok[3]), fp);
470 /* If variable is unset or not set to optional value, return an
471 * indication that the parsing of this specification should
472 * continue; otherwise, return an indication that the parse should
474 else if (StrEq(tok[0], "break")) {
475 if ((cp = FindMappingVal(Variables, tok[1]))) {
476 if ((ntok <= 2) || (strcmp(cp, tok[2]) == 0)) return CONT_BREAK;
478 return CONT_CONTINUE;
481 /* Insert a node into the tree at start/end, pointing to action to perform.
482 * Format: _insertnode S|E action */
483 else if (StrEq(tok[0], "insertnode")) {
484 action = atoi(tok[2]);
485 if (*tok[1] == 'S') e->gen_trans[0] = action;
486 else if (*tok[1] == 'E') e->gen_trans[1] = action;
489 /* Do an OSF DTD table spec for TeX or troff. Looks through attributes
490 * and determines what to output. "check" means to check consistency,
491 * and print error messages.
492 * This is (hopefully) the only hard-coded part of the program.
493 * Format: _osftable [tex|roff|check] [cell|top|bottom|rowend] */
494 else if (StrEq(tok[0], "osftable")) {
495 OSFtable(e, fp, tok, ntok);
498 /* Do action if element's attr is set, optionally to value.
499 * If not set, do nothing.
500 * Format: _attval att [value] action */
501 else if (StrEq(tok[0], "attval")) {
502 if ((atval = FindAttValByName(e, tok[1]))) {
503 if (ntok == 3) TranByAction(e, atoi(tok[2]), fp);
504 else if (ntok > 3 && !strcmp(atval, tok[2]))
505 TranByAction(e, atoi(tok[3]), fp);
508 /* Same thing, but look at parent */
509 else if (StrEq(tok[0], "pattval")) {
510 if ((atval = FindAttValByName(e->parent, tok[1]))) {
512 TranByAction(e, atoi(tok[2]), fp);
514 if (ntok > 3 && !strcmp(atval, tok[2]))
515 TranByAction(e, atoi(tok[3]), fp);
519 /* Print each attribute and value for the current element, hopefully
520 * in a legal sgml form: <elem-name att1="value1" att2="value2:> .
521 * Format: _allatts */
522 else if (StrEq(tok[0], "allatts")) {
523 for (i=0; i<e->natts; i++) {
524 if (i != 0) Putc(' ', fp);
525 FPuts(e->atts[i].name, fp);
527 FPuts(e->atts[i].sval, fp);
532 /* Print the element's input filename, and optionally, the line number.
533 * Format: _infile [line] */
534 else if (StrEq(tok[0], "infile")) {
536 if (ntok > 1 && !strcmp(tok[1], "root")) {
537 strcpy(buf, e->infile);
538 if ((cp = strrchr(buf, '.'))) *cp = EOS;
542 FPuts(e->infile, fp);
543 if (ntok > 1 && !strcmp(tok[1], "line"))
545 sprintf(tempbuf, " %d", e->lineno);
549 return CONT_CONTINUE;
551 else FPuts("input-file??", fp);
554 /* Get value of an environement variable */
555 else if (StrEq(tok[0], "env")) {
556 if (ntok > 1 && (cp = getenv(tok[1]))) {
557 OutputString(cp, fp, track_pos);
561 /* Get the cdata content of the node (and descendents) */
562 else if (StrEq(tok[0], "cdata")) {
563 OutputCDATA_info_t client_data;
565 client_data.track_pos = track_pos;
567 DescendTree(e, 0, 0, OutputCDATA, (void *) &client_data);
570 /* Something unknown */
572 fprintf(stderr, "Unknown special variable: %s\n", tok[0]);
574 if (tt && tt->lineno)
575 fprintf(stderr, "Used in transpec, line %d\n", tt->lineno);
577 return CONT_CONTINUE;
580 /* ______________________________________________________________________ */
581 /* A routine to pass to DescendTree(). This routine will be called
582 * on each data node in the tree from the current element (e) down -
583 * putting any cdata on the output stream.
585 * Pointer to content of the node
586 * Client data - holds fp and track_pos from ExpandSpecialVariable()
589 OutputCDATA(Content_t *cp, void *client_data)
591 OutputCDATA_info_t *pInfo = (OutputCDATA_info_t *) client_data;
593 if (cp->type == CMD_DATA)
594 OutputString(cp->ch.data, pInfo->fp, pInfo->track_pos);
597 /* ______________________________________________________________________ */
598 /* Chase IDs until we find an element whose GI matches. We also check
599 * child element names, not just the names of elements directly pointed
600 * at (by IDREF attributes).
609 /* did user or transpec set the variable */
610 if ((cp = FindMappingVal(Variables, "link_atts")))
611 idrefs = Split(cp, 0, S_STRDUP|S_ALVEC);
617 /* ______________________________________________________________________ */
618 /* Chase ID references - follow IDREF(s) attributes until we find
619 * a GI named 'gi', then perform given action on that GI.
621 * Pointer to element under consideration.
622 * Name of GI we're looking for.
623 * Spec ID of action to take.
624 * FILE pointer to where to write output.
635 char **tok, **s, *atval;
637 /* First, see if we got what we came for with this element */
638 if (StrEq(e->gi, gi)) {
639 TranByAction(e, action, fp);
644 /* loop for each attribute of type IDREF(s) */
645 for (s=idrefs; *s; s++) {
646 /* is this IDREF attr set? */
647 if ((atval = FindAttValByName(e, *s))) {
649 tok = Split(atval, &ntok, 0);
650 for (i=0; i<ntok; i++) {
651 /* get element pointed to */
652 if ((e = FindElemByID(tok[i]))) {
653 /* OK, we found a matching GI name */
654 if (StrEq(e->gi, gi)) {
655 /* process using named action */
656 TranByAction(e, action, fp);
660 /* this elem itself did not match, try its children */
661 for (ei=0; ei<e->necont; ei++) {
662 if (StrEq(e->econt[ei]->gi, gi)) {
663 TranByAction(e->econt[ei], action, fp);
667 /* try this elem's IDREF attributes */
668 ChaseIDRefs(e, gi, action, fp);
673 /* should not happen, since parser checks ID/IDREFs */
674 fprintf(stderr, "Error: Could not find ID %s\n", atval);
680 /* if the pointers didn't lead to the GI, give error */
682 fprintf(stderr, "Error: Could not find '%s'\n", gi);
685 /* ______________________________________________________________________ */
687 /* state to pass to recursive routines - so we don't have to use
688 * global variables. */
703 if (StrEq(ds->gi, e->gi))
704 if (ds->action) TranByAction(e, ds->action, ds->fp);
713 if (StrEq(ds->gi, e->gi) && e->parent &&
714 StrEq(ds->gi2, e->parent->gi))
715 if (ds->action) TranByAction(e, ds->action, ds->fp);
725 if ((atval = FindAttValByName(e, ds->gi)) && StrEq(ds->gi2, atval))
726 TranByAction(e, ds->action, ds->fp);
735 if (QRelation(e, ds->gi, REL_Parent)) {
736 if (ds->action) TranByAction(e, ds->action, ds->fp);
740 /* ______________________________________________________________________ */
741 /* Descend tree, finding elements that match criteria, then perform
744 * Pointer to element under consideration.
745 * Number of tokens in special variable.
746 * Vector of tokens in special variable (eg, "find" "gi" "TITLE")
747 * FILE pointer to where to write output.
757 Descent_t DS; /* state passed to recursive routine */
759 memset(&DS, 0, sizeof(Descent_t));
763 /* see if we should start at the top of instance tree */
764 if (StrEq(av[1], "top")) {
770 fprintf(stderr, "Bad '_find' specification - missing args.\n");
773 /* Find elem whose GI is av[2] */
774 if (StrEq(av[1], "gi")) {
776 DS.action = atoi(av[3]);
777 DescendTree(e, tr_find_gi, 0, 0, &DS);
779 /* Find elem whose GI is av[2] and whose parent GI is av[3] */
780 else if (StrEq(av[1], "gi-parent")) {
783 DS.action = atoi(av[4]);
784 DescendTree(e, tr_find_gipar, 0, 0, &DS);
786 /* Find elem whose parent GI is av[2] */
787 else if (StrEq(av[0], "parent")) {
789 DS.action = atoi(av[3]);
790 DescendTree(e, tr_find_parent, 0, 0, &DS);
792 /* Find elem whose attribute av[2] has value av[3] */
793 else if (StrEq(av[0], "attr")) {
796 DS.action = atoi(av[4]);
797 DescendTree(e, tr_find_attr, 0, 0, &DS);
801 /* ______________________________________________________________________ */