summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authorjfieber <jfieber@FreeBSD.org>1996-09-08 01:55:10 +0000
committerjfieber <jfieber@FreeBSD.org>1996-09-08 01:55:10 +0000
commit38d12975246b6eddde95da3643affaa704a15ce9 (patch)
tree0e70312e4c44553d986f47d7fdbe2d735fb990a3 /usr.bin
downloadFreeBSD-src-38d12975246b6eddde95da3643affaa704a15ce9.zip
FreeBSD-src-38d12975246b6eddde95da3643affaa704a15ce9.tar.gz
Tool for manipulating SGML document instances. Replaces sgmlsasp.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/sgmls/instant/Makefile15
-rw-r--r--usr.bin/sgmls/instant/README150
-rw-r--r--usr.bin/sgmls/instant/browse.c462
-rw-r--r--usr.bin/sgmls/instant/general.h329
-rw-r--r--usr.bin/sgmls/instant/hyper.c100
-rw-r--r--usr.bin/sgmls/instant/info.c300
-rw-r--r--usr.bin/sgmls/instant/instant.1183
-rw-r--r--usr.bin/sgmls/instant/main.c710
-rw-r--r--usr.bin/sgmls/instant/tables.c2013
-rw-r--r--usr.bin/sgmls/instant/traninit.c577
-rw-r--r--usr.bin/sgmls/instant/translate.c881
-rw-r--r--usr.bin/sgmls/instant/translate.h153
-rw-r--r--usr.bin/sgmls/instant/transpec.5526
-rw-r--r--usr.bin/sgmls/instant/tranvar.c757
-rw-r--r--usr.bin/sgmls/instant/util.c1109
15 files changed, 8265 insertions, 0 deletions
diff --git a/usr.bin/sgmls/instant/Makefile b/usr.bin/sgmls/instant/Makefile
new file mode 100644
index 0000000..cefd268
--- /dev/null
+++ b/usr.bin/sgmls/instant/Makefile
@@ -0,0 +1,15 @@
+# $Id: Makefile,v 1.1.1.1 1996/01/16 05:14:09 jfieber Exp $
+
+PROG= instant
+SRCS= browse.c info.c main.c tables.c traninit.c translate.c
+SRCS+= tranvar.c util.c
+
+CFLAGS+= -I${.CURDIR}/../libsgmls -I${.CURDIR}/../sgmls
+
+LDADD= ${LIBSGMLS} -lcompat
+DPADD= ${LIBSGMLS}
+
+MAN1= instant.1
+MAN5= transpec.5
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sgmls/instant/README b/usr.bin/sgmls/instant/README
new file mode 100644
index 0000000..d04547c
--- /dev/null
+++ b/usr.bin/sgmls/instant/README
@@ -0,0 +1,150 @@
+#
+# 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 formatting application for OSF SGML instances
+____________________________________________________________________________
+
+Requirements
+
+ ANSI C compiler (gcc is one)
+
+ sgmls 1.1 -- sgml parser from James Clark. Based on Goldfarb's ARC parser.
+
+ Vanilla unix make
+
+ POSIX C libraries
+
+
+Files for instant program
+
+ Module Function
+ ------ --------
+ browse.c interactive browser
+ general.h general definitions
+ info.c print information about the instances
+ main.c main entry, arg parsing, instance reading
+ tables.c table-specific formatting routines (TeX and tbl)
+ traninit.c translator initialization (read spec, etc.)
+ translate.c main translator
+ translate.h structure definitions for translation code
+ tranvar.c routines for handling "special variables"
+ util.c general utilities
+
+
+Also required
+
+ 1. Translation spec (transpec) files. (../transpecs/*.ts)
+ 2. SDATA mapping files for mapping sdata entities. (../transpecs/*.sdata)
+ 3. Character mapping files for mapping characters. (../transpecs/*.cmap)
+
+
+Platforms tried on
+
+ OSF1 1.3 (i486)
+ Ultrix 4.2 (mips)
+ HP-UX 9.01 (hp 9000/700)
+ AIX 3.2 (rs6000)
+ SunOS [missing strerror()]
+
+____________________________________________________________________________
+
+ General outline of program
+ ------- ------- -- -------
+
+To summarize in a sentence, instant reads the output of sgmls, builds a tree
+of the instnace in memory, then traverses the tree in in-order, processing
+the nodes according to a translation spec.
+
+Element tree storage
+------- ---- -------
+
+The first thing instant must do is read the ESIS (output of sgmls) from the
+specified file or stdin, forming a tree in memory. (Nothing makes sense
+without an instance...) Each element of the instance is a node in the tree,
+stored as a structure called Element_t. Elements contain content (what
+else?), which is a mixture of data (#PCDATA, #CDATA, #RCDATA - all the same
+in the ESIS), child elements, and PIs. Each 'chunk' of content is referred
+to by a Content_t structure. A Content_t contains an enum that can point to
+a string (data or PI), another Element_t. For example, if a <p> element
+contains some characters, an <emphasis> element, some more characters,
+a <function> element, then some more characters, it has 5 Content_t children
+as an array.
+
+Element_t's have pointers to their parents, and a next element in a linked
+list (they're stored as a linked list, for cases when you'd want to quickly
+travers all the nodes, in no particular order).
+For convenience, Element_t's have an array of pointers to it's child
+Element_t's. These are just pointers to the same thing contained in the
+Content_t array, without the intervening data or PIs. This makes it easier
+for the program to traverse the elements of the tree (it does not have to
+be concerned with skipping data, etc.). There is an analagous array of
+pointers for the data content, an array of (char *)'s. This makes it easier
+to consider the immediate character content of an element.
+
+Attributes are kept as an array of name-value mappings (using the typedef
+Mapping_t). ID attributes are also stored in a separate list of ID value -
+element pointer pairs so that it is quick to find an element by ID.
+
+Other information kept about each element (in the Element_t struct) includes
+the line number in the EISI where the element occurs, the input filename.
+(These depend on sgmls being run with the "-l" option.) Also stored is
+an element's order in its parent's collection of children and an element's
+depth in the tree.
+
+Translation specs
+----------- -----
+
+A translation spec is read into a linked list in memory. As the instance
+tree is traversed, the transpecs are searched in order for a match. As a
+rule, one should position the less specific transpecs later in the file.
+Also, specs for seldom-used element are best placed later in the file, since
+it takes cpu cycles to skip over them for each of the more-used elements.
+
+During translation of a particular element, the list of Content_t structures
+are processed in order. If a content 'chunk' is data, it is printed to
+the output stream. If it is an element, the translation routine is called
+for that elemen, recursively. Hence, in-order traversal.
+
+Miscellaneous information displays
+------------- ----------- --------
+
+There are several informational display options available. They include:
+ - a list of element usage (-u) -- lists each element in the instance,
+ it's attributes, number of children, parent, data content 'nodes'.
+ - statistics about elements (-S) -- lists elements, number of times
+ each is used, percent of elements that this is, total char content
+ in that element, average number of characters in they element.
+ - show context of each element (-x) -- lists each element and its
+ context, looking up the tree (this is the same context that
+ would match the Context field of a transpec).
+ - show the hierarchy of the instance (-h) -- show an ascii 'tree' of
+ the instance, where elements deeper in the tree are indented more.
+ Numbers after the element name in parentheses are the number of
+ element content nodes and the number of data content nodes.
+
+Interactive browser
+----------- -------
+
+Originally, the interactive browser was intended as a debugging aid for
+the code developer. It was a way to examine a particular node of the
+instance tree or process a subtree without being distracted by the rest
+of the instance. Many of the commands test functionality of the query
+and search code (such as testing whether a certain element has a given
+relationship to the current position in the tree).
+
+
+____________________________________________________________________________
+
diff --git a/usr.bin/sgmls/instant/browse.c b/usr.bin/sgmls/instant/browse.c
new file mode 100644
index 0000000..c904476
--- /dev/null
+++ b/usr.bin/sgmls/instant/browse.c
@@ -0,0 +1,462 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+
+/* ________________________________________________________________________
+ *
+ * Module for interactive browsing.
+ *
+ * Entry points for this module:
+ * Browse() interactive browser
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/browse.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "general.h"
+
+static void PrElemPlusID(Element_t *);
+static void ls_node(Element_t *, int, char **);
+static void do_query(Element_t *, char *, char *);
+static void do_find(Element_t *, char **);
+
+/* ______________________________________________________________________ */
+
+static char *br_help_msg[] = {
+ " ls List info about current element in tree",
+ " (context, children, attributes, etc.)",
+ " cd N ... Change to Nth elememt child, where N is shown by 'ls'.",
+ " N may also be '/' (top) or '..' (up).",
+ " cd id I Change to elememt whose ID is I",
+ " data N Show data of Nth data node",
+ " where Show current position in the tree",
+ " id I Show path to element with id I",
+ " (using '?' for I will lists all IDs and their paths)",
+ " find S Find elements matching spec S. Recognized syntaxes:",
+ " find attr <name> <value>",
+ " find cont <string>",
+ " find parent <gi-name>",
+ " find child <gi-name>",
+ " find gi <gi-name>",
+ " q rel gi Query: report if elem 'gi' has relation to current elem",
+ " ('rel' is one of 'child parent ancestor descendant",
+ " sibling sibling+ sibling+1 sibling- sibling-1 cousin')",
+ "",
+ " tran [outfile]",
+ " Translate into 'outfile' (stdout)",
+ " stat Print statistics (how often elements occur, etc.)",
+ " sum Print elem usage summary (# of children, depth, etc.)",
+ " tree Print document hierarchy as a tree",
+ " cont Print context of each element",
+ NULL
+};
+
+/* ______________________________________________________________________ */
+
+void
+Browse()
+{
+ char buf[256], *cmd, **av, **sv;
+ char *Prompt;
+ Element_t *ce; /* current element */
+ Element_t *e;
+ int i, n, ac;
+
+ if (slave) Prompt = "=>\n";
+ else Prompt = "=> ";
+
+ ce = DocTree;
+ while (fputs(Prompt, stdout)) {
+ if (!fgets(buf, 256, stdin)) break;
+ stripNL(buf);
+ if (buf[0] == EOS) {
+ fputs(Prompt, stdout);
+ continue;
+ }
+ ac = 20;
+ av = Split(buf, &ac, S_ALVEC);
+ if (ac > 0) cmd = av[0];
+ if (!cmd || !(*cmd)) continue;
+
+ if (!strcmp(cmd, "ls")) ls_node(ce, ac, av);
+
+ else if (!strcmp(cmd, "cd")) {
+ if (av[1]) {
+ if (ac == 3 && !strcmp(av[1], "id")) {
+ if ((e = FindElemByID(av[2]))) ce = e;
+ else printf("Element with ID '%s' not found.\n", av[2]);
+ continue;
+ }
+ for (i=1; i<ac; i++) {
+ if (!strcmp(av[i], "..")) {
+ if (ce->parent) ce = ce->parent;
+ continue;
+ }
+ if (!strcmp(av[i], "/")) {
+ if (ce->parent) ce = DocTree;
+ continue;
+ }
+ if (!isdigit(*av[i])) {
+ printf("Expecting digit, '..', or '/', got '%s'.\n",
+ av[i]);
+ break;
+ }
+ n = atoi(av[i]);
+ if (n < ce->necont) ce = ce->econt[n];
+ else {
+ printf("Must be in range 0 - %d.\n", ce->necont);
+ break;
+ }
+ }
+ }
+ }
+
+ else if (!strcmp(cmd, "data")) {
+ if (av[1] && isdigit(*av[1])) {
+ n = atoi(av[1]);
+ if (n < ce->ndcont) {
+ printf(ce->dcont[n]);
+ fputs("\n", stdout);
+ }
+ else if (ce->ndcont == 0)
+ printf("No data at this node.\n");
+ else printf("Must be in range 0 - %d.\n", ce->ndcont);
+ }
+ }
+
+ /* show where we are in the tree */
+ else if (!strcmp(cmd, "where")) PrintLocation(ce, stdout);
+
+ /* show where we are in the tree */
+ else if (!strcmp(cmd, "pwd")) PrElemPlusID(ce);
+
+ /* perform query with yes/no answer */
+ else if (!strcmp(cmd, "q") && av[1] && av[2])
+ do_query(ce, av[1], av[2]);
+
+ /* perform query printing paths to matching elements */
+ else if (!strcmp(cmd, "find") && av[1] && av[2])
+ do_find(ce, av);
+
+ /* list locations where specified ID(s) occur */
+ else if (!strcmp(cmd, "id")) {
+ if (ac <= 1) continue;
+ if (*av[1] == '?') PrintIDList();
+ else {
+ /* short: "id i1 i2 ...", long: "id -l i1 i2 ..." */
+ if (!strcmp(av[1], "-l")) n = 2;
+ else n = 1;
+ for (i=n; i<ac; i++) {
+ if ((e = FindElemByID(av[i]))) {
+ if (n == 2) { /* long (multiline) format */
+ if (n != i) putchar('\n');
+ PrintLocation(e, stdout);
+ }
+ else PrElemPlusID(e);
+ }
+ else printf("Element with ID '%s' not found.\n", av[i]);
+ }
+ }
+ }
+
+ /* show and set variables */
+ else if (!strcmp(cmd, "show") && av[1]) {
+ printf("%s\n", FindMappingVal(Variables, av[1]));
+ }
+ else if (!strcmp(cmd, "set") && av[1] && av[2]) {
+ SetMappingNV(Variables, av[1], av[2]);
+ }
+
+ /* print summary of tag usage */
+ else if (!strcmp(cmd, "sum")) {
+ if (ac > 1) PrintElemSummary(ce);
+ else PrintElemSummary(DocTree);
+ }
+ /* print element tree */
+ else if (!strcmp(cmd, "tree")) {
+ if (ac > 1) PrintElemTree(ce);
+ else PrintElemTree(DocTree);
+ }
+ /* print statistics */
+ else if (!strcmp(cmd, "stat")) {
+ if (ac > 1) PrintStats(ce);
+ else PrintStats(DocTree);
+ }
+ /* print context of each element of tree */
+ else if (!strcmp(cmd, "cont")) {
+ if (ac > 1) PrintContext(ce);
+ else PrintContext(DocTree);
+ }
+ /* print translation, given transpec */
+ else if (!strcmp(cmd, "tran")) {
+ FILE *fp;
+ if (ac > 1){
+ if (!(fp = fopen(av[1], "w"))) {
+ perror("Can not open output file");
+ continue;
+ }
+ }
+ else fp = stdout;
+ DoTranslate(ce, fp);
+ if (ac > 1) fclose(fp);
+ }
+
+ else if (!strcmp(cmd, "help") || *cmd == '?') {
+ sv = br_help_msg;
+ while (*sv) puts(*sv++);
+ }
+
+ /* quit (control-D also works) */
+ else if (!strcmp(cmd, "quit")) break;
+
+ else
+ fprintf(stderr, "Unknown command '%s' - ingored.\n", cmd);
+ }
+ putc(NL, stdout);
+}
+
+/* ______________________________________________________________________ */
+/* Do the "ls" command.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Arg count from command line (this command, not the shell command).
+ * Arg vector.
+ */
+
+static void
+ls_node(
+ Element_t *e,
+ int ac,
+ char **av
+)
+{
+ int i;
+ char buf[LINESIZE];
+
+ if (ac > 1 && !strcmp(av[1], "-n")) {
+ for(i=0; i<e->ncont; i++) {
+ if (IsContElem(e,i)) printf("%s\n", ContElem(e,i)->gi);
+ else if (IsContData(e,i)) printf("#data %s\n", ContData(e,i));
+ else if (IsContPI(e,i)) printf("#pi %s\n", ContData(e,i));
+ }
+ return;
+ }
+
+ printf("Element: %s\tLineNumber: %d\n", e->gi, e->lineno);
+ if (e->parent)
+ printf("Context: %s\n", FindContext(e, 20, buf));
+
+ if (e->natts) {
+ printf("%d attributes:\n", e->natts);
+ for (i=0; i<e->natts; i++)
+ printf("\t%2d: %s = '%s'\n", i, e->atts[i].name, e->atts[i].sval);
+ }
+ if (e->entity) {
+ printf("Entity & notation information:\n");
+ if (e->entity->ename)
+ printf("Entity name: %s\n", e->entity->ename);
+ if (e->entity->nname)
+ printf("Notation name: %s\n", e->entity->nname);
+ if (e->entity->sysid)
+ printf("Sys id: %s\n", e->entity->sysid);
+ if (e->entity->pubid)
+ printf("Pub id: %s\n", e->entity->pubid);
+ if (e->entity->fname)
+ printf("Filename: %s\n", e->entity->fname);
+ }
+
+ if (e->my_eorder >= 0)
+ printf("My order among my siblings: %d\n", e->my_eorder);
+
+ if (e->necont) {
+ printf("%d child element nodes:\n", e->necont);
+ for(i=0; i<e->necont; i++) printf("\t%2d: %s\n", i, e->econt[i]->gi);
+ }
+
+ if (e->ndcont) {
+ printf("%d child data nodes:\n", e->ndcont);
+ for(i=0; i<e->ndcont; i++) {
+ if (strlen(e->dcont[i]) < 40)
+ printf("\t%2d: %s\n", i, e->dcont[i]);
+ else
+ printf("\t%2d: %-40.40s...\n", i, e->dcont[i]);
+ }
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Perform query. Syntax: find relationship gi. Tells whether gi has
+ * given relationship to current element. Result (message) sent to stdout.
+ * Args:
+ * Pointer to element under consideration.
+ * Pointer to name of relationship. (see FindRelByName() for names)
+ * Pointer to GI to look for.
+ */
+
+static void
+do_query(
+ Element_t *e,
+ char *rel,
+ char *gi
+)
+{
+ char *cp;
+ Relation_t r;
+ Element_t *ep;
+
+ for (cp=gi; *cp; cp++) if (islower(*cp)) *cp = toupper(*cp);
+
+ if ((r = FindRelByName(rel)) == REL_Unknown) {
+ return;
+ }
+ ep = QRelation(e, gi, r);
+ printf("%s, '%s' is%s %s of '%s'.\n", (ep ? "Yes" : "No"), gi,
+ (ep ? "" : " not"), rel, e->gi);
+}
+
+/* ______________________________________________________________________ */
+/* Print path to the element and its ID (if it has one) on a single line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+static void
+PrElemPlusID(
+ Element_t *e
+)
+{
+ char buf[LINESIZE];
+
+ if (e->id) printf("%s -- ID=%s\n", FindElementPath(e, buf), e->id);
+ else printf("%s\n", FindElementPath(e, buf));
+}
+
+/* ______________________________________________________________________ */
+/* Print path to the element and its ID (if it has one) on a single line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+
+static void
+match_gi(
+ Element_t *e,
+ char **av
+)
+{
+ if (!strcmp(av[1], e->gi)) PrElemPlusID(e);
+}
+
+/* Shorthand for defining simple finctions, which are just interfaces to
+ * calling QRelation(). DescendTree() only passes ptr to element. */
+#define MATCH(Fun,Rel) \
+ static void Fun(Element_t *e, char **av) \
+ { if (QRelation(e, av[1], Rel)) PrElemPlusID(e); }
+
+MATCH(match_parent, REL_Parent)
+MATCH(match_child, REL_Child)
+MATCH(match_anc, REL_Ancestor)
+MATCH(match_desc, REL_Descendant)
+MATCH(match_sib, REL_Sibling)
+
+static void
+match_attr(
+ Element_t *e,
+ char **av
+)
+{
+ char *atval;
+
+ if ((atval = FindAttValByName(e, av[1])) && !strcmp(av[2], atval))
+ PrElemPlusID(e);
+}
+
+static void
+match_cont(
+ Element_t *e,
+ char **av
+)
+{
+ int i;
+ for (i=0; i<e->ncont; i++) {
+ if (IsContData(e,i) && strstr(ContData(e,i), av[1])) {
+ PrElemPlusID(e);
+ return;
+ }
+ }
+}
+
+/* Find an element, given the criteria on its command line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+static void
+do_find(
+ Element_t *e,
+ char **av
+)
+{
+ av++;
+ if (!strcmp(av[0], ".")) av++;
+ else e = DocTree;
+ if (!strcmp(av[0], "gi")) DescendTree(e, match_gi, 0, 0, av);
+ else if (!strcmp(av[0], "attr")) DescendTree(e, match_attr, 0, 0, av);
+ else if (!strcmp(av[0], "parent")) DescendTree(e, match_parent, 0, 0, av);
+ else if (!strcmp(av[0], "child")) DescendTree(e, match_child, 0, 0, av);
+ else if (!strcmp(av[0], "cont")) DescendTree(e, match_cont, 0, 0, av);
+ else if (!strcmp(av[0], "sib")) DescendTree(e, match_sib, 0, 0, av);
+ else if (!strcmp(av[0], "desc")) DescendTree(e, match_desc, 0, 0, av);
+ else if (!strcmp(av[0], "anc")) DescendTree(e, match_anc, 0, 0, av);
+ else fprintf(stderr, "Unknown find command: %s.\n", av[0]);
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/general.h b/usr.bin/sgmls/instant/general.h
new file mode 100644
index 0000000..f6e6ea0
--- /dev/null
+++ b/usr.bin/sgmls/instant/general.h
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+/* ________________________________________________________________________
+ *
+ * Common definitions for "instant" program.
+ * ________________________________________________________________________
+ */
+
+#ifdef STORAGE
+#ifndef lint
+static char *gen_h_RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/general.h,v 1.5 1996/06/11 20:25:03 fld Exp $";
+#endif
+#endif
+
+/* string/numeric/character definitions */
+
+#define EOS '\0'
+#define NL '\n'
+#define TAB '\t'
+#define CR '\r'
+#define ANCHOR '^'
+
+/* bigmask/flags for the Split() function */
+#define S_STRDUP 0x01
+#define S_ALVEC 0x02
+
+/* Command codes (1st char of esis lines) from sgmls. See its manpage. */
+#define CMD_DATA '-'
+#define CMD_OPEN '('
+#define CMD_CLOSE ')'
+#define CMD_ATT 'A'
+#define CMD_D_ATT 'D'
+#define CMD_NOTATION 'N'
+#define CMD_EXT_ENT 'E'
+#define CMD_INT_ENT 'I'
+#define CMD_SYSID 's'
+#define CMD_PUBID 'p'
+#define CMD_FILENAME 'f'
+#define CMD_LINE 'L'
+#define CMD_PI '?'
+#define CMD_SUBDOC 'S'
+#define CMD_SUBDOC_S '{'
+#define CMD_SUBDOC_E '}'
+#define CMD_EXT_REF '&'
+#define CMD_APPINFO '#'
+#define CMD_CONFORM 'C'
+
+/* Some sizes */
+#define MAX_DEPTH 40
+#define LINESIZE 60000
+
+/* Name of library env variable, and default value. */
+#ifndef TPT_LIB
+#define TPT_LIB "TPT_LIB"
+#endif
+#ifndef DEF_TPT_LIB
+#define DEF_TPT_LIB "/usr/share/sgml/transpec"
+#endif
+
+/* Relationships - for querying */
+typedef enum {
+ REL_None, REL_Parent, REL_Child, REL_Ancestor, REL_Descendant,
+ REL_Sibling, REL_Preceding, REL_ImmPreceding, REL_Following,
+ REL_ImmFollowing, REL_Cousin, REL_Unknown
+} Relation_t;
+
+/* Initial map sizes (IMS) */
+#define IMS_relations 3
+#define IMS_setvar 3
+#define IMS_incvar 3
+#define IMS_sdata 50
+#define IMS_sdatacache 30
+#define IMS_variables 20
+#define IMS_attnames 50
+#define IMS_elemnames 50
+
+/* ----- typedef and other misc definitions ----- */
+
+#ifndef TRUE
+#define TRUE (1 == 1)
+#endif
+
+#ifndef FALSE
+#define FALSE (1 == 0)
+#endif
+
+typedef short bool;
+
+
+/* ----- structure definitions ----- */
+
+/* We use this for variables, attributes, etc., so the caller only needs an
+ * opaque handle to the thing below, not worrying about array management. */
+typedef struct {
+ char *name; /* name of the thing */
+ char *sval; /* string value */
+} Mapping_t;
+
+typedef struct {
+ int n_alloc; /* number of elements allocated */
+ int n_used; /* number of elements used */
+ int slot_incr; /* increment for allocating slots */
+ int flags; /* info about this set of mappings */
+ Mapping_t *maps; /* array of mappings */
+} Map_t;
+
+/* ______________________________________________________________________ */
+
+/* Information about an entity reference. Not all fields will be used
+ * at once. */
+typedef struct _ent {
+ char *type; /* entity type */
+ char *ename; /* entity name */
+ char *nname; /* notation name */
+ char *sysid; /* sys id */
+ char *pubid; /* pub id */
+ char *fname; /* filename */
+ struct _ent *next; /* next in linked list */
+} Entity_t;
+
+/* Content (child nodes) of an element (node in the tree) -- both data
+ * and other elements. */
+typedef struct {
+ char type; /* element, data, or pi? */
+ union {
+ struct _elem *elem; /* direct children of this elem */
+ char *data; /* character data of this elem */
+ } ch;
+} Content_t;
+
+/* An element (node in the tree) */
+typedef struct _elem {
+ char *gi; /* element GI */
+ Content_t *cont; /* content - element & data children */
+ int ncont; /* # of content/children */
+ struct _elem **econt; /* element children */
+ int necont; /* # of element children */
+ char **dcont; /* character data children */
+ int ndcont; /* # of data children */
+ Mapping_t *atts; /* array of attributes */
+ int natts; /* # of attributes */
+ Entity_t *entity; /* ext entity & notation info */
+ char *id; /* for linking */
+ int index; /* an internal bookkeeping mechanism */
+ int depth; /* how deep in tree */
+ int lineno; /* line number */
+ char *infile; /* input filename */
+ int my_eorder; /* order of this elem of its parent */
+ struct _elem *parent; /* this elem's direct parent */
+ struct _elem *next; /* kept in linked list */
+ void *trans; /* pointer to translation spec */
+ /* I'm not crazy about this, but it works */
+ int gen_trans[2]; /* refs to generated trans specs */
+ int processed; /* was this node processed? */
+} Element_t;
+
+/* For mapping of element IDs to elements themselves. */
+typedef struct id_s {
+ char *id; /* ID of the element */
+ Element_t *elem; /* pointer to it */
+ struct id_s *next;
+} ID_t;
+
+/* ----- global variable declarations ----- */
+
+#ifdef STORAGE
+# define def
+#else
+# define def extern
+#endif
+
+def Element_t *DocTree; /* root of document tree */
+def char **UsedElem; /* a unique list of used elem names */
+def int nUsedElem; /* number of used elem names */
+def char **UsedAtt; /* a unique list of used attrib names */
+def int nUsedAtt; /* number of used attrib names */
+def ID_t *IDList; /* list of IDs used in the doc */
+def Map_t *Variables; /* general, global variables */
+def Map_t *SDATAmap; /* SDATA mappings */
+def Map_t *PImap; /* Processing Instruction mappings */
+def Entity_t *Entities; /* list of entities */
+
+def FILE *outfp; /* where output is written */
+def char *tpt_lib; /* TPT library directory */
+def int verbose; /* flag - verbose output? */
+def int warnings; /* flag - show warnings? */
+def int interactive; /* flag - interactive browsing? */
+def int slave; /* are we slave to another process? */
+def int fold_case; /* flag - fold case of GIs? */
+
+/* ----- some macros for convenience and ease of code reading ----- */
+
+#define stripNL(s) { char *_cp; if ((_cp=strchr(s, NL))) *_cp = EOS; }
+
+/* Similar to calloc(), malloc(), and realloc(), but check for success.
+ * Args to all:
+ * (1) number of 'elements' to allocate
+ * (2) variable to point at allocated space
+ * (3) type of 'element'
+ * Eg: Calloc(5, e, Element_t) replaces
+ * if (!(e = (Element_t *)calloc(5, sizeof(Element_t))) {
+ * ... handle error ... ;
+ * }
+ */
+#define Calloc(N,V,T) \
+ { if (!((V) = (T *)calloc((size_t)N, sizeof(T)))) { \
+ perror("Calloc failed -- out of memory. Bailing out."); exit(1); \
+ }; memset((void *) (V), 0, (size_t) sizeof(T)); }
+#define Malloc(N,V,T) \
+ { if (!((V) = (T *)malloc((size_t)N*sizeof(T)))) { \
+ perror("Malloc failed -- out of memory. Bailing out."); exit(1); \
+ }; memset((void *) (V), 0, (size_t) sizeof(T)); }
+#define Realloc(N,V,T) \
+ { if (!((V) = (T *)realloc(V,(size_t)N*sizeof(T)))) { \
+ perror("Realloc failed -- out of memory. Bailing out."); exit(1); \
+ } }
+
+/* similar to strcmp(), but check first chars first, for efficiency */
+#define StrEq(s1,s2) (s1[0] == s2[0] && !strcmp(s1,s2))
+
+/* similar to isspace(), but check for blank or tab - without overhead
+ * of procedure call */
+#define IsWhite(c) (c == ' ' || c == TAB || c == NL)
+
+#define ContType(e,i) (e->cont[i].type)
+#define ContData(e,i) (e->cont[i].ch.data)
+#define ContElem(e,i) (e->cont[i].ch.elem)
+#define IsContData(e,i) (e->cont[i].type == CMD_DATA)
+#define IsContElem(e,i) (e->cont[i].type == CMD_OPEN)
+#define IsContPI(e,i) (e->cont[i].type == CMD_PI)
+
+/* ----- function prototypes ----- */
+
+/* things defined in util.c */
+Element_t *QRelation(Element_t *, char *, Relation_t);
+Relation_t FindRelByName(char *);
+char *FindAttValByName(Element_t *, char *);
+char *FindContext(Element_t *, int, char *);
+char *AddElemName(char *);
+char *AddAttName(char *);
+char *ExpandString(char *);
+void OutputString(char *, FILE *, int);
+char *LookupSDATA(char *);
+FILE *OpenFile(char *);
+char *FilePath(char *);
+char *FindElementPath(Element_t *, char *);
+char *NearestOlderElem(Element_t *, char *);
+void PrintLocation(Element_t *, FILE *);
+char **Split(char *, int *, int);
+void DescendTree(Element_t *, void(*)(), void(*)(), void(*)(), void *);
+Map_t *NewMap(int);
+Mapping_t *FindMapping(Map_t *, const char *);
+char *FindMappingVal(Map_t *, const char *);
+void SetMapping(Map_t *, const char *);
+void SetMappingNV(Map_t *, const char *, const char *);
+void AddID(Element_t *, char *);
+Element_t *FindElemByID(char *);
+
+/* things defined in translate.c */
+void DoTranslate(Element_t*, FILE *);
+void ExpandVariables(char*, char*, Element_t*);
+
+/* things defined in traninit.c */
+void ReadTransSpec(char *);
+
+/* things defined in tranvar.c */
+char *Get_A_C_value(const char *);
+
+/* things defined in info.c */
+void PrintContext(Element_t *e);
+void PrintElemSummary(Element_t *);
+void PrintElemTree(Element_t *);
+void PrintStats(Element_t *);
+void PrintIDList();
+
+/* things defined in table.c */
+void CALStable(Element_t *, FILE *, char **, int);
+
+/* ----- other declarations ----- */
+
+#ifdef ultrix
+#define strdup(s) strcpy((char *)malloc(strlen(s)+1), s)
+#endif
+
diff --git a/usr.bin/sgmls/instant/hyper.c b/usr.bin/sgmls/instant/hyper.c
new file mode 100644
index 0000000..4f50b97
--- /dev/null
+++ b/usr.bin/sgmls/instant/hyper.c
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+/* ________________________________________________________________________
+ *
+ * Hypermedia-related facilities.
+ *
+ * Entry points for this module:
+ * AddID(elem, idval) add elem-id pair to list of known ids
+ * FindElemByID(idval) find elem by id
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/hyper.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+
+#include "general.h"
+
+
+/* ______________________________________________________________________ */
+
+void
+AddID(Element *e, char *idval)
+{
+ static ID *id_last;
+ if (!IDList) {
+ Calloc(1, id_last, ID);
+ IDList = id_last;
+ }
+ else {
+ Calloc(1, id_last->next, ID);
+ id_last = id_last->next;
+ }
+ id_last->elem = e;
+ id_last->id = idval;
+}
+
+Element *
+FindElemByID(char *idval)
+{
+ ID *id;
+ for (id=IDList; id; id=id->next)
+ if (!strcmp(id->id, idval)) return id->elem;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/info.c b/usr.bin/sgmls/instant/info.c
new file mode 100644
index 0000000..27ab1c7
--- /dev/null
+++ b/usr.bin/sgmls/instant/info.c
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+/* ________________________________________________________________________
+ *
+ * Functions for printing information about an instance in the 'instant'
+ * program. Most of these are fairly short and simple.
+ *
+ * Entry points for this module:
+ * PrintElemSummary(elem) print summary info of each element
+ * PrintContext(elem) print context of each element
+ * PrintElemTree(elem) print tree of document
+ * PrintStats(elem) print statistics about doc tree
+ * PrintIDList(elem) print list of IDs and element context
+ * Most Print*() functions start at subtree pointed to by 'elem'.
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/info.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "general.h"
+
+/* ______________________________________________________________________ */
+/* Print a summary of each tag use in the instance. Things like depth in
+ * the tree, number of children, parent, attributes.
+ */
+
+/* Do the actual printing. Print the info about the node. If null,
+ * print a header for the columns.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_summ(
+ Element_t *e
+)
+{
+ int i, n, dsize;
+ char *hfmt="%-18.18s %4s %5s %4s %4s %s\n";
+ char *fmt ="%-18.18s %4d %5d %4d %4d %s\n";
+
+ if (e == NULL) {
+ fprintf(outfp, hfmt, "Element", "Att", "Data", "Chd", "Dep", "Parent");
+ return;
+ }
+ for (i=0,n=0; i<e->ncont; i++) if (IsContElem(e,i)) n++;
+ for (i=0,dsize=0; i<e->ncont; i++)
+ if (IsContElem(e,i)) dsize += strlen(e->cont[i].ch.data);
+ fprintf(outfp, fmt, e->gi, e->natts, dsize, n, e->depth,
+ e->parent ? e->parent->gi : "-");
+
+ for (i=0; i<e->natts; i++) {
+ fprintf(outfp, "%45d: %s = %s\n", i, e->atts[i].name,
+ e->atts[i].sval ? e->atts[i].sval : "empty");
+ }
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintElemSummary(
+ Element_t *e
+)
+{
+ print_summ(0);
+ DescendTree(e, print_summ, 0, 0, 0);
+}
+
+/* ______________________________________________________________________ */
+/* Print the context of each tag in the instance (i.e. the tag with its
+ * ancestors).
+ */
+
+/* Do the actual printing. Print the context of the node.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_context(
+ Element_t *e
+)
+{
+ char buf[LINESIZE];
+
+ fprintf(outfp, "%-22s %s\n", e->gi, FindContext(e, 10, buf));
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintContext(
+ Element_t *e
+)
+{
+ fprintf(outfp, "%-22s %s\n", "Element", "Context");
+ fprintf(outfp, "%-22s %s\n", "---------------", "-----------");
+ DescendTree(e, print_context, 0, 0, 0);
+
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print tree of the instance. GI's are printed indented by their depth
+ * in the tree.
+ */
+
+/* Do the actual printing. Print the element name, indented the right amount.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_indent(
+ Element_t *e
+)
+{
+ int i, ne, nd;
+ for(i=0; i<e->depth; i++) fputs(". ", outfp);
+ for(i=0,ne=0; i<e->ncont; i++) if (IsContElem(e,i)) ne++;
+ for(i=0,nd=0; i<e->ncont; i++) if IsContData(e,i) nd++;
+ fprintf(outfp, "%s (%d,%d)\n", e->gi, ne, nd);
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintElemTree(
+ Element_t *e
+)
+{
+ DescendTree(e, print_indent, 0, 0, 0);
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print some statistics about the instance.
+ */
+
+/* Accumulate the totals for the statistics.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ * Pointer to the total number of elements.
+ * Pointer to the total amount of content data.
+ * Pointer to the maximum depth of tree.
+ */
+static void
+acc_tots(
+ Element_t *e,
+ int *tot_el,
+ int *tot_data,
+ int *max_depth
+)
+{
+ int i;
+ for(i=0; i<e->necont; i++)
+ acc_tots(e->econt[i], tot_el, tot_data, max_depth);
+ for (i=0; i<e->necont; i++) (*tot_el)++;
+ for (i=0; i<e->ndcont; i++) (*tot_data) += strlen(e->dcont[i]);
+ if (e->depth > (*max_depth)) *max_depth = e->depth;
+}
+
+/* Descend the tree (recursively), collecting the statistics.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ * Pointer to the total number of elements.
+ * Pointer to the total amount of content data.
+ * Pointer to the maximum depth of tree.
+ */
+static void
+elem_usage(
+ Element_t *e,
+ char *name,
+ int *n_used,
+ int *nchars
+)
+{
+ int i;
+ if (!strcmp(name, e->gi)) {
+ (*n_used)++;
+ for (i=0; i<e->ncont; i++)
+ if (IsContData(e,i)) (*nchars) += strlen(ContData(e,i));
+ }
+ for(i=0; i<e->necont; i++)
+ elem_usage(e->econt[i], name, n_used, nchars);
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintStats(
+ Element_t *top
+)
+{
+ int i, n;
+ int dif_el=0, tot_el=0, tot_data=0, nchars, max_depth=0;
+ float pct;
+
+ fprintf(outfp, "%-22s %s %s\n", "Element name", "Occurrances", "Character Content");
+ fprintf(outfp, "%-22s %s %s\n", "---------------", "-----------", "-----------------");
+
+ acc_tots(top, &tot_el, &tot_data, &max_depth);
+
+ for (i=0; i<nUsedElem; i++) {
+ n = 0;
+ nchars = 0;
+ elem_usage(top, UsedElem[i], &n, &nchars);
+ if (n > 0) {
+ pct = 100.0 * (float)n / (float)tot_el;
+ fprintf(outfp, "%-22s %4d %4.1f%% %6d %4d\n", UsedElem[i],
+ n, pct, nchars, (nchars/n));
+ dif_el++;
+ }
+ }
+
+ fprintf(outfp, "\nTotal of %d elements used, %d different ones.\n",
+ tot_el, dif_el);
+ fprintf(outfp, "Total character data: %d.\n", tot_data);
+ fprintf(outfp, "Maximum element depth: %d.\n", max_depth);
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print list of: ID, GI, input file, line number, separated by colons.
+ * This is better for other programs to manipulate (like for keeping a
+ * database of IDs in documents) than humans to read.
+ */
+
+void
+PrintIDList()
+{
+ ID_t *id;
+ Element_t *ep;
+
+ for (id=IDList; id; id=id->next) {
+ ep = id->elem;
+ fprintf(outfp, "%s:%s:%s:%d\n", id->id, ep->gi,
+ ep->infile?ep->infile:"-", ep->lineno);
+ }
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/instant.1 b/usr.bin/sgmls/instant/instant.1
new file mode 100644
index 0000000..513c84d
--- /dev/null
+++ b/usr.bin/sgmls/instant/instant.1
@@ -0,0 +1,183 @@
+...\"
+...\" 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) 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.
+...\"
+...\" Translated with /usr/local/lib/tpt/ref-man.ts by fld on cord, Wed 07 Feb 1996, 21:59
+.Dd September 5, 1996
+.Os FreeBSD 2.2
+.Dt SGMLFMT 1
+.Sh "NAME"
+instant - manipulates ESIS from parsed SGML instance
+.Sh "Synopsis"
+.na
+.Pp
+\fBinstant\fP [ \fB-bhuvxHISW\fP ] [ \fB-t\fP \fIfile\fP] [ \fB-o\fP \fIfile\fP] [ \fB-D\fP \fIvariable\fP\fB=\fP\fIvalue\fP ...] [ \fB-i\fP \fIid\fP] [ \fB-l\fP \fIdirectory\fP] [\fIfile\fP]
+.ad
+.Sh "DESCRIPTION"
+.Pp
+The \fBinstant\fP program manipulates an SGML document instance in a variety of ways,
+including translating into a form suitable for a formatting application and printing information about this instance.
+Input to \fBinstant\fP is the output of \fBsgmls\fP, whose format is called Element Structure Information Set (ESIS).
+.Sh "FLAGS"
+.Pp
+The following are the possible command line options to \fBinstant\fP. Output is sent to the standard output, except where otherwise noted.
+.\"'br\" labeled list
+.Bl -tag -width Ds
+.It "\fB-t\fP \fIfile\fP"
+Translate the SGML instance to another form, usually suitable for a formatting application.
+The \fIfile\fP is called a translation spec, which specifies how the tags are to be translated. See \fBtranspec\fP(4).
+By convention, names for \fIfile\fP use the suffix \fB.ts\fP, for \fItranslation spec\fP.
+.It "\fB-d\fP"
+"Data hack" \(em strip newline at the beginning of data records
+.It "\fB-f \fIlength\fR"
+Set the threshold for the length, in characters,
+of an <Entry>, over which it is called a block of filled text, to \fIlength\fR.
+.It "\fB-o\fP \fIfile\fP "
+Write all output (except error and warning messages) to file \fIfile\fP. By default, output goes to stdout.
+.It "\fB-h\fP"
+Print a text representation of the hierarchy of the instance elements.
+The deeper an element is in the tree, the more it is indented. The data content is not printed.
+.It "\fB-u\fP"
+Print a summary of the usage of each element in the instance.
+Information given includes attributes, number of children, and depth in the hierarchy.
+.It "\fB-S\fP"
+Print some statistics about element usage in the instance, including how often each element is used
+and how much PCDATA is contained.
+.It "\fB-x\fP"
+Print the context of each element in the instance, from each element to the root of the document tree.
+.It "\fB-v\fP"
+Validate the SGML instance based on the set of constraints or descriptions in the transpec file.
+This flags tells \fBinstant\fP to turn off normal output, leaving only diagnostics.
+.It "\fB-l\fP \fIdirectory\fP"
+Try to read the translation specs or other files from in the directory \fIdirectory\fP
+if not found in the current directory.
+This is called the library directory.
+The environment variable \fITPT_LIB\fP may also be used to specify this.
+.It "\fB-b\fP"
+Interactive browser mode. The user is prompted for actions,
+which include moving among and examining the various nodes in the hierarchy of the instance, displaying information about them, etc.
+.It "\fB-I\fP"
+List the IDs of all elements in the instance that have an ID. The format is more suitable for other programs than humans.
+Lines show the ID, element GI, filename, and line, separated by colons.
+(This depends on the \fB-l\fP option to \fBsgmls\fP which provide filenames and line numbers.)
+.It "\fB-i\fP \fIid\fP"
+When translating the instance, begin processing at the element whose ID is \fIid\fP instead of the topmost element.
+.It "\fB-D\fP \fIvariable\fP\fB=\fP\fIvalue\fP"
+Define the variable \fIvariable\fP with value \fIvalue\fP.
+.It "\fB-W\fP"
+Do not print warning messages.
+.It "\fB-H\fP"
+Print a help message briefly describing the options.
+.It "\fIfile\fP"
+Read the instance from the file \fIfile\fP.
+This is expected to be the output of the program \fBsgmls\fP.
+If not specified, \fBinstant\fP reads the instance from its standard input.
+.El
+.\"'br\" labeled list end
+.Pp
+In some cases it makes no sense to combine options.
+This is especially true if one of the options is to perform a translation. No checking is done for this.
+.Sh "INTERACTIVE BROWSER"
+.Pp
+These are the commands to the interactive browser:
+.Bl -tag -width Ds
+.\"'br\" labeled list
+.It "\fBcd\fP \fIargs ...\fP"
+Change to a different element in the hierarchy.
+\fBcd\fP \fBid\fP \fIid\fP will change to the element whose ID is \fIid\fP.
+\fBcd\fP \fIN\fP will change to the \fIN\fPth child element of the current element.
+Several values of \fIN\fP may be specified, so the program will change to successively descending elements in the hierarchy.
+The string \fB..\fP may appear for \fIN\fP to move up a level in the hierarchy (like in a unix file system).
+A \fB/\fP may be specified for \fIN\fP to change to the top of the hierarchy.
+.It "\fBcont\fP"
+Print the context of each element.
+.It "\fBdata\fP \fIN\fP"
+Show the data content (PCDATA, RCDATA, and DATA) of child node N.
+.It "\fBfind\fP \fIspec\fP"
+Find paths to elements matching \fIspec\fP, where \fIspec\fP may be one of:
+.Bl -tag -width Ds
+.\".RS +\n(INu
+.It "\fBparent\fP \fIgi\fP"
+Find all elements whose parent element is \fIgi\fP.
+.It "\fBchild\fP \fIgi\fP"
+Find all elements which have a child element \fIgi\fP.
+.It "\fBgi\fP \fIgi\fP"
+Find all elements whose name is \fIgi\fP.
+.It "\fBattr\fP \fIname\fP \fIvalue\fP"
+Find all elements that have a attribute \fIname\fP that have a value \fIvalue\fP.
+.\".RE
+.El
+.It "\fBid\fP \fIID\fP"
+Show location of element whose ID is \fIID\fP.
+If \fIID\fP is \fB?\fP, it will list all IDs with the paths to them.
+.It "\fBls\fP"
+List information about the current element in the hierarchy.
+This includes element name, line number in instance, context, attributes and their values, child elements, data directly within this element,
+and the order of the current element among its siblings.
+.It "\fBq\fP \fIrelation\fP \fIelement\fP"
+Report whether or not the current element has the relation \fIrelation\fP to the named element \fIelement\fP.
+Values of \fIrelation\fP are the same as for \fB_followrel\fP in \fBtranspec\fP reference page.
+.It "\fBstat\fP"
+Show statistics about the hierarchy.
+.It "\fBsum\fP"
+Show a tag usage summary about the hierarchy.
+.It "\fBtran\fP \fIoutfile\fP"
+Write translated output to file \fIoutfile\fP.
+If \fIoutfile\fP is not specified, output is sent to stdout.
+.It "\fBtree\fP"
+Print a textual representation of the hierarchy of the instance, where deeper elements are indented more.
+.It "\fBwhere\fP"
+Show current position in the hierarchy.
+.It "<\fBcontrol-D\fP>"
+Exits the program.
+.El
+.Pp
+The \fBstat\fP, \fBsum\fP, \fBtree\fP, \fBcont\fP commands take an optional first argument (of any value),
+which means to only consider the entire instance instead of the hierarchy from the current element.
+.Sh "FILES"
+.Bl -tag -width Ds
+.It "\fIfile\fP\fB.ts\fP"
+Translation specification file.
+.El
+.Sh "SEE ALSO"
+.Pp
+.Xr transpec 5 ,
+.Xr sgmls 1 ,
+Standard Generalized Markup Language (SGML), ISO 8879.
diff --git a/usr.bin/sgmls/instant/main.c b/usr.bin/sgmls/instant/main.c
new file mode 100644
index 0000000..511fedf
--- /dev/null
+++ b/usr.bin/sgmls/instant/main.c
@@ -0,0 +1,710 @@
+/*
+ * 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.
+ */
+/*
+ * 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 read an SGML document instance, creating any of several things:
+ *
+ * "translated" output for formatting applications (given a trans. spec)
+ * validation report (given a appropriate trans spec)
+ * tree of the document's structure
+ * statistics about the element usage
+ * summary of the elements used
+ * context of each element used
+ * IDs of each element
+ *
+ * A C structure is created for each element, which includes:
+ * name, attributes, parent, children, content
+ * The tree is descended, and the desired actions performed.
+ *
+ * Takes input from James Clark's "sgmls" program (v. 1.1).
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/main.c,v 1.8 1996/06/12 03:32:48 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <time.h>
+#include <locale.h>
+
+#define STORAGE
+#include "general.h"
+
+static int do_context, do_tree, do_summ, do_stats, do_validate, do_idlist;
+static int do_DATAhack = 0;
+static char *this_prog;
+static char *in_file, *out_file;
+static char *tranfile;
+static char *start_id;
+static char *last_file;
+static int last_lineno;
+
+extern int BOFTTextThresh;
+
+/* forward references */
+static void HandleArgs(int, char *[]);
+static void Initialize1();
+static void Initialize2();
+static void ReadInstance(char *);
+static void DoHelpMessage();
+extern void Browse();
+
+/* external reference to version number */
+char _HeadVeRsIoN_[] = "1.0 (FreeBSD)";
+
+/* ______________________________________________________________________ */
+/* Program entry point. Look at args, read instance, dispatch to the
+ * correct routines to do the work, and finish.
+ */
+
+int
+main(
+ int ac,
+ char *av[]
+)
+{
+ setlocale(LC_ALL, "");
+ Initialize1(av[0]);
+ HandleArgs(ac, av);
+ Initialize2();
+
+ if (tranfile) ReadTransSpec(tranfile);
+ ReadInstance(in_file);
+
+ if (interactive) {
+ Browse(); /* this will handle interactive commands */
+ }
+ else {
+ /* Perform tasks based on command line flags... */
+ if (tranfile) {
+ Element_t *e;
+ /* If user wants to start at a particular ID, point to that
+ * element. Else, point to the top of the tree. */
+ if (start_id) {
+ if (!(e=FindElemByID(start_id))) {
+ fprintf(stderr, "Error: Can not find element with ID %s\n",
+ start_id);
+ exit(1);
+ }
+ }
+ else e = DocTree;
+ /* If we're doing validation, make output file pointer null.
+ * This means that we generate no output, except error messages. */
+ if (do_validate) outfp = NULL;
+ if (tranfile)
+ DoTranslate(e, outfp);
+ else
+ fprintf(stderr, "Translation spec file not specified. Skipping translation.\n");
+ }
+ if (do_summ) PrintElemSummary(DocTree);
+ if (do_tree) PrintElemTree(DocTree);
+ if (do_stats) PrintStats(DocTree);
+ if (do_context) PrintContext(DocTree);
+ if (do_idlist) PrintIDList();
+ }
+ if (out_file && outfp) fclose(outfp);
+
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+/* Initialization stuff done before dealing with args.
+ * Arguments:
+ * Name of program (string).
+ */
+
+static void
+Initialize1(
+ char *myname
+)
+{
+ time_t tnow;
+ struct tm *nowtm;
+ char *cp, buf[100];
+ extern int gethostname(char *, int); /* not in a system .h file... */
+
+ /* where we try to find data/library files */
+ if (!(tpt_lib=getenv(TPT_LIB))) tpt_lib = DEF_TPT_LIB;
+
+ /* set some global variables */
+ warnings = 1;
+ fold_case = 1;
+ this_prog = myname;
+
+ /* setup global variable mapping */
+ Variables = NewMap(IMS_variables);
+
+ /* set some pre-defined variables */
+ SetMappingNV(Variables, "user", (cp=getenv("USER")) ? cp : "UnknownUser" );
+ time(&tnow);
+ nowtm = localtime(&tnow);
+ strftime(buf, 100, "%a %d %b %Y, %R", nowtm);
+ SetMappingNV(Variables, "date", buf);
+ if (gethostname(buf, 100) < 0) strcpy(buf, "unknown-host");
+ SetMappingNV(Variables, "host", buf);
+ SetMappingNV(Variables, "transpec", tranfile ? tranfile : "??");
+}
+
+/* Initialization stuff done after dealing with args. */
+
+static void
+Initialize2()
+{
+ SetMappingNV(Variables, "transpec", tranfile ? tranfile : "??");
+
+ /* If user wants to send output to a file, open the file, and set
+ * the file pointer. Else we send output to standard out. */
+ if (out_file) {
+ if (!(outfp = fopen(out_file, "w"))) {
+ fprintf(stderr, "Could not open output '%s' file for writing.\n%s",
+ out_file, strerror(errno));
+ exit(1);
+ }
+ }
+ else outfp = stdout;
+}
+
+/* ______________________________________________________________________ */
+/* Set a variable. If it is one of the "known" variables, set the
+ * variable in the C code (this program).
+ * Arguments:
+ * Variable name/value string - separated by an '=' (eg, "myname=Sally").
+ */
+static void
+CmdLineSetVariable(
+ char *var
+)
+{
+ char *cp, buf[100], **tok;
+ int n;
+
+ /* Turn '=' into a space, to isolate the name. Then set variable. */
+ strcpy(buf, var);
+ if ((cp=strchr(buf, '='))) {
+ /* we have "var=value" */
+ *cp = ' ';
+ n = 2;
+ tok = Split(buf, &n, 0);
+ /* see if variable name matches one of our internal ones */
+ if (!strcmp(tok[0], "verbose")) verbose = atoi(tok[1]);
+ else if (!strcmp(tok[0], "warnings")) warnings = atoi(tok[1]);
+ else if (!strcmp(tok[0], "foldcase")) fold_case = atoi(tok[1]);
+ else SetMappingNV(Variables, tok[0], tok[1]);
+ }
+ else {
+ fprintf(stderr, "Expected an '=' in variable assignment: %s. Ignored\n",
+ var);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Bounce through arguments, setting variables and flags.
+ * Arguments:
+ * Argc and Argv, as passed to main().
+ */
+static void
+HandleArgs(
+ int ac,
+ char *av[]
+)
+{
+ int c, errflag=0;
+ extern char *optarg;
+ extern int optind;
+
+ while ((c=getopt(ac, av, "df:t:v:o:huSxIl:bHVWi:D:Z")) != EOF) {
+ switch (c) {
+ case 't': tranfile = optarg; break;
+ case 'v': do_validate = 1; break;
+ case 'h': do_tree = 1; break;
+ case 'u': do_summ = 1; break;
+ case 'S': do_stats = 1; break;
+ case 'x': do_context = 1; break;
+ case 'I': do_idlist = 1; break;
+ case 'l': tpt_lib = optarg; break;
+ case 'i': start_id = optarg; break;
+ case 'o': out_file = optarg; break;
+ case 'd': do_DATAhack = 1; break;
+ case 'f': BOFTTextThresh = atoi(optarg); break;
+ case 'b': interactive = 1; break;
+ case 'W': warnings = 0; break;
+ case 'V': verbose = 1; break;
+ case 'Z': slave = 1; break;
+ case 'H': DoHelpMessage(); exit(0); break;
+ case 'D': CmdLineSetVariable(optarg); break;
+ case '?': errflag = 1; break;
+ }
+ if (errflag) {
+ fprintf(stderr, "Try '%s -H' for help.\n", this_prog);
+ exit(1);
+ }
+ }
+
+ /* input (ESIS) file name */
+ if (optind < ac) in_file = av[optind];
+
+ /* If doing interactive/browsing, we can't take ESIS from stdin. */
+ if (interactive && !in_file) {
+ fprintf(stderr,
+ "You must specify ESIS file on cmd line for browser mode.\n");
+ exit(1);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Simply print out a help/usage message.
+ */
+
+static char *help_msg[] = {
+ "",
+ " -t file Print translated output using translation spec in <file>",
+ " -v Validate using translation spec specified with -t",
+ " -i id Consider only subtree starting at element with ID <id>",
+ " -b Interactive browser",
+ " -S Print statistics (how often elements occur, etc.)",
+ " -u Print element usage summary (# of children, depth, etc.)",
+ " -x Print context of each element",
+ " -h Print document hierarchy as a tree",
+ " -o file Write output to <file>. Default is standard output.",
+ " -l dir Set library directory to <dir>. (or env. variable TPT_LIB)",
+ " -I List all IDs used in the instance",
+ " -W Do not print warning messages",
+ " -H Print this help message",
+ " -Dvar=val Set variable 'var' to value 'val'",
+ " file Take input from named file. If not specified, assume stdin.",
+ " File should be output from the 'sgmls' program (ESIS).",
+ NULL
+};
+
+static void
+DoHelpMessage()
+{
+ char **s = help_msg;
+ printf("usage: %s [option ...] [file]", this_prog);
+ while (*s) puts(*s++);
+ printf("\nVersion: %s\n", _HeadVeRsIoN_);
+}
+
+/* ______________________________________________________________________ */
+/* Remember an external entity for future reference.
+ * Arguments:
+ * Pointer to entity structure to remember.
+ */
+
+static void
+AddEntity(
+ Entity_t *ent
+)
+{
+ static Entity_t *last_ent;
+
+ if (!Entities) {
+ Malloc(1, Entities, Entity_t);
+ last_ent = Entities;
+ }
+ else {
+ Malloc(1, last_ent->next, Entity_t);
+ last_ent = last_ent->next;
+ }
+ *last_ent = *ent;
+
+}
+
+/* Find an entity, given its entity name.
+ * Arguments:
+ * Name of entity to retrieve.
+ */
+static Entity_t *
+FindEntity(
+ char *ename
+)
+{
+ Entity_t *n;
+ for (n=Entities; n; n=n->next)
+ if (StrEq(ename, n->ename)) return n;
+ return 0;
+}
+
+/* Accumulate lines up to the open tag. Attributes, line number,
+ * entity info, notation info, etc., all come before the open tag.
+ */
+static Element_t *
+AccumElemInfo(
+ FILE *fp
+)
+{
+ char buf[LINESIZE+1];
+ int c;
+ int i, na;
+ char *cp, *atval;
+ Mapping_t a[100];
+ Element_t *e;
+ Entity_t ent, *ent2;
+ char **tok;
+ static int Index=0;
+ static Element_t *last_e;
+
+
+ Calloc(1, e, Element_t);
+ memset(&ent, 0, sizeof ent); /* clean space for entity info */
+
+ /* Also, keep a linked list of elements, so we can easily scan through */
+ if (last_e) last_e->next = e;
+ last_e = e;
+
+ e->index = Index++; /* just a unique number for identification */
+
+ /* in case these are not set for this element in the ESIS */
+ e->lineno = last_lineno;
+ e->infile = last_file;
+
+ na = 0;
+ while (1) {
+ if ((c = getc(fp)) == EOF) break;
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ switch (c) {
+ case EOF: /* End of input */
+ fprintf(stderr, "Error: Unexpectedly reached end of ESIS.\n");
+ exit(1);
+ break;
+
+ case CMD_OPEN: /* (gi */
+ e->gi = AddElemName(buf);
+ if (na > 0) {
+ Malloc(na, e->atts, Mapping_t);
+ memcpy(e->atts, a, na*sizeof(Mapping_t));
+ e->natts = na;
+ na = 0;
+ }
+ /* Check if this elem has a notation attr. If yes, and there
+ is no notation specified, recall the previous one. (feature
+ of sgmls - it does not repeat notation stuff if we the same
+ is used twice in a row) */
+ if (((atval=FindAttValByName(e, "NAME")) ||
+ (atval=FindAttValByName(e, "ENTITYREF")) ||
+ (atval=FindAttValByName(e, "EXTERNAL"))) && /* HACK */
+ (ent2=FindEntity(atval))) {
+ e->entity = ent2;
+ }
+
+ return e;
+ break;
+
+ case CMD_ATT: /* Aname val */
+ i = 3;
+ tok = Split(buf, &i, 0);
+ if (!strcmp(tok[1], "IMPLIED"))
+ break; /* skip IMPLIED atts. */
+ else if (!strcmp(tok[1], "CDATA"))
+ {
+ /* CDATA attributes must have ESIS escape
+ sequences and SDATA entities expanded. */
+ char *val = ExpandString(tok[2]);
+ a[na].name = AddAttName(tok[0]);
+ a[na].sval = AddAttName(val);
+ free(val);
+ na++;
+ }
+ else if (!strcmp(tok[1], "TOKEN") ||
+ !strcmp(tok[1], "ENTITY") ||!strcmp(tok[1], "NOTATION"))
+ {
+ a[na].name = AddAttName(tok[0]);
+ a[na].sval = AddAttName(tok[2]);
+ na++;
+ }
+ else {
+ fprintf(stderr, "Error: Bad attr line (%d): A%s %s...\n",
+ e->lineno, tok[0], tok[1]);
+ }
+ break;
+
+ case CMD_LINE: /* Llineno */
+ /* These lines come in 2 forms: "L123" and "L123 file.sgml".
+ * Filename is given only at 1st occurance. Remember it.
+ */
+ if ((cp = strchr(buf, ' '))) {
+ cp++;
+ last_file = strdup(cp);
+ }
+ last_lineno = e->lineno = atoi(buf);
+ e->infile = last_file;
+ break;
+
+ case CMD_DATA: /* -data */
+ fprintf(stderr, "Error: Data in AccumElemInfo, line %d:\n%c%s\n",
+ e->lineno, c,buf);
+ /*return e;*/
+ exit(1);
+ break;
+
+ case CMD_D_ATT: /* Dename name val */
+
+ case CMD_NOTATION: /* Nnname */
+ case CMD_PI: /* ?pi */
+ /* This should be reworked soon, as it
+ forces all PI's before the first GI
+ to be ignored. -CSS */
+ break;
+
+ case CMD_EXT_ENT: /* Eename typ nname */
+ i = 3;
+ tok = Split(buf, &i, 0);
+ ent.ename = strdup(tok[0]);
+ ent.type = strdup(tok[1]);
+ ent.nname = strdup(tok[2]);
+ AddEntity(&ent);
+ break;
+ case CMD_INT_ENT: /* Iename typ text */
+ fprintf(stderr, "Error: Got CMD_INT_ENT in AccumElemInfo: %s\n", buf);
+ break;
+ case CMD_SYSID: /* ssysid */
+ ent.sysid = strdup(buf);
+ break;
+ case CMD_PUBID: /* ppubid */
+ ent.pubid = strdup(buf);
+ break;
+ case CMD_FILENAME: /* ffilename */
+ ent.fname = strdup(buf);
+ break;
+
+ case CMD_CLOSE: /* )gi */
+ case CMD_SUBDOC: /* Sename */
+ case CMD_SUBDOC_S: /* {ename */
+ case CMD_SUBDOC_E: /* }ename */
+ case CMD_EXT_REF: /* &name */
+ case CMD_APPINFO: /* #text */
+ case CMD_CONFORM: /* C */
+ default:
+ fprintf(stderr, "Error: Unexpected input in AccumElemInfo, %d:\n%c%s\n",
+ e->lineno, c,buf);
+ exit(1);
+ break;
+ }
+ }
+ fprintf(stderr, "Error: End of AccumElemInfo - should not be here: %s\n",
+ e->gi);
+/* return e;*/
+ exit(1);
+}
+
+/* Read ESIS lines.
+ * Limitation? Max 5000 children per node. (done for efficiency --
+ * should do some malloc and bookkeeping games later).
+ */
+
+static Element_t *
+ReadESIS(
+ FILE *fp,
+ int depth
+)
+{
+ char *buf;
+ int i, c, ncont;
+ Element_t *e;
+ Content_t cont[5000];
+
+ Malloc( LINESIZE+1, buf, char );
+
+ /* Read input stream - the output of "sgmls", called "ESIS". */
+ e = AccumElemInfo(fp);
+ e->depth = depth;
+
+ ncont = 0;
+ while (1) {
+ if ((c = getc(fp)) == EOF) break;
+ switch (c) {
+ case EOF: /* End of input */
+ break;
+
+ case CMD_DATA: /* -data */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ if (do_DATAhack && (buf[0] == '\\') && (buf[1] == 'n')) {
+ if ( ! buf[2] )
+ break;
+ buf[0] = ' ';
+ memcpy(&buf[1], &buf[2], strlen(buf)-1);
+ }
+ cont[ncont].ch.data = ExpandString(buf);
+ cont[ncont].type = CMD_DATA;
+ ncont++;
+ break;
+
+ case CMD_PI: /* ?pi */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ cont[ncont].type = CMD_PI;
+ cont[ncont].ch.data = strdup(buf);
+ ncont++;
+ break;
+
+ case CMD_CLOSE: /* )gi */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ if (ncont) {
+ e->ncont = ncont;
+ Malloc(ncont, e->cont, Content_t);
+ for (i=0; i<ncont; i++) e->cont[i] = cont[i];
+ }
+ free(buf);
+ return e;
+ break;
+
+ case CMD_OPEN: /* (gi */
+/*fprintf(stderr, "+++++ OPEN +++\n");*/
+/* break;*/
+
+ case CMD_ATT: /* Aname val */
+ case CMD_D_ATT: /* Dename name val */
+ case CMD_NOTATION: /* Nnname */
+ case CMD_EXT_ENT: /* Eename typ nname */
+ case CMD_INT_ENT: /* Iename typ text */
+ case CMD_SYSID: /* ssysid */
+ case CMD_PUBID: /* ppubid */
+ case CMD_FILENAME: /* ffilename */
+ ungetc(c, fp);
+ cont[ncont].ch.elem = ReadESIS(fp, depth+1);
+ cont[ncont].type = CMD_OPEN;
+ cont[ncont].ch.elem->parent = e;
+ ncont++;
+ break;
+
+ case CMD_LINE: /* Llineno */
+ fgets(buf, LINESIZE, fp);
+ break; /* ignore these here */
+
+ case CMD_SUBDOC: /* Sename */
+ case CMD_SUBDOC_S: /* {ename */
+ case CMD_SUBDOC_E: /* }ename */
+ case CMD_EXT_REF: /* &name */
+ case CMD_APPINFO: /* #text */
+ case CMD_CONFORM: /* C */
+ default:
+ fgets(buf, LINESIZE, fp);
+ fprintf(stderr, "Error: Unexpected input at %d: '%c%s'\n",
+ e->lineno, c, buf);
+ exit(1);
+ break;
+ }
+ }
+ fprintf(stderr, "Error: End of ReadESIS - should not be here: %s\n", e->gi);
+ free(buf);
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Read input stream, creating a tree in memory of the elements and data.
+ * Arguments:
+ * Filename where instance's ESIS is.
+ */
+static void
+ReadInstance(
+ char *filename
+)
+{
+ int i, n;
+ FILE *fp;
+ Element_t *e;
+ char *idatt;
+
+ if (filename) { /* if we specified input file. else stdin */
+ if ((fp=fopen(filename, "r")) == NULL) {
+ perror(filename);
+ exit(1);
+ }
+ }
+ else fp = stdin;
+ last_file = filename;
+ DocTree = ReadESIS(fp, 0);
+ if (filename) fclose(fp);
+
+ /* Traverse tree, filling in econt and figuring out which child
+ * (ie. what birth order) each element is. */
+ DocTree->my_eorder = -1;
+ for (e=DocTree; e; e=e->next) {
+
+ /* count element children */
+ for (i=0,n=0; i<e->ncont; i++) if (IsContElem(e,i)) n++;
+ if (n > 0) Calloc(n, e->econt, Element_t *);
+ for (i=0; i<e->ncont; i++)
+ if (IsContElem(e,i)) e->econt[e->necont++] = ContElem(e,i);
+
+ /* count data children */
+ for (i=0,n=0; i<e->ncont; i++) if (IsContData(e,i)) n++;
+ if (n > 0) Calloc(n, e->dcont, char *);
+ for (i=0; i<e->ncont; i++)
+ if (IsContData(e,i)) e->dcont[e->ndcont++] = ContData(e,i);
+
+ /* where in child order order */
+ for (i=0; i<e->necont; i++)
+ e->econt[i]->my_eorder = i;
+
+ /* Does this element have an ID? */
+ for (i=0; i<e->natts; i++) {
+ if ((idatt=FindAttValByName(e, "ID"))) {
+ AddID(e, idatt);
+ /* remember ID value for quick reference */
+ e->id = idatt;
+ break;
+ }
+ }
+ }
+ return;
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/tables.c b/usr.bin/sgmls/instant/tables.c
new file mode 100644
index 0000000..54fd211
--- /dev/null
+++ b/usr.bin/sgmls/instant/tables.c
@@ -0,0 +1,2013 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ *
+ * Originally coded for OSF DTD tables, now recoded (fld 3/27/95)
+ * for CALS-type tables (fragment taken from the DocBook DTD). Then,
+ * *really* upgraded to CALS tables by FLD on 5/28/96.
+ *
+ * This module is for handling table markup, printing TeX or tbl
+ * (tbl) markup to the output stream. Also, table markup checking is
+ * done here. Yes, this depends on the DTD, but it makes translation
+ * specs much cleaner (and makes some things possible).
+ *
+ * Incomplete / not implemented / limitations / notes:
+ * vertical alignment (valign attr)
+ * vertical spanning
+ * row separators are for the whole line, not per cell (the prog looks
+ * at rowsep for the 1st cell and applies it to the whole row)
+ * trusts that units in colwidths are acceptable to LaTeX and tbl
+ * "s" is an acceptable shorthand for "span" in model attributes
+ *
+ * A note on use of OutputString(): Strings with backslashes (\) need lots
+ * of backslashes. You have to escape them for the C compiler, and escape
+ * them again for OutputString() itself.
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/tables.c,v 1.11 1996/06/15 03:45:02 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 <regexp.h>
+#include "general.h"
+#include "translate.h"
+
+/* text width of page, in inches */
+#define TEXTWIDTH 5.5
+#define MAXCOLS 100
+#define SPAN_NOT 0
+#define SPAN_START 1
+#define SPAN_CONT 2
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+/*table parameters */
+
+#define TBLMAXCOL 30 /* max number of columns in tbl table */
+#define NAMELEN 40 /* max length of a name */
+#define BOFTTHRESHOLD 35 /* text length over which to consider
+ * generating a block of filled text */
+
+
+/* handy declarations */
+
+typedef enum { Left, Right, Center, Justify, Char, Span } tblalign;
+
+typedef enum { TGroup, THead, TFoot, TBody } tblsource; /* source of a spec */
+
+
+/* table line format information structures */
+
+struct tblcolspec {
+
+ char name[NAMELEN]; /* colspec's name */
+ short num; /* column number */
+ tblsource source; /* where defined */
+
+ tblalign align; /* column's alignment */
+ char alignchar; /* character for alignment */
+ short aligncharoff; /* offset for alignment */
+ char colwidth[10]; /* width for column */
+ char colpwidth[10]; /* proportional widths for column */
+ bool colsep; /* separator to right of column? */
+ bool rowsep; /* separator to bottom of column? */
+ short moreRows; /* value for Morerows */
+
+ struct tblcolspec * next; /* next colspec */
+};
+
+struct tblspanspec {
+
+ char name[NAMELEN]; /* spanspec's name */
+ tblsource source; /* where defined */
+
+ struct tblcolspec * start; /* start column */
+ struct tblcolspec * end; /* end column */
+ tblalign align; /* span's alignment */
+ char alignchar; /* character for alignment */
+ short aligncharoff; /* offset for alignment */
+ bool colsep; /* separator to right of column? */
+ bool rowsep; /* separator to bottom of column? */
+
+ struct tblspanspec * next; /* next spanspec */
+};
+
+struct tblformat {
+ short count; /* count of rows matching this spec */
+
+ short cols; /* # of columns */
+ short rowNum; /* row number */
+ char colformat[TBLMAXCOL]; /* per-column formats */
+ char colwidth[TBLMAXCOL][10]; /* per-column widths */
+ char colpwidth[TBLMAXCOL][10]; /* per-column proportional widths */
+ char font[TBLMAXCOL][3]; /* column fonts (headers) */
+ bool colsep[TBLMAXCOL]; /* column separators */
+ bool rowsep[TBLMAXCOL]; /* row separators */
+ short moreRows[TBLMAXCOL]; /* moreRows indicator */
+
+ struct tblformat * next; /* for the next row */
+};
+
+
+/* table state info */
+
+static short tblcols = 0; /* number of columns in the table */
+static short tblrow = 0; /* the current row in the table */
+
+static bool tblTGroupSeen = FALSE; /* seen a TGroup in this table yet? */
+
+static char * tblFrame; /* table frame info */
+static bool tblgcolsep; /* global colsep (in table) */
+static bool tblgrowsep; /* global rowsep (in table) */
+
+static int tblBOFTCount = 0; /* count of bofts that we've created
+ * (per table) */
+int BOFTTextThresh = BOFTTHRESHOLD;
+ /* length of text before we
+ * call it a BOFT */
+static bool tblboft = FALSE; /* within a block of filled text? */
+static bool tblinBOFT = FALSE; /* within a boft now? */
+
+static struct tblformat * formP = 0; /* THead/TBody format lines */
+
+static struct tblcolspec * tblColSpec = 0; /* colspec structure for table */
+static struct tblspanspec * tblSpanSpec = 0; /* spanspec structure for table */
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+/* these cover the attributes on the Table, TGroup, Colspec elements */
+typedef struct {
+ char *cols;
+ char *align, **align_v;
+ char *colwidth, **colwidth_v;
+ char *colsep, **colsep_v;
+ char *rowsep, **rowsep_v;
+ char *frame;
+ char *orient;
+ int pgwide;
+ int n_align, n_model, n_colwidth, n_colsep;
+ int nc;
+} TableInfo;
+
+
+/* some flags, set when the table tag is processed, used later */
+static int rowsep, siderules;
+static int frametop, framebot, frameall;
+static char basemodel[128]; /* model for table (in formatting language) */
+static int spaninfo[MAXCOLS]; /* 100 columns, max */
+static TableInfo TheTab;
+
+/* forward references */
+void SetTabAtts(Element_t *, TableInfo *, int);
+void FreeTabAtts(TableInfo *);
+void ClearTable(TableInfo *);
+void CheckTable(Element_t *);
+void TblTStart(Element_t *, FILE *);
+void TblTEnd(Element_t *, FILE *);
+void TblTGroup(Element_t *, FILE *);
+void TblTGroupEnd(Element_t *, FILE *);
+void TblTFoot(Element_t *, FILE *);
+void TblBuildFormat(Element_t *, struct tblformat **, tblsource);
+struct tblformat * TblBuild1Format(Element_t *, bool, tblsource);
+char TblGetAlign(short, Element_t *, tblsource);
+char * TblGetWidth(short, Element_t *, bool, tblsource);
+char * TblGetFont(short, Element_t *, tblsource);
+bool TblGetColSep(short, Element_t *, tblsource);
+bool TblGetRowSep(short, Element_t *, tblsource);
+short TblGetMoreRows(short, Element_t *, tblsource);
+bool TblColAdv(short, Element_t *, struct tblformat *, tblsource);
+struct tblcolspec * TblEntryColSpec(short, Element_t *, tblsource);
+struct tblspanspec * TblEntrySpanSpec(short, Element_t *, tblsource);
+bool TblFormatMatch(struct tblformat *, struct tblformat *);
+void TblPrintFormat(FILE *, struct tblformat *);
+void TblTRowStart(Element_t *, FILE *);
+void TblTRowEnd(Element_t *, FILE *);
+void TblTCellStart(Element_t *, FILE *);
+int TblCountContent(Element_t *);
+void TblTCellEnd(Element_t *, FILE *);
+struct tblcolspec * TblDoColSpec(short, Element_t *, struct tblcolspec *, tblsource);
+struct tblspanspec * TblDoSpanSpec(Element_t *, struct tblspanspec *, tblsource);
+struct tblcolspec * TblFindColSpec(char *, tblsource);
+struct tblcolspec * TblFindColNum(short, tblsource);
+struct tblspanspec * TblFindSpanSpec(char *, tblsource);
+void TexTable(Element_t *, FILE *);
+void TexTableCellStart(Element_t *, FILE *);
+void TexTableCellEnd(Element_t *, FILE *);
+void TexTableRowStart(Element_t *, FILE *);
+void TexTableRowEnd(Element_t *, FILE *);
+void TexTableTop(Element_t *, FILE *);
+void TexTableBottom(Element_t *, FILE *);
+
+/* ______________________________________________________________________ */
+/* Hard-coded stuff for CALS-style DTD tables.
+ * Here are the TABLE attributes (for handy reference):
+ *
+ * Table/InformalTable:
+ * Colsep NUMBER separate all columns in table?
+ * Frame (Top|Bottom|Topbot|All|Sides|None) frame style
+ * Orient (Port | Land) orientation
+ * Pgwide NUMBER wide table?
+ * Rowsep NUMBER separate all rows in the table?
+ * Tabstyle NMTOKEN FOSI table style
+ *
+ * TGroup:
+ * Align (Left|Right|Center|Justify|Char) alignment of cols
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Cols NUMBER number of columns
+ * Colsep NUMBER separate all columns in tgroup?
+ * Rowsep NUMBER separate all rows in tgroup?
+ * TGroupstyle NMTOKEN FOSI table group style
+ *
+ * Colspec:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colname NMTOKEN Column identifier
+ * Colnum NUMBER number of column
+ * Colsep NUMBER separate this col from next?
+ * Colwidth CDATA width spec
+ * Rowsep NUMBER serarate entry from following row?
+ *
+ * SpanSpec:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colsep NUMBER separate this col from next?
+ * Nameend NMTOKEN name of rightmost col of a span
+ * Namest NMTOKEN name of leftmost col of a span
+ * Rowsep NUMBER serarate entry from following row?
+ * Spanname NMTOKEN name of a horiz. span
+ *
+ * THead/TFoot/TBody:
+ * VAlign (Top | Middle | Bottom) group placement
+ *
+ * Row:
+ * Rowsep NUMBER separate this row from next?
+ * VAlign (Top | Middle | Bottom) row placement
+ *
+ * Entry:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colname NMTOKEN Column identifier
+ * Colsep NUMBER separate this col from next?
+ * Morerows NUMBER number of addn'l rows in vert straddle
+ * Nameend NMTOKEN name of rightmost col of a span
+ * Namest NMTOKEN name of leftmost col of a span
+ * Rotate NUMBER 90 degree rotation counterclockwise to table?
+ * Rowsep NUMBER serarate entry from following row?
+ * Spanname NMTOKEN name of a horiz. span
+ * VAlign (Top | Middle | Bottom) text vert alignment
+ *
+ *
+ ** OBSOLETE OSF DTD FORM (still used for TeX form):
+ ** Usage in transpec: _calstable [tex|check|clear] ['aspect']
+ ** where 'aspect' is:
+ ** rowstart stuff to do at start of a row (tests for spanning)
+ ** rowend stuff to do at end of a row (eg, rules, etc.)
+ ** cellstart stuff to do at start of a cell (eg, handle actual
+ ** spanning instructions, etc.)
+ ** cellend stuff to do at end of a cell (eg, cell separator)
+ ** top stuff to do at top of the table
+ ** (like whether or not it needs a starting horiz rule)
+ ** bottom stuff to do at bottom of the table
+ ** (like whether or not it needs an ending horiz rule)
+ ** (nothing) the 'cols' param to LaTeX's \begin{tabular}[pos]{cols}
+ ** or 'options' and 'formats' part in tbl
+ *
+ *
+ * New tbl form:
+ * Usage in transpec: _calstable [tbl] ['aspect']
+ * where 'aspect' is:
+ * tablestart start a table and do style info
+ * tableend end the table and clean up
+ * tablegroup table TGroup (.T& if not 1st, line format info)
+ * tablegroupend end a TGroup
+ * tablefoot TFoot within a TGroup
+ * rowstart start of a row
+ * rowend end of a row
+ * entrystart start of an entry (block of filled text, if
+ * appropriate)
+ * entryend end of a cell (eg, cell separator)
+ */
+
+/* Procedure to
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ * Vector of args to _osftable
+ * Count of args to _osftable
+ */
+void
+CALStable(
+ Element_t *e,
+ FILE *fp,
+ char **av,
+ int ac
+)
+{
+ /* Check params and dispatch to appropriate routine */
+
+ if (!strcmp(av[1], "tbl")) {
+
+ if (ac > 2) {
+ if (!strcmp(av[2], "tablestart")) TblTStart(e, fp);
+ else if (!strcmp(av[2], "tableend")) TblTEnd(e, fp);
+ else if (!strcmp(av[2], "tablegroup")) TblTGroup(e, fp);
+ else if (!strcmp(av[2], "tablegroupend")) TblTGroupEnd(e, fp);
+ else if (!strcmp(av[2], "tablefoot")) TblTFoot(e, fp);
+ else if (!strcmp(av[2], "rowstart")) TblTRowStart(e, fp);
+ else if (!strcmp(av[2], "rowend")) TblTRowEnd(e, fp);
+ else if (!strcmp(av[2], "entrystart")) TblTCellStart(e, fp);
+ else if (!strcmp(av[2], "entryend")) TblTCellEnd(e, fp);
+ else fprintf(stderr, "Unknown %s table instruction: %s\n",
+ av[1], av[2]);
+ }
+ else {
+ fprintf(stderr, "Incomplete %s table instruction\n");
+ }
+ }
+
+ else if (!strcmp(av[1], "tex")) {
+
+ if (ac > 1 && !strcmp(av[1], "check")) CheckTable(e);
+
+ else
+ if (ac > 1 && !strcmp(av[1], "clear")) ClearTable(&TheTab);
+
+ if (ac > 2) {
+ if (!strcmp(av[2], "cellstart")) TexTableCellStart(e, fp);
+ else if (!strcmp(av[2], "cellend")) TexTableCellEnd(e, fp);
+ else if (!strcmp(av[2], "rowstart")) TexTableRowStart(e, fp);
+ else if (!strcmp(av[2], "rowend")) TexTableRowEnd(e, fp);
+ else if (!strcmp(av[2], "top")) TexTableTop(e, fp);
+ else if (!strcmp(av[2], "bottom")) TexTableBottom(e, fp);
+ else fprintf(stderr, "Unknown %s table instruction: %s\n",
+ av[1], av[2]);
+ }
+ else TexTable(e, fp);
+ }
+
+ else fprintf(stderr, "Unknown table type: %s\n", av[1]);
+
+}
+
+/* ClearTable -- start a new table process
+ *
+ */
+
+
+void
+ClearTable( TableInfo * t )
+{
+ memset(t, 0, sizeof(TableInfo));
+}
+
+
+/* ______________________________________________________________________ */
+/* Set values of the our internal table structure based on the table's
+ * attributes. (This is called for tables, tgroups, colspecs, and rows,
+ * since tables and rows share many of the same attributes.)
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer table info structure which will be filled in.
+ * Flag saying whether or not to set global variables based on attrs.
+ */
+void
+SetTabAtts(
+ Element_t *e,
+ TableInfo *t,
+ int set_globals
+)
+{
+ char *at;
+ Element_t * ep;
+
+ /* remember values of attributes */
+ if ((at = FindAttValByName(e, "ALIGN"))) t->align = at;
+ if ((at = FindAttValByName(e, "COLWIDTH"))) t->colwidth = at;
+ if ((at = FindAttValByName(e, "COLSEP"))) t->colsep = at;
+ if ((at = FindAttValByName(e, "FRAME"))) t->frame = at;
+ if ((at = FindAttValByName(e, "COLS"))) t->cols = at;
+
+ /* Set some things for later when processing this table */
+ if (set_globals) {
+
+ rowsep = 1;
+ frametop = framebot = 1; /* default style */
+
+ /* For now we look at the first number of rowsep - it controls the
+ * horiz rule for then entire row. (not easy to specify lines that
+ * span only some columns in tex or tbl. */
+ if ((at = FindAttValByName(e, "ROWSEP"))) rowsep = atoi(at);
+ }
+
+ if (t->frame) {
+ /* Top|Bottom|Topbot|All|Sides|None */
+ if (!strcmp(t->frame, "NONE") || !strcmp(t->frame, "SIDES"))
+ frametop = framebot = 0;
+ else if (!strcmp(t->frame, "TOP")) framebot = 0;
+ else if (!strcmp(t->frame, "BOTTOM")) frametop = 0;
+ }
+
+ /* tbl and tex like lower case for units. convert. */
+ if (t->colwidth) {
+ char *cp;
+ for (cp=t->colwidth; *cp; cp++)
+ if (isupper(*cp)) *cp = tolower(*cp);
+ }
+
+ /* Now, split (space-separated) strings into vectors. Hopefully, the
+ * number of elements in each vector matches the number of columns.
+ */
+ t->align_v = Split(t->align, &t->n_align, S_STRDUP|S_ALVEC);
+ t->colwidth_v = Split(t->colwidth, &t->n_colwidth, S_STRDUP|S_ALVEC);
+ t->colsep_v = Split(t->colsep, &t->n_colsep, S_STRDUP|S_ALVEC);
+
+ /* Determin the _numeric_ number of columns, "nc". MUST be specified
+ * in Cols attribute of TGroup element.
+ */
+ if (t->cols) t->nc = atoi(t->cols);
+}
+
+/* ______________________________________________________________________ */
+
+/* Free the storage of info use by the table info structure. (not the
+ * structure itself, but the strings its elements point to)
+ * Arguments:
+ * Pointer table info structure to be freed.
+ */
+void
+FreeTabAtts(
+ TableInfo *t
+)
+{
+ if (!t) return;
+ if (t->align_v) free(*t->align_v);
+ if (t->colwidth_v) free(*t->colwidth_v);
+ if (t->colsep_v) free(*t->colsep_v);
+}
+
+/* ______________________________________________________________________ */
+/* Check the attributes and children of the table pointed to by e.
+ * Report problems and inconsistencies to stderr.
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ */
+
+void
+CheckTable(
+ Element_t *e
+)
+{
+ int pr_loc=0; /* flag to say if we printed location */
+ int i, r, c;
+ Element_t *ep, *ep2;
+ float wt;
+ char *tpref = "Table Check"; /* prefix for err messages */
+ char *ncolchk =
+ "Table Check: %s ('%s') has wrong number of tokens. Expecting %d.\n";
+
+ if (strcmp(e->gi, "TABLE") &&
+ strcmp(e->gi, "INFORMALTABLE") &&
+ strcmp(e->gi, "TGROUP") &&
+ strcmp(e->gi, "COLSPEC") &&
+ strcmp(e->gi, "ROW") ) {
+ fprintf(stderr, "%s: Not pointing to a table element(%s)!\n",
+ tpref, e->gi);
+ return;
+ }
+
+ FreeTabAtts(&TheTab); /* free storage, if allocated earlier */
+ SetTabAtts(e, &TheTab, 1); /* look at attributes */
+
+#if FALSE
+ /* NCOLS attribute set? */
+ if (!TheTab.ncols) {
+ pr_loc++;
+ fprintf(stderr, "%s: NCOLS attribute missing. Inferred as %d.\n",
+ tpref, TheTab.nc);
+ }
+
+ /* ALIGN attribute set? */
+ if (!TheTab.align) {
+ pr_loc++;
+ fprintf(stderr, "%s: ALIGN attribute missing.\n", tpref);
+ }
+
+ /* See if the number of cells in each row matches */
+ for (r=0; r<e->necont && (ep=e->econt[r]); r++) { /* each TGroup */
+ for (i=0; i<ep->necont && (ep2=ep->econt[i]); i++) {
+ if ( strcmp(ep2->gi, "TBODY") ) /* only TBodys */
+ continue;
+
+ for (c=0; c<ep2->necont; c++) {
+ if (ep2->econt[c]->necont != TheTab.nc) {
+ pr_loc++;
+ fprintf(stderr, "%s: COLS (%d) differs from actual number of cells (%d) in row %d.\n",
+ tpref, TheTab.nc, ep2->econt[c]->necont, c);
+ }
+ }
+ }
+ }
+#endif
+
+ /* Check ALIGN */
+ if (TheTab.align) {
+ if (TheTab.nc != TheTab.n_align) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "ALIGN", TheTab.align, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+ if (*TheTab.align_v[i] != 'C' && *TheTab.align_v[i] != 'L' &&
+ *TheTab.align_v[i] != 'R') {
+ pr_loc++;
+ fprintf(stderr, "%s: ALIGN (%d) value wrong: %s\n",
+ tpref, i, TheTab.align_v[i]);
+ }
+ }
+ }
+ }
+
+ /* check COLWIDTH */
+ if (TheTab.colwidth) {
+ if (TheTab.nc != TheTab.n_colwidth) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "COLWIDTH", TheTab.colwidth, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+
+ /* check that the units after the numbers are OK
+ we want "in", "cm".
+ */
+ }
+ }
+ }
+
+ /* check COLSEP */
+ if (TheTab.colsep) {
+ if (TheTab.nc != TheTab.n_colsep) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "COLSEP", TheTab.colsep, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+ }
+ }
+ }
+
+ if (pr_loc) {
+ fprintf(stderr, "%s: Above problem in table located at:\n", tpref);
+ PrintLocation(e, stderr);
+ }
+}
+
+/* ______________________________________________________________________ */
+
+/* Look at colspec attribute for spanning. If set, remember info for when
+ * doing the cells. Called by TblTableRowStart() and TexTableRowStart().
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ */
+int
+check_for_spans(
+ Element_t *e
+)
+{
+ char *at;
+ char **spans;
+ int n, i, inspan;
+
+#if FALSE /* NOT IMPLEMENTED RIGHT NOW */
+
+ /* See if COLSPEC element present */
+ for (i=0; i < e->necont; i++) {
+
+ }
+
+
+ if ((at = FindAttValByName(e, "MODEL"))) {
+
+ /* Split into tokens, then look at each for the word "span" */
+ n = TheTab.nc;
+ spans = Split(at, &n, S_STRDUP|S_ALVEC);
+
+ /* Mark columns as start-of-span, in-span, or not spanned. Remember
+ * in at list, "spaningo". (Span does not make sense in 1st column.)
+ */
+ for (i=1,inspan=0; i<n; i++) {
+ if (StrEq(spans[i], "span") || StrEq(spans[i], "s")) {
+ if (inspan == 0) spaninfo[i-1] = SPAN_START;
+ spaninfo[i] = SPAN_CONT;
+ inspan = 1;
+ }
+ else {
+ spaninfo[i] = SPAN_NOT;
+ inspan = 0;
+ }
+ }
+ free(*spans); /* free string */
+ free(spans); /* free vector */
+ spaninfo[TheTab.nc] = SPAN_NOT; /* after last cell */
+ return 1;
+ }
+ /* if model not set, mark all as not spanning */
+ else
+
+#endif /* NOT CURRENTLY IMPLEMENTED */
+
+ for (i=0; i<MAXCOLS; i++) spaninfo[i] = SPAN_NOT;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+/* Do the "right thing" for the table spec for TeX tables. This will
+ * generate the arg to \begin{tabular}[xxx].
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTable(
+ Element_t *e,
+ FILE *fp
+)
+{
+ int i, n;
+ float tot;
+ char *cp, wbuf[1500], **widths=0, **widths_v=0;
+
+ FreeTabAtts(&TheTab); /* free storage, if allocated earlier */
+ SetTabAtts(e, &TheTab, 1); /* look at attributes */
+ SetTabAtts(e->econt[0], &TheTab, 1); /* attrs of TGroup */
+
+ /* Figure out the widths, based either on "colwidth".
+ */
+ if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) {
+ widths = TheTab.colwidth_v;
+ }
+
+ siderules = 1;
+ if (TheTab.frame)
+ if (strcmp(TheTab.frame, "ALL") && strcmp(TheTab.frame, "SIDES"))
+ siderules = 0;
+
+ if (siderules) OutputString("|", fp, 1);
+ for (i=0; i<TheTab.nc; i++) {
+ /* If width specified, use it; else if align set, use it; else left. */
+ if (widths && widths[i][0] != '0' && widths[i][1] != EOS) {
+ fprintf(fp, "%sp{%s}", (i?" ":""), widths[i]);
+ }
+ else if (TheTab.align && TheTab.nc == TheTab.n_align) {
+ fprintf(fp, "%s%s", (i?" ":""), TheTab.align_v[i]);
+ }
+ else
+ fprintf(fp, "%sl", (i?" ":""));
+ /* See if we want column separators. */
+ if (TheTab.colsep) {
+
+ if ( (i+1) < TheTab.nc ) {
+ if ( *TheTab.colsep_v[i] == '1' ) {
+ fprintf(fp, " |");
+ }
+ if ( *TheTab.colsep_v[i] == '2' ) {
+ fprintf(fp, " ||");
+ }
+ }
+
+ }
+ }
+ if (siderules) OutputString("|", fp, 1);
+
+ if (widths_v) free(widths_v);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (cell) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableCellStart(
+ Element_t *e,
+ FILE *fp
+)
+{
+ int n, i;
+ char buf[50], *at;
+
+ if (spaninfo[e->my_eorder] == SPAN_START) {
+ for (i=e->my_eorder+1,n=1; ; i++) {
+ if (spaninfo[i] == SPAN_CONT) n++;
+ else break;
+ }
+ sprintf(buf, "\\\\multicolumn{%d}{%sc%s}", n,
+ (siderules?"|":""), (siderules?"|":""));
+ OutputString(buf, fp, 1);
+ }
+#ifdef New
+ if ((at = FindAttValByName(e->parent, "ALIGN"))) {
+ /* no span, but user wants to change the alignment */
+ h_v = Split(wbuf, 0, S_ALVEC|S_STRDUP);
+ OutputString("\\\\multicolumn{1}{%sc%s}", n,
+ fp, 1);
+ }
+#endif
+
+ if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("{", fp, 1);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (cell) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableCellEnd(
+ Element_t *e,
+ FILE *fp
+)
+{
+ if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("} ", fp, 1);
+
+ /* do cell/col separators */
+ if (e->my_eorder < (TheTab.nc-1)) {
+ if (spaninfo[e->my_eorder] == SPAN_NOT ||
+ spaninfo[e->my_eorder+1] != SPAN_CONT)
+ OutputString("& ", fp, 1);
+ }
+}
+
+/* Look at model for spanning. If set, remember it for when doing the cells.
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableRowStart(
+ Element_t *e,
+ FILE *fp
+)
+{
+ check_for_spans(e);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableRowEnd(
+ Element_t *e,
+ FILE *fp
+)
+{
+ char *at;
+
+ /* check this row's attributes */
+ if ((at = FindAttValByName(e, "ROWSEP"))) {
+ if (at[0] == '1') OutputString("\\\\\\\\[2mm] \\\\hline ", fp, 1);
+ }
+ else if (rowsep) OutputString("\\\\\\\\ ", fp, 1);
+ else
+ OutputString("\\\\\\\\ ", fp, 1);
+
+}
+
+/*
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableTop(Element_t *e, FILE *fp)
+{
+ if (frametop) OutputString("\\\\hline", fp, 1);
+}
+
+void
+TexTableBottom(Element_t *e, FILE *fp)
+{
+ if (framebot) OutputString("\\\\hline", fp, 1);
+}
+
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ___________________________| |____________________________ */
+/* ___________________________| TBL STUFF |____________________________ */
+/* ___________________________| |____________________________ */
+/* ___________________________|_____________|____________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+
+
+
+/* TblTStart() -- start a table and do style information
+ *
+ * TO DO:
+ *
+ * do .TS
+ * find global rowsep and colsep
+ */
+
+
+void
+TblTStart(Element_t * ep,
+ FILE * fP)
+{
+ register char * cp;
+ register struct Element_t * ep2;
+
+
+
+ OutputString("^.TS^", fP, 1);
+
+ tblTGroupSeen = FALSE;
+ tblinBOFT = FALSE; /* within a boft? */
+ tblBOFTCount = 0; /* count of Blocks of Filled Text that
+ * we've created */
+
+ tblgcolsep = (cp = FindAttValByName(ep, "COLSEP")) && !strcmp(cp, "1");
+ tblgrowsep = (cp = FindAttValByName(ep, "ROWSEP")) && !strcmp(cp, "1");
+}
+
+/* TblTEnd() -- end a table and do any cleanup
+ *
+ * TO DO:
+ *
+ * do .TE
+ *
+ * deallocate format line info
+ */
+
+
+
+void
+TblTEnd(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblformat * ffp, * ffp2;
+
+
+ if ( tblBOFTCount > 31 ) {
+ fprintf(stderr, "# warning, line %d: created %d blocks of filled text in one table\n",
+ ep->lineno, tblBOFTCount);
+ fprintf(stderr, "#\t\t(31 is the limit in some systems)\n");
+ }
+
+ OutputString("^.TE^", fP, 1);
+
+ for ( ffp=formP; ffp; ffp=ffp2 ) {
+ ffp2 = ffp->next;
+ free(ffp); /* clear entire list */
+ }
+ formP = 0;
+}
+
+/* TblTTGroup() -- do body work (row format info)
+ *
+ * TO DO:
+ *
+ * set number of columns
+ *
+ * if this is the first TGroup of this table, do style info:
+ * a. alignment
+ * b. defaults: tab
+ * c. box vx allbox
+ *
+ * do format info:
+ * a. generate tableformat structure
+ * b. output it
+ *
+ * prepare structures for colspecs and spanspecs
+ *
+ */
+
+
+
+void
+TblTGroup(Element_t * ep,
+ FILE * fP)
+{
+ register int i, j, k;
+ register char * cp, * cp2;
+ register Element_t * ep2, ep3;
+ register struct tblcolspec * tcsp, * tcsp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ tblColSpec = 0; /* make sure they're clear */
+ tblSpanSpec = 0;
+
+ /* set the number of columns */
+
+ tblcols = atoi(FindAttValByName(ep, "COLS"));
+
+ /* do colspecs */
+
+ tblColSpec = tcsp = TblDoColSpec(0, ep, 0, TGroup);
+ /* do TGroup first -- it becomes the default */
+
+ for ( i=0, k=1; i < ep->necont; i++ ) {
+
+ if ( !strcmp(ep->econt[i]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep->econt[i], tblColSpec, TGroup);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "THEAD") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, THead);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "TFOOT") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TFoot);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "TBODY") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TBody);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+ }
+
+ /* do spanspecs */
+
+ tblSpanSpec = tssp = TblDoSpanSpec(ep, 0, TGroup);
+ /* do TGroup first -- it becomes the default */
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "SPANSPEC") ) {
+ tssp2 = TblDoSpanSpec(ep->econt[i], tblSpanSpec, TGroup);
+ tssp->next = tssp2; /* put into list */
+ tssp = tssp2;
+ }
+ }
+
+
+ /* if this is the first TGroup in this table, do style stuff */
+
+ if ( ! tblTGroupSeen ) {
+
+ OutputString("tab(\007)", fP, 1);
+
+ ep2 = ep->parent;
+ if ( ! (tblFrame = FindAttValByName(ep2, "FRAME")) )
+ tblFrame = "";
+
+ if ( !strcmp(tblFrame, "ALL") ) {
+ if ( tcsp->colsep && tcsp->rowsep )
+ OutputString(" allbox", fP, 1);
+ else
+ OutputString(" box", fP, 1);
+ }
+
+ if ( (cp = FindAttValByName(ep, "ALIGN")) &&
+ !strcmp(cp, "CENTER") ) {
+ OutputString(" center", fP, 1);
+ }
+
+ OutputString(";\n", fP, 1);
+
+ tblTGroupSeen = TRUE;
+ }
+
+
+ /* do format stuff -- step through all THead rows then all TBody
+ * rows. Build a list of tblformats that describe all of them.
+ * then output the resulting list.
+ */
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "THEAD") ) {
+ TblBuildFormat(ep->econt[i], &formP, THead);
+ /* add in those rows */
+ break;
+ }
+ }
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "TBODY") ) {
+ TblBuildFormat(ep->econt[i], &formP, TBody);
+ /* add in those rows */
+ break;
+ }
+ }
+
+ TblPrintFormat(fP, formP);
+
+ tblrow = 0; /* the current row within this format */
+}
+
+/* TblTGroupEnd() -- end a TGroup
+ *
+ * TO DO:
+ *
+ * deallocate colspecs and spanspecs
+ */
+
+
+void
+TblTGroupEnd(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblcolspec * tcsp, * tcsp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp2 ) {
+ tcsp2 = tcsp->next;
+ free(tcsp);
+ }
+ for ( tssp=tblSpanSpec; tssp; tssp=tssp2 ) {
+ tssp2 = tssp->next;
+ free(tssp);
+ }
+}
+
+/* TblTTFoot() -- do body foot work (row format info)
+ *
+ * TO DO:
+ *
+ * do format info:
+ * a. generate tableformat structure
+ * i. if it is only 1 line long and matches the
+ * prevailing format, just output rows.
+ * ii. else, output a .T& and the new format specs
+ */
+
+
+
+void
+TblTFoot(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblformat * ffp, * ffp2;
+ static struct tblformat * tfp, * tfp2;
+
+
+ TblBuildFormat(ep, &tfp, TFoot); /* gen format for the foot */
+
+ for ( tfp2=formP; tfp2 && tfp2->next; tfp2=tfp2->next )
+ ;
+
+ if ( tfp->next || !TblFormatMatch(tfp, tfp2) ) {
+
+ for ( ffp=formP; ffp; ffp=ffp2 ) {
+ ffp2 = ffp->next;
+ free(ffp); /* clear entire list */
+ }
+
+ formP = tfp; /* this becomes the prevailing format */
+
+ OutputString("^.T&^", fP, 1);
+ TblPrintFormat(fP, formP);
+ }
+
+ tblrow = 0; /* the current row within this format */
+}
+
+/* TblBuildFormat() -- build a format structure out of a set of
+ * rows and columns
+ *
+ */
+
+
+void
+TblBuildFormat(Element_t * ep, /* parent of rows.. */
+ struct tblformat ** fp, /* pointer to head of struct we're
+ * building */
+ tblsource source) /* type of record */
+{
+ register int i;
+ register struct tblformat * lfp; /* "current" format */
+ register struct tblformat * nfp; /* the next format */
+
+
+ for ( lfp= *fp; lfp && lfp->next; lfp=lfp->next )
+ ; /* find end of format list */
+
+ for ( i=0; i < ep->necont; i++ )
+ if ( !strcmp(ep->econt[i]->gi, "ROW") )
+ break; /* find where rows start */
+
+ for ( ; i < ep->necont; i++ ) {
+
+ nfp = TblBuild1Format(ep->econt[i], FALSE, source);
+ /* do one row */
+
+ if ( !lfp )
+ lfp = *fp = nfp; /* first one */
+ else
+ if ( TblFormatMatch(lfp, nfp) )
+ lfp->count++; /* matches */
+ else {
+ lfp->count = 1; /* only 1 so far */
+ lfp->next = nfp; /* new one */
+ lfp = nfp;
+ }
+ }
+}
+
+/* TblBuild1Format() -- build one row's worth of format information
+ *
+ */
+
+
+
+struct tblformat *
+TblBuild1Format(Element_t * rp, /* the row to deal with */
+ bool addinRowsep, /* insert rowsep into model? */
+ tblsource source) /* type type of row */
+{
+ register int i;
+ register bool allProp;
+ float totalProp;
+ register struct tblformat * tfp;
+ register Element_t * ep; /* entry pointer */
+
+
+ Calloc(1, tfp, struct tblformat);
+ tfp->cols = tblcols;
+ ep = (rp->necont) ? rp->econt[0] : 0; /* first entry */
+ allProp = TRUE;
+ totalProp = 0;
+
+ for ( i=1; i <= tblcols; i++ ) {
+ tfp->colformat[i] = TblGetAlign(i, ep, source);
+ strcpy(tfp->colwidth[i], TblGetWidth(i, ep, TRUE, source));
+ strcpy(tfp->colpwidth[i], TblGetWidth(i, ep, FALSE, source));
+ if ( allProp ) {
+ allProp = tfp->colpwidth[i][0];
+ totalProp += atof(tfp->colpwidth[i]);
+ }
+ strcpy(tfp->font[i], TblGetFont(i, ep, source));
+ tfp->colsep[i] = tblgcolsep || TblGetColSep(i, ep, source);
+ if ( addinRowsep )
+ tfp->rowsep[i] = tblgrowsep || TblGetRowSep(i, ep, source);
+ tfp->moreRows[i] = TblGetMoreRows(i, ep, source);
+
+ if ( (i < rp->necont) && TblColAdv(i, ep, tfp, source) ) {
+ ep = rp->econt[i];
+ }
+ }
+
+ /* turn proportional widths into real widths */
+
+ if ( allProp ) {
+ for ( i=1; i <= tblcols; i++ ) {
+ sprintf(tfp->colwidth[i], "%fi",
+ (atof(tfp->colpwidth[i]) / totalProp) * TEXTWIDTH);
+ }
+ }
+
+ return tfp;
+}
+
+/* TblGetAlign() -- get alignment spec for a entry
+ *
+ */
+
+
+char
+TblGetAlign(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register tblalign talign;
+
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ talign = tssp->align;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ talign = tcsp->align;
+ free(tcsp);
+ } else {
+ return 'l';
+ }
+
+ switch ( talign ) {
+ case Left: return 'l';
+ case Right: return 'r';
+ case Center: return 'c';
+ case Justify: return 'l';
+ case Char: return 'd';
+ case Span: return 's';
+ }
+}
+
+/* TblGetWidth() -- get width spec, if any, for a entry
+ *
+ */
+
+
+char *
+TblGetWidth(short col, /* column number */
+ Element_t * entry, /* the entry */
+ bool literal, /* literal (or proportional) */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ static char colWidth[10];
+
+
+ colWidth[0] = 0;
+
+ if ( entry &&
+ (tcsp = TblEntryColSpec(col, entry, source)) &&
+ tcsp->colwidth[0] ) {
+
+ if ( !strstr(tcsp->colwidth, "*") ) {
+ if ( literal )
+ strcpy(colWidth, tcsp->colwidth);
+ } else {
+ if ( ! literal )
+ strcpy(colWidth, tcsp->colwidth);
+ }
+ free(tcsp);
+ }
+
+ return colWidth;
+}
+
+/* TblGetFont() -- get font spec, if any, for a entry
+ *
+ */
+
+
+char *
+TblGetFont(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+
+
+ return "";
+}
+
+/* TblGetColSep() -- get column separater spec, if any, for a entry
+ *
+ */
+
+
+bool
+TblGetColSep(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register bool colsep;
+
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ colsep = tssp->colsep;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ colsep = tcsp->colsep;
+ free(tcsp);
+ } else
+ colsep = FALSE;
+
+ return colsep;
+}
+
+/* TblGetRowSep() -- get row separater spec, if any, for a entry
+ *
+ */
+
+
+bool
+TblGetRowSep(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register bool rowsep;
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ rowsep = tssp->rowsep;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ rowsep = tcsp->rowsep;
+ free(tcsp);
+ } else {
+ rowsep = FALSE;
+ }
+
+ return rowsep;
+}
+
+/* TblGetmoreRows() -- get moreRows value
+ *
+ */
+
+
+bool
+TblGetMoreRows(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register char * cp;
+
+
+ if ( cp = FindAttValByName(entry, "MOREROWS") )
+ return atoi(cp);
+ else
+ return 0;
+}
+
+/* TblColAdv() -- advance pointer to next entry, if appropriate
+ *
+ */
+
+
+bool
+TblColAdv(short col, /* the current column */
+ Element_t *ep, /* pointer to entry */
+ struct tblformat * tfp, /* pointer to prevailing format */
+ tblsource source) /* context */
+{
+ register bool bump;
+ register struct tblspanspec * tssp;
+
+
+ bump = TRUE;
+
+ if ( tssp = TblEntrySpanSpec(col, ep, source) ) {
+ bump = tssp->align != Span;
+ free(tssp);
+ }
+
+ return bump;
+}
+
+/* TblEntryColSpec() -- get a completely localized colspec for an entry
+ *
+ */
+
+
+struct tblcolspec *
+TblEntryColSpec(short num, /* column number */
+ Element_t * ep, /* entry */
+ tblsource source) /* context */
+{
+ register int i;
+ register bool throwAway;
+ register char * cp;
+ register struct tblcolspec * tcsp, * tcsp2;
+
+
+ tcsp = tcsp2 = 0;
+ throwAway = FALSE;
+
+ if ( (cp = FindAttValByName(ep, "COLNAME")) ) {
+ if ( ! (tcsp = TblFindColSpec(cp, source)) ) {
+ fprintf(stderr, "? can't find column name '%s'\n", cp);
+ }
+ }
+
+ if ( tcsp2 = TblFindColNum(num, source) ) {
+ tcsp = TblDoColSpec(num, ep, tcsp2, source);
+ throwAway = TRUE;
+ }
+
+ tcsp2 = TblDoColSpec(num, ep, tcsp, source);
+
+ if ( throwAway )
+ free(tcsp);
+
+ return tcsp2;
+}
+
+/* TblEntrySpanSpec() -- get a completely localized spanspec for an entry
+ *
+ */
+
+
+struct tblspanspec *
+TblEntrySpanSpec(short num, /* column number */
+ Element_t * ep, /* entry */
+ tblsource source) /* context */
+{
+ register char * cp, * cp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ tssp2 = 0;
+
+ if ( !(cp = FindAttValByName(ep, "SPANNAME")) ||
+ !(tssp2 = TblFindSpanSpec(cp, source)) ) {
+
+ if ( !FindAttValByName(ep, "NAMEST") )
+ return 0;
+ }
+
+ tssp = TblDoSpanSpec(ep, tssp2, source);
+
+ if ( tssp->start && tssp->end &&
+ (tssp->start->num < num) && (tssp->end->num >= num) ) {
+ tssp->align = Span;
+ }
+
+ return tssp;
+}
+
+/* TblFormatMatch() -- compare two format rows for consistency
+ *
+ */
+
+
+bool
+TblFormatMatch(struct tblformat * tf1, /* one row */
+ struct tblformat * tf2) /* the other */
+{
+ register int i;
+
+ if ( tf1->cols != tf2->cols ) {
+ return FALSE;
+ }
+
+ for ( i=0; i < tf1->cols; i++ ) {
+
+ if ( tf1->colformat[i] != tf2->colformat[i] ) {
+ return FALSE;
+ }
+ if ( strcmp(tf1->colwidth[i], tf2->colwidth[i]) ) {
+ return FALSE;
+ }
+ if ( strcmp(tf1->font[i], tf2->font[i]) ) {
+ return FALSE;
+ }
+ if ( tf1->colsep[i] != tf2->colsep[i] ) {
+ return FALSE;
+ }
+ if ( tf1->rowsep[i] != tf2->rowsep[i] ) {
+ return FALSE;
+ }
+ if ( tf1->moreRows[i] || tf2->moreRows[i] ) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* TblPrintFormat() -- print a tbl format structure
+ *
+ */
+
+
+void
+TblPrintFormat(FILE * fP, /* where to print */
+ struct tblformat * tfp) /* the structure */
+{
+ register int i;
+ register struct tblformat * tfp2, * tfp3;
+ static char buf[3] = "\000\000";
+
+
+ for ( tfp2=tfp, tfp3=0; tfp2; tfp2=tfp2->next ) {
+ for ( i=1; i <= tfp->cols; i++ ) {
+ if ( i > 1 )
+ OutputString(" ", fP, 1);
+ if ( tfp3 && tfp3->moreRows[i] )
+ OutputString("\\^", fP, 1);
+ else {
+ buf[0] = tfp2->colformat[i];
+ OutputString(buf, fP, 1);
+ }
+ if ( tfp2->colwidth[i][0] ) {
+ OutputString("w(", fP, 1);
+ OutputString(tfp2->colwidth[i], fP, 1);
+ OutputString(")", fP, 1);
+ }
+ if ( tfp2->font[i][0] )
+ OutputString(tfp2->font[i], fP, 1);
+ if ( tfp2->colsep[i] )
+ OutputString("|", fP, 1);
+ }
+ if ( ! tfp2->next )
+ OutputString(".", fP, 1);
+ OutputString("^", fP, 1);
+ tfp3 = tfp2;
+ }
+}
+
+/* TblTRowStart() -- start a row (not much to do)
+ *
+ * TO DO:
+ *
+ * nothing..
+ *
+ */
+
+
+
+void
+TblTRowStart(Element_t * ep,
+ FILE * fP)
+{
+
+ /* nothing to do */
+
+ tblrow++; /* except note that we're within a new row */
+
+}
+
+/* TblTRowEnd() -- end a row
+ *
+ * TO DO:
+ *
+ * output a row end character (newline)
+ * if the current row had a rowsep, then output a "fake" row
+ * with underlines in the proper place(s).
+ */
+
+
+
+void
+TblTRowEnd(Element_t * ep,
+ FILE * fP)
+{
+ register int i, k;
+ register tblsource source;
+ register bool startedRow, didSep;
+ register struct tblformat * rfp;
+
+
+ OutputString("^", fP, 1);
+
+ /* get the format for this row */
+
+ if ( !strcmp(ep->parent->gi, "TFoot") )
+ source = TFoot;
+ else
+ if ( !strcmp(ep->parent->gi, "THead") )
+ source = THead;
+ else
+ source = TBody;
+
+ rfp = TblBuild1Format(ep, TRUE, source);
+ startedRow = FALSE;
+ didSep = FALSE;
+
+ for ( i=1; i <= formP->cols; i++ ) {
+ if ( rfp->rowsep[i] ||
+ (didSep && (rfp->colformat[i] == 's')) ) {
+ if ( ! startedRow ) {
+ OutputString("^", fP, 1);
+ for ( k=1; k < i; k++ )
+ OutputString("\007", fP, 1);
+ startedRow = TRUE;
+ }
+ OutputString("_\007", fP, 1);
+ didSep = TRUE;
+ } else {
+ if ( startedRow )
+ OutputString("\007", fP, 1);
+ }
+ didSep = FALSE;
+ }
+ free(rfp); /* clear that row.. */
+
+ if ( startedRow )
+ OutputString("^", fP, 1);
+}
+
+/* TblTEntryStart() -- start an entry (block of filled text if
+ * appropriate)
+ *
+ * TO DO:
+ *
+ * if text length > BOFTTextThresh or there is PI,
+ * then output "T{\n", else do nothing
+ *
+ */
+
+
+
+void
+TblTCellStart(Element_t * ep,
+ FILE * fP)
+{
+ register int i;
+ register Element_t * ep2;
+ register bool sawPI;
+
+
+ for ( i=0, sawPI=FALSE; (i < ep->ncont) && !sawPI; i++ )
+ if ( ep->cont[i].type == '?' )
+ sawPI = TRUE;
+
+ if ( sawPI || (TblCountContent(ep) > BOFTTextThresh) ) {
+ tblBOFTCount++;
+ OutputString("T{^", fP, 1);
+ tblinBOFT = TRUE; /* within a boft now */
+ }
+}
+
+/* TblCountContent() -- count all content below the given element
+ *
+ *
+ */
+
+
+
+int
+TblCountContent(Element_t * ep) /* the element to look under */
+{
+ register int i, count;
+
+
+ count = 0;
+
+ for ( i=0; i < ep->ncont; i++ ) {
+ if ( ep->cont[i].type == '-' ) {
+ count += strlen(ep->cont[i].ch.data);
+ } else
+ if ( ep->cont[i].type == '(' ) {
+ count += TblCountContent(ep->cont[i].ch.elem);
+ }
+ }
+
+ return count;
+}
+
+/* TblTEntryEnd() -- end an entry
+ *
+ * TO DO:
+ *
+ * if within BOFT, output "T}"
+ * if not last entry, output tab character
+ *
+ */
+
+
+
+void
+TblTCellEnd(Element_t * ep,
+ FILE * fP)
+{
+ register Element_t * ep2;
+
+
+ if ( tblinBOFT ) {
+ OutputString("^T}", fP, 1);
+ tblinBOFT = FALSE; /* back out again */
+ }
+
+ for ( ep2=ep->next; ep2; ep2=ep2->next ) {
+ if ( !strcmp(ep2->gi, "ENTRY") || !strcmp(ep2->gi, "ENTRYTBL") ) {
+ OutputString("\007", fP, 1);
+ break;
+ }
+ if ( !strcmp(ep2->gi, "ROW") )
+ break;
+ }
+}
+
+/* TblDoColSpec() -- process one element to create a new colspec
+ *
+ *
+ */
+
+
+
+struct tblcolspec *
+TblDoColSpec(short number, /* this column number */
+ Element_t * ep, /* element containing colspec stuff */
+ struct tblcolspec * pcsp, /* prevailing colspec (with defaults) */
+ tblsource source) /* precedence level of the resulting spec */
+{
+ register char * cp;
+ register struct tblcolspec * tcsp;
+
+
+ Calloc(1, tcsp, struct tblcolspec);
+
+ if ( cp = FindAttValByName(ep, "COLNAME") )
+ strcpy(tcsp->name, cp);
+
+ tcsp->num = number;
+ tcsp->source = source;
+
+ if ( cp = FindAttValByName(ep, "ALIGN") ) {
+ if ( !strcmp(cp, "LEFT") ) tcsp->align = Left;
+ else if ( !strcmp(cp, "RIGHT") ) tcsp->align = Right;
+ else if ( !strcmp(cp, "CENTER") ) tcsp->align = Center;
+ else if ( !strcmp(cp, "JUSTIFY") ) tcsp->align = Justify;
+ else if ( !strcmp(cp, "CHAR") ) tcsp->align = Char;
+ } else
+ tcsp->align = ( pcsp ) ? pcsp->align : Left;
+
+ if ( cp = FindAttValByName(ep, "CHAR") )
+ tcsp->alignchar = cp[0];
+ else
+ tcsp->alignchar = ( pcsp ) ? pcsp->alignchar : 0;
+
+ if ( cp = FindAttValByName(ep, "CHAROFF") )
+ tcsp->aligncharoff = atoi(cp);
+ else
+ tcsp->aligncharoff = ( pcsp ) ? pcsp->aligncharoff : 0;
+
+ if ( cp = FindAttValByName(ep, "COLWIDTH") )
+ strcpy(tcsp->colwidth, cp);
+ else
+ strcpy(tcsp->colwidth, ( pcsp ) ? pcsp->colwidth : "");
+
+ if ( cp = FindAttValByName(ep, "COLSEP") )
+ tcsp->colsep = !strcmp(cp, "1");
+ else
+ tcsp->colsep = ( pcsp ) ? pcsp->colsep : FALSE;
+
+ if ( cp = FindAttValByName(ep, "ROWSEP") )
+ tcsp->rowsep = !strcmp(cp, "1");
+ else
+ tcsp->rowsep = ( pcsp ) ? pcsp->rowsep : FALSE;
+
+ return tcsp;
+}
+
+/* TblDoSpanSpec() -- process one element to create a new spanspec
+ *
+ * Note that there's a hack inside here... NameSt and NameEnd are
+ * supposed to point at colnames, but if no colname is found, this
+ * code will look for a colnum by the same value.
+ */
+
+
+
+struct tblspanspec *
+TblDoSpanSpec(Element_t * ep, /* element containing spanspec stuff */
+ struct tblspanspec * pssp, /* prevailing spanspec (with defaults) */
+ tblsource source) /* precedence level of the resulting spec */
+{
+ register char * cp;
+ register struct tblspanspec * tssp;
+ register struct tblcolspec * tcsp;
+
+
+ Calloc(1, tssp, struct tblspanspec);
+
+ if ( cp = FindAttValByName(ep, "SPANNAME") ) strcpy(tssp->name, cp);
+ tssp->source = source;
+
+ if ( cp = FindAttValByName(ep, "NAMEST") ) {
+ if ( (tcsp = TblFindColSpec(cp, source)) ||
+ (tcsp = TblFindColNum(atoi(cp), source)) ) {
+ tssp->start = tcsp;
+ } else {
+ fprintf(stderr, "? spanspec namest points to unknown column '%s'\n", cp);
+ tssp->start = 0;
+ }
+ } else {
+ if ( pssp && pssp->start ) {
+ tssp->start = pssp->start;
+ }
+ }
+
+ if ( cp = FindAttValByName(ep, "NAMEEND") ) {
+ if ( (tcsp = TblFindColSpec(cp, source)) ||
+ (tcsp = TblFindColNum(atoi(cp), source)) ) {
+ tssp->end = tcsp;
+ } else {
+ fprintf(stderr, "? spanspec nameend points to unknown column '%s'\n", cp);
+ tssp->end = 0;
+ }
+ } else {
+ if ( pssp && pssp->end ) {
+ tssp->end = pssp->end;
+ }
+ }
+
+ if ( cp = FindAttValByName(ep, "ALIGN") ) {
+ if ( !strcmp(cp, "LEFT") ) tssp->align = Left;
+ else if ( !strcmp(cp, "RIGHT") ) tssp->align = Right;
+ else if ( !strcmp(cp, "CENTER") ) tssp->align = Center;
+ else if ( !strcmp(cp, "JUSTIFY") ) tssp->align = Justify;
+ else if ( !strcmp(cp, "CHAR") ) tssp->align = Char;
+ } else {
+ if ( pssp )
+ tssp->align = pssp->align;
+ }
+
+ if ( cp = FindAttValByName(ep, "CHAR") )
+ tssp->alignchar = cp[0];
+ else {
+ if ( pssp )
+ tssp->alignchar = pssp->alignchar;
+ }
+ if ( cp = FindAttValByName(ep, "CHAROFF") )
+ tssp->aligncharoff = atoi(cp);
+ else {
+ if ( pssp )
+ tssp->alignchar = pssp->alignchar;
+ }
+
+ if ( cp = FindAttValByName(ep, "COLSEP") )
+ tssp->colsep = !strcmp(cp, "1");
+ else {
+ if ( pssp )
+ tssp->colsep = pssp->colsep;
+ }
+ if ( cp = FindAttValByName(ep, "ROWSEP") )
+ tssp->rowsep = !strcmp(cp, "1");
+ else {
+ if ( pssp )
+ tssp->rowsep = pssp->rowsep;
+ }
+
+ return tssp;
+}
+
+/* TblFindColSpec() -- find a table colspec by name (colname)
+ *
+ */
+
+
+
+struct tblcolspec *
+TblFindColSpec(char * name, /* the name we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblcolspec * tcsp;
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == source) && !strcmp(tcsp->name, name) )
+ return tcsp;
+ }
+
+ /* else, try to find one from a TGroup.. */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == TGroup) && !strcmp(tcsp->name, name) )
+ return tcsp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
+
+/* TblFindColNum() -- find a table colspec by number
+ *
+ */
+
+
+
+struct tblcolspec *
+TblFindColNum(short number, /* the number we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblcolspec * tcsp;
+
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->num == number) &&
+ ((tcsp->source == source) ||
+ ((source == THead) && (tcsp->source == TGroup))) )
+ return tcsp;
+ }
+
+ /* else, try to find one from a TGroup.. */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == TGroup) && (tcsp->num == number) )
+ return tcsp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
+
+/* TblFindSpanSpec() -- find a table spanspec by name (spanname)
+ *
+ */
+
+
+
+struct tblspanspec *
+TblFindSpanSpec(char * name, /* the name we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblspanspec * tssp;
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tssp=tblSpanSpec; tssp; tssp=tssp->next ) {
+ if ( !strcmp(tssp->name, name) &&
+ ((tssp->source == source) ||
+ ((source == THead) && (tssp->source == TGroup))) )
+ return tssp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
diff --git a/usr.bin/sgmls/instant/traninit.c b/usr.bin/sgmls/instant/traninit.c
new file mode 100644
index 0000000..d3df959
--- /dev/null
+++ b/usr.bin/sgmls/instant/traninit.c
@@ -0,0 +1,577 @@
+/*
+ * 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.
+ */
+/*
+ * 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 contains the initialization routines for translation module.
+ * They mostly deal with reading data files (translation specs, SDATA
+ * mappings, character mappings).
+ *
+ * Entry points:
+ * ReadTransSpec(transfile) read/store translation spec from file
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/local/home/jfieber/src/cvsroot/nsgmlfmt/traninit.c,v 1.1.1.1 1996/01/16 05:14:10 jfieber 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 <regexp.h>
+
+#include "general.h"
+#include "translate.h"
+
+#include "sgmls.h"
+#include "config.h"
+#include "std.h"
+
+#ifndef TRUE
+#define TRUE (1 == 1)
+#endif
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/* forward references */
+void RememberTransSpec(Trans_t *, int);
+static void do_data(char *gi, struct sgmls_data *v, int n);
+static void build_ts(char *gi, char* cp);
+void AddCharMap(const char *from, const char* to);
+void AddSDATA(const char *from, const char *to);
+
+/* ______________________________________________________________________ */
+/* Read the translation specs from the input file, storing in memory.
+ * Arguments:
+ * Name of translation spec file.
+ */
+
+static Trans_t T;
+
+
+static
+void input_error(num, str, lineno)
+ int num;
+ char *str;
+ unsigned long lineno;
+{
+ fprintf(stderr, "Error at input line %lu: %s\n", lineno, str);
+}
+
+void
+ReadTransSpec(
+ char *transfile
+)
+{
+ FILE *fp;
+ struct sgmls *sp;
+ struct sgmls_event e;
+ char gi[LINESIZE];
+ char buf[LINESIZE];
+ char buf2[LINESIZE];
+ char *command;
+ char *sgmls = "sgmls ";
+ char maptype = '\0';
+
+ (void)sgmls_set_errhandler(input_error);
+ transfile = FilePath(transfile);
+ if (!transfile)
+ {
+ fprintf(stderr, "Error: Could not locate specified transfile\n");
+ exit(1);
+ }
+
+ /* XXX this is a quick, gross hack. Should write a parse() function. */
+ Malloc(strlen(sgmls) + strlen(transfile) + 2, command, char);
+ sprintf(command, "%s %s", sgmls, transfile);
+ fp = popen(command, "r");
+
+ sp = sgmls_create(fp);
+ while (sgmls_next(sp, &e))
+ switch (e.type) {
+ case SGMLS_EVENT_DATA:
+ do_data(gi, e.u.data.v, e.u.data.n);
+ break;
+ case SGMLS_EVENT_ENTITY:
+ fprintf(stderr, "Hm... got an entity\n");
+ break;
+ case SGMLS_EVENT_PI:
+ break;
+ case SGMLS_EVENT_START:
+ if (strncmp("RULE", e.u.start.gi, 4) == 0) {
+ /* A new transpec, so clear the data structure
+ * and look for an ID attribute.
+ */
+ struct sgmls_attribute *attr = e.u.start.attributes;
+ memset(&T, 0, sizeof T);
+ while (attr) {
+ if (attr->type == SGMLS_ATTR_CDATA
+ && strncmp("ID", attr->name, 2) == 0) {
+ strncpy(buf, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE));
+ buf[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ T.my_id = atoi(buf);
+ }
+ attr = attr->next;
+ }
+ }
+ else if (strncmp("CMAP", e.u.start.gi, 4) == 0)
+ maptype = 'c';
+ else if (strncmp("SMAP", e.u.start.gi, 4) == 0)
+ maptype = 's';
+ else if (strncmp("MAP", e.u.start.gi, 3) == 0) {
+ struct sgmls_attribute *attr = e.u.start.attributes;
+ char *from = 0;
+ char *to = 0;
+
+ while (attr) {
+ if (attr->value.data.v && strncmp("FROM", attr->name, 4) == 0) {
+ strncpy(buf, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE - 1));
+ buf[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ }
+ if (attr->value.data.v && strncmp("TO", attr->name, 2) == 0) {
+ strncpy(buf2, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE - 1));
+ buf2[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ }
+ attr = attr->next;
+ }
+ if (maptype == 'c')
+ AddCharMap(buf, buf2);
+ else if (maptype == 's')
+ AddSDATA(buf, buf2);
+ else
+ fprintf(stderr, "Unknown map type!\n");
+ }
+ else {
+ strncpy(gi, e.u.start.gi, 512);
+ sgmls_free_attributes(e.u.start.attributes);
+ }
+ break;
+ case SGMLS_EVENT_END:
+ if (strncmp("RULE", e.u.start.gi, 4) == 0)
+ RememberTransSpec(&T, e.lineno);
+ break;
+ case SGMLS_EVENT_SUBSTART:
+ break;
+ case SGMLS_EVENT_SUBEND:
+ break;
+ case SGMLS_EVENT_APPINFO:
+ break;
+ case SGMLS_EVENT_CONFORMING:
+ break;
+ default:
+ abort();
+ }
+ sgmls_free(sp);
+ pclose(fp);
+ free(command);
+}
+
+
+static void do_data(char *gi, struct sgmls_data *v, int n)
+{
+ int i;
+ char *cp;
+ static char *buf = 0;
+ static int buf_size = 0;
+ int buf_pos = 0;
+
+
+ /* figure out how much space this element will really
+ take, inculding expanded sdata entities. */
+
+ if (!buf)
+ {
+ buf_size = 1024;
+ Malloc(buf_size, buf, char);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ char *s;
+ int len;
+
+ /* Mark the current position. If this is SDATA
+ we will have to return here. */
+ int tmp_buf_pos = buf_pos;
+
+ /* Make sure the buffer is big enough. */
+ if (buf_size - buf_pos <= v[i].len)
+ {
+ buf_size += v[i].len * (n - i);
+ Realloc(buf_size, buf, char);
+ }
+
+ s = v[i].s;
+ len = v[i].len;
+ for (; len > 0; len--, s++)
+ {
+ if (*s != RSCHAR) {
+ if (*s == RECHAR)
+ buf[buf_pos] = '\n';
+ else
+ buf[buf_pos] = *s;
+ buf_pos++;
+ }
+ }
+ if (v[i].is_sdata)
+ {
+ char *p;
+ buf[buf_pos] = '\0';
+ p = LookupSDATA(buf + tmp_buf_pos);
+ if (p)
+ {
+ if (buf_size - tmp_buf_pos <= strlen(p))
+ {
+ buf_size += strlen(p) * (n - i);
+ Realloc(buf_size, buf, char);
+ }
+ strcpy(buf + tmp_buf_pos, p);
+ buf_pos = tmp_buf_pos + strlen(p);
+ }
+ }
+ }
+
+ /* Clean up the trailing end of the data. */
+ buf[buf_pos] = '\0';
+ buf_pos--;
+ while (buf_pos > 0 && isspace(buf[buf_pos]) && buf[buf_pos] != '\n')
+ buf_pos--;
+ if (buf[buf_pos] == '\n')
+ buf[buf_pos] = '\0';
+
+ /* Skip over whitespace at the beginning of the data. */
+ cp = buf;
+ while (*cp && isspace(*cp))
+ cp++;
+ build_ts(gi, cp);
+}
+
+/* ______________________________________________________________________ */
+/* Set a transpec parameter
+ * Arguments:
+ * gi - the parameter to set
+ * cp - the value of the parameter
+ */
+static void build_ts(char *gi, char* cp)
+{
+ if (strcmp("GI", gi) == 0)
+ {
+ char *cp2;
+ /* if we are folding the case of GIs, make all upper (unless
+ it's an internal pseudo-GI name, which starts with '_') */
+ if (fold_case && cp[0] != '_' && cp[0] != '#')
+ {
+ for (cp2=cp; *cp2; cp2++)
+ if (islower(*cp2)) *cp2 = toupper(*cp2);
+ }
+ T.gi = AddElemName(cp);
+ }
+ else if (strcmp("START", gi) == 0)
+ T.starttext = strdup(cp);
+ else if (strcmp("END", gi) == 0)
+ T.endtext = strdup(cp);
+ else if (strcmp("RELATION", gi) == 0)
+ {
+ if (!T.relations)
+ T.relations = NewMap(IMS_relations);
+ SetMapping(T.relations, cp);
+ }
+ else if (strcmp("REPLACE", gi) == 0)
+ T.replace = strdup(cp);
+ else if (strcmp("ATTVAL", gi) == 0)
+ {
+ if (!T.nattpairs)
+ {
+ Malloc(1, T.attpair, AttPair_t);
+ }
+ else
+ Realloc((T.nattpairs+1), T.attpair, AttPair_t);
+ /* we'll split name/value pairs later */
+ T.attpair[T.nattpairs].name = strdup(cp);
+ T.nattpairs++;
+ }
+ else if (strcmp("CONTEXT", gi) == 0)
+ T.context = strdup(cp);
+ else if (strcmp("MESSAGE", gi) == 0)
+ T.message = strdup(cp);
+ else if (strcmp("DO", gi) == 0)
+ T.use_id = atoi(cp);
+ else if (strcmp("CONTENT", gi) == 0)
+ T.content = strdup(cp);
+ else if (strcmp("PATTSET", gi) == 0)
+ T.pattrset = strdup(cp);
+ else if (strcmp("VERBATIM", gi) == 0)
+ T.verbatim = TRUE;
+ else if (strcmp("IGNORE", gi) == 0)
+ {
+ if (!strcmp(cp, "all"))
+ T.ignore = IGN_ALL;
+ else if (!strcmp(cp, "data"))
+ T.ignore = IGN_DATA;
+ else if (!strcmp(cp, "children"))
+ T.ignore = IGN_CHILDREN;
+ else
+ fprintf(stderr, "Bad 'Ignore:' arg in transpec %s: %s\n",
+ gi, cp);
+ }
+ else if (strcmp("VARVAL", gi) == 0)
+ {
+ char **tok;
+ int i = 2;
+ tok = Split(cp, &i, S_STRDUP);
+ T.var_name = tok[0];
+ T.var_value = tok[1];
+ }
+ else if (strcmp("VARREVAL", gi) == 0)
+ {
+ char buf[1000];
+ char **tok;
+ int i = 2;
+ tok = Split(cp, &i, S_STRDUP);
+ T.var_RE_name = tok[0];
+ ExpandVariables(tok[1], buf, 0);
+ if (!(T.var_RE_value=regcomp(buf))) {
+ fprintf(stderr, "Regex error in VarREValue Content: %s\n",
+ tok[1]);
+ }
+ }
+ else if (strcmp("SET", gi) == 0)
+ {
+ if (!T.set_var)
+ T.set_var = NewMap(IMS_setvar);
+ SetMapping(T.set_var, cp);
+ }
+ else if (strcmp("INCR", gi) == 0)
+ {
+ if (!T.incr_var)
+ T.incr_var = NewMap(IMS_incvar);
+ SetMapping(T.incr_var, cp);
+ }
+ else if (strcmp("NTHCHILD", gi) == 0)
+ T.nth_child = atoi(cp);
+ else if (strcmp("VAR", gi) == 0)
+ SetMapping(Variables, cp);
+ else if (strcmp("QUIT", gi) == 0)
+ T.quit = strdup(cp);
+ else
+ fprintf(stderr, "Unknown translation spec (skipping it): %s\n", gi);
+
+}
+
+
+/* ______________________________________________________________________ */
+/* Store translation spec 't' in memory.
+ * Arguments:
+ * Pointer to translation spec to remember.
+ * Line number where translation spec ends.
+ */
+void
+RememberTransSpec(
+ Trans_t *t,
+ int lineno
+)
+{
+ char *cp;
+ int i, do_regex;
+ static Trans_t *last_t;
+ char buf[1000];
+
+ /* If context testing, check some details and set things up for later. */
+ if (t->context) {
+ /* See if the context specified is a regular expression.
+ * If so, compile the reg expr. It is assumed to be a regex if
+ * it contains a character other than what's allowed for GIs in the
+ * OSF sgml declaration (alphas, nums, '-', and '.').
+ */
+ for (do_regex=0,cp=t->context; *cp; cp++) {
+ if (!isalnum(*cp) && *cp != '-' && *cp != '.' && *cp != ' ') {
+ do_regex = 1;
+ break;
+ }
+ }
+
+ if (do_regex) {
+ t->depth = MAX_DEPTH;
+ if (!(t->context_re=regcomp(t->context))) {
+ fprintf(stderr, "Regex error in Context: %s\n", t->context);
+ }
+ }
+ else {
+ /* If there's only one item in context, it's the parent. Treat
+ * it specially, since it's faster to just check parent gi.
+ */
+ cp = t->context;
+ if (!strchr(cp, ' ')) {
+ t->parent = t->context;
+ t->context = NULL;
+ }
+ else {
+ /* Figure out depth of context string */
+ t->depth = 0;
+ while (*cp) {
+ if (*cp) t->depth++;
+ while (*cp && !IsWhite(*cp)) cp++; /* find end of gi */
+ while (*cp && IsWhite(*cp)) cp++; /* skip space */
+ }
+ }
+ }
+ }
+
+ /* Compile regular expressions for each attribute */
+ for (i=0; i<t->nattpairs; i++) {
+ /* Initially, name points to "name value". Split them... */
+ cp = t->attpair[i].name;
+ while (*cp && !IsWhite(*cp)) cp++; /* point past end of name */
+ if (*cp) { /* value found */
+ *cp++ = EOS; /* terminate name */
+ while (*cp && IsWhite(*cp)) cp++; /* point to value */
+ ExpandVariables(cp, buf, 0); /* expand any variables */
+ t->attpair[i].val = strdup(buf);
+ }
+ else { /* value not found */
+ t->attpair[i].val = ".";
+ }
+ if (!(t->attpair[i].rex=regcomp(t->attpair[i].val))) {
+ fprintf(stderr, "Regex error in AttValue: %s %s\n",
+ t->attpair[i].name, t->attpair[i].val);
+ }
+ }
+
+ /* Compile regular expression for content */
+ t->content_re = 0;
+ if (t->content) {
+ ExpandVariables(t->content, buf, 0);
+ if (!(t->content_re=regcomp(buf)))
+ fprintf(stderr, "Regex error in Content: %s\n",
+ t->content);
+ }
+
+ /* If multiple GIs, break up into a vector, then remember it. We either
+ * sture the individual, or the list - not both. */
+ if (t->gi && strchr(t->gi, ' ')) {
+ t->gilist = Split(t->gi, 0, S_ALVEC);
+ t->gi = NULL;
+ }
+
+ /* Now, store structure in linked list. */
+ if (!TrSpecs) {
+ Malloc(1, TrSpecs, Trans_t);
+ last_t = TrSpecs;
+ }
+ else {
+ Malloc(1, last_t->next, Trans_t);
+ last_t = last_t->next;
+ }
+ *last_t = *t;
+}
+
+
+/* ______________________________________________________________________ */
+/* Add an entry to the character mapping table, allocating or
+ * expanding the table if necessary.
+ * Arguments:
+ * Character to map
+ * String to map the character to
+ * A 'c' or an 's' for character or sdata map
+ */
+
+void
+AddCharMap(
+ const char *from,
+ const char* to
+)
+{
+ static int n_alloc = 0;
+
+ if (from && to) {
+ if (nCharMap >= n_alloc) {
+ n_alloc += 32;
+ if (!CharMap) {
+ Malloc(n_alloc, CharMap, Mapping_t);
+ }
+ else {
+ Realloc(n_alloc, CharMap, Mapping_t);
+ }
+ }
+ CharMap[nCharMap].name = strdup(from);
+ CharMap[nCharMap].sval = strdup(to);
+ nCharMap++;
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Add an entry to the SDATA mapping table.
+ * Arguments:
+ * String to map
+ * String to map to
+ */
+
+void
+AddSDATA(
+ const char *from,
+ const char *to
+)
+{
+ if (from && to) {
+ if (!SDATAmap)
+ SDATAmap = NewMap(IMS_sdata);
+ SetMappingNV(SDATAmap, from, to);
+ }
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/translate.c b/usr.bin/sgmls/instant/translate.c
new file mode 100644
index 0000000..cc96b25
--- /dev/null
+++ b/usr.bin/sgmls/instant/translate.c
@@ -0,0 +1,881 @@
+/*
+ * 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.
+ */
+/*
+ * 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, fp)
+ * ExpandVariables(in, out, e)
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/translate.c,v 1.11 1996/06/15 22:49:00 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 <regexp.h>
+
+#include "general.h"
+#define STORAGE
+#include "translate.h"
+
+static Trans_t NullTrans; /* an empty one */
+
+/* forward references */
+void ProcesOutputSpec(char *, Element_t *, FILE *, int);
+static void WasProcessed(Element_t *);
+
+/* ______________________________________________________________________ */
+/* Translate the subtree starting at 'e'. Output goes to 'fp'.
+ * This is the entry point for translating an instance.
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ */
+
+void
+DoTranslate(
+ Element_t *e,
+ FILE *fp
+)
+{
+ Trans_t *t, *tn;
+
+ /* 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;
+ 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 {
+ fprintf(stderr, "warning: ${+current} skipped element content\n");
+ }
+ }
+
+ } 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) {
+ /* 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 (! 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 (!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 || !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 (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,
+ int verbatim
+)
+{
+ if (!fp) return;
+ OutputString(data, 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[500];
+
+ /* 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);
+ }
+
+ /* 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 */
+ 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->verbatim);
+ }
+ 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;
+}
+
+/* ______________________________________________________________________ */
+/* 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);
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/translate.h b/usr.bin/sgmls/instant/translate.h
new file mode 100644
index 0000000..777d591
--- /dev/null
+++ b/usr.bin/sgmls/instant/translate.h
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ *
+ * These are data definitions for the "translating" portion of the program.
+ *
+ * ________________________________________________________________________
+ */
+
+#ifdef STORAGE
+#ifndef lint
+static char *tr_h_RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/translate.h,v 1.3 1996/06/02 21:47:32 fld Exp $";
+#endif
+#endif
+
+#define L_CURLY '{'
+#define R_CURLY '}'
+
+/* things to ignore when processing an element */
+#define IGN_NONE 0
+#define IGN_ALL 1
+#define IGN_DATA 2
+#define IGN_CHILDREN 3
+
+/* for CheckRelation() */
+typedef enum { RA_Current, RA_Related } RelAction_t;
+
+typedef struct {
+ char *name; /* attribute name string */
+ char *val; /* attribute value string */
+ regexp *rex; /* attribute value reg expr (compiled) */
+} AttPair_t;
+
+typedef struct _Trans {
+ /* criteria */
+ char *gi; /* element name of tag under consideration */
+ char **gilist; /* list of element names (multiple gi's) */
+ char *context; /* context in tree - looking depth levels up */
+ regexp *context_re; /* tree heirarchy looking depth levels up */
+ int depth; /* number of levels to look up the tree */
+ AttPair_t *attpair; /* attr name-value pairs */
+ int nattpairs; /* number of name-value pairs */
+ char *parent; /* GI has this element as parent */
+ int nth_child; /* GI is Nth child of this of parent element */
+ char *content; /* element has this string in content */
+ regexp *content_re; /* content reg expr (compiled) */
+ char *pattrset; /* is this attr set (any value) in parent? */
+ char *var_name; /* variable name */
+ char *var_value; /* variable value */
+ char *var_RE_name; /* variable name (for VarREValue) */
+ regexp *var_RE_value; /* variable value (compiled, for VarREValue) */
+ Map_t *relations; /* various relations to check */
+
+ /* actions */
+ char *starttext; /* string to output at the start tag */
+ char *endtext; /* string to output at the end tag */
+ char *replace; /* string to replace this subtree with */
+ char *message; /* message for stderr, if element encountered */
+ int ignore; /* flag - ignore content or data of element? */
+ int verbatim; /* flag - pass content verbatim or do cmap? */
+ char *var_reset;
+ char *increment; /* increment these variables */
+ Map_t *set_var; /* set these variables */
+ Map_t *incr_var; /* increment these variables */
+ char *quit; /* print message and exit */
+
+ /* pointers and bookkeeping */
+ int my_id; /* unique (hopefully) ID of this transpec */
+ int use_id; /* use transpec whose ID is this */
+ struct _Trans *use_trans; /* pointer to other transpec */
+ struct _Trans *next; /* linked list */
+ int lineno; /* line number of end of transpec */
+} Trans_t;
+
+#ifdef def
+#undef def
+#endif
+#ifdef STORAGE
+# define def
+#else
+# define def extern
+#endif
+
+def Trans_t *TrSpecs;
+def Mapping_t *CharMap;
+def int nCharMap;
+
+/* prototypes for things defined in translate.c */
+int CheckRelation(Element_t *, char *, char *, char *, FILE*, RelAction_t);
+Trans_t *FindTrans(Element_t *, int);
+Trans_t *FindTransByName(char *);
+Trans_t *FindTransByID(int);
+void PrepTranspecs(Element_t *);
+void ProcessOneSpec(char *, Element_t *, FILE *, int);
+void TransElement(Element_t *, FILE *, Trans_t *);
+void TranByAction(Element_t *, int, FILE *);
+void TranTByAction(Element_t *, char *, FILE *);
+
+/* prototypes for things defined in tranvar.c */
+void ExpandSpecialVar(char *, Element_t *, FILE *, int);
+
+/* prototypes for things defined in tables.c */
+void OSFtable(Element_t *, FILE *, char **, int);
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/transpec.5 b/usr.bin/sgmls/instant/transpec.5
new file mode 100644
index 0000000..3226bbf
--- /dev/null
+++ b/usr.bin/sgmls/instant/transpec.5
@@ -0,0 +1,526 @@
+...\"
+...\"
+...\" 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) 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.
+...\"
+...\" Translated with /usr/local/lib/tpt/ref-man.ts by fld on cord, Wed 07 Feb 1996, 22:00
+.TH "\fBtranspec\fP" "file format"
+.SH "Name"
+\fBtranspec\fP - translation specification for \fBinstant\fP
+.SH "Synopsis"
+.na
+.PP
+\fBfile.ts\fP
+.ad
+.SH "Description"
+.PP
+The \fBtranspec\fP file is used by the \fBinstant\fP program to translate an SGML document instance to a format suitable for a formatting application.
+The convention is to name the file with the suffix \fB.ts\fP.
+.PP
+A \fBtranspec\fP file is composed of a number of individual translation specs.
+Each translation spec (transpec) is made up of a number of fields, one per line.
+Translation specs are separated by a line with a leading dash. Text after the dash is ignored.
+Fields are composed of two parts, a name and a value, separated by a colon.
+The colon must immediately follow the name, and any amount of whitespace (blanks and tabs) may be present between the colon and value.
+Values should not be quoted, and you should be careful of trailing spaces.
+(Trailing space will be considered part of the value.)
+Quotes, if they appear, will be considered part of the value of the fields.
+Lines that begin with whitespace (blanks and tabs) are a continuation of the previous line; the leading space is ignored.
+These characteristics are very similar to those of e-mail headers.
+Lines beginning with a \fB#\fP (number sign) are comments and blank lines are ignored.
+.SH "Field Descriptions"
+.PP
+Some fields are for identifying criteria that determines if a particular spec matches an element in the instance.
+Others specify what action is to take place when a match happens, such as sending text to the output stream.
+.SS "Criteria fields"
+.PP
+Criteria fields restrict the conditions under which a single translation spec will apply.
+If each field specified in a particular transpec matches an element under consideration in the document instance,
+then that translation spec is said to match. The appropriate actions, as specified in that spec, are then taken.
+The program, \fBinstant\fP, searches the list of transpecs in the order given in the file.
+Therefore, the more restrictive specs (those with more criteria) should appear before less restrictive ones.
+.PP
+For quick reference, this is a brief summary of the possible criteria fields for translation. A complete discussion of each follows.
+.P
+.TS
+tab(@);
+l l l.
+\fBField Label\fR@\fBField Value\fR@\fBDescription\fR
+GI@gi ...@name of this GI
+AttValue@attname reg-expr@current element has attribute with value
+Content@reg-expr@is reg-expr in char content>
+Context@context@element context, up the tree
+NthChild@number@current element is Nth child of its parent
+PAttSet@attname (val)@parent has this attribute set (optional to value val)
+Relation@relationship gi@gi has relationship to current element
+VarREValue@var REvalue@variable is set to regular expression value
+VarValue@var value@variable is set to value
+.TE
+'br\" labeled list
+.IP "\fBGI:\fP \fIgi\fP [...]"
+\fIgi\fP is the name of the generic identifier, or element name, to consider.
+More than one GI may appear in this field.
+.IP "\fBAttValue:\fP \fIattname\fP \fIregular-expression\fP"
+This is an attribute name-value pair, where \fIattname\fP is an attribute if the GI.
+The \fIregular-expression\fP is of the form accepted by the unix program \fBegrep\fP.
+This pair is compared to the corresponding attribute name-value pairs of the GI under consideration.
+To simply test if an attribute us set, use \fB.\fP (a dot) for \fIregular-expression\fP.
+There may be more than one of these lines for each transpec.
+.IP "\fBContent:\fP \fIregular-expression\fP"
+This specifies that the character content of GI contains a string matching the \fIregular-expression\fP.
+.IP "\fBContext:\fP \fIcontext\fP"
+This specifies the \fIcontext\fP in which to apply this translation spec.
+It is either a list of generic identifiers or a regular expression describing a list of generic identifiers, looking up the hierarchy.
+The first is the parent of the GI.
+.IP "\fBNthChild:\fP \fInumber\fP"
+This specifies that the GI is the \fInumber\fPth child element of its parent.
+Children are numbered starting with \fB1\fP.
+Negative numbers may be used to indicate order counting backwards.
+For example, -1 denotes the last child.
+.IP "\fBPAttSet:\fP \fIattname\fP"
+This specifies that the parent has this attribute, \fIattname\fP, set to any value (not IMPLIED). A value to match may optionally
+be specified after attname.
+.IP "\fBRelation:\fP \fIrelationship\fP \fIgi\fP"
+This specifies that the current element has the \fIrelationship\fP to the named \fIgi\fP.
+The acceptable relationships are: \fBancestor\fP (anywhere up the tree), \fBchild\fP (immediate child),
+\fBdescendant\fP (anywhere down the tree), \fBparent\fP (immediate ancestor), \fBsibling\fP (share same parent element),
+\fBsibling+\fP (any later sibling), \fBsibling+1\fP (the immediately following sibling), \fBsibling-\fP (any earlier sibling),
+\fBsibling-1\fP (the immediately following sibling).
+.IP "\fBVarREValue:\fP \fIvarname\fP \fIREvalue\fP"
+This specifies that the global variable \fIvarname\fP has the value \fIREvalue\fP,
+where \fIREvalue\fP is a regular expression
+(see the \fBVarValue\fP statement).
+.IP "\fBVarValue:\fP \fIvarname\fP \fIvalue\fP"
+This specifies that the global variable \fIvarname\fP has the (literal)
+value \fIvalue\fP (see the \fBVarREValue\fP statement).
+'br\" labeled list end
+.PP
+There are two special GIs.
+If specified, \fB_Start\fP and \fB_End\fP are processed as if they were GIs in the instance at the start and end of the translation, respectively.
+Their criteria are never checked. Only their actions are performed.
+.SS "Action fields"
+.PP
+For quick reference, this is a brief summary of the action fields for translation. They are only performed if all the criteria are satisfied.
+A complete discussion of each follows.
+.P
+.TS
+tab(@);
+l l l.
+\fBField Label\fR@\fBField Value\fR@\fBDescription\fR
+Action@spec-id@use transpec whose spec ID is `spec-id'
+EndText@text@text for end of element
+Increment@name@increment variable `name'
+Ignore@key@flag for ignoring element's children and/or data
+Message@text@text to send to stderr
+Quit@text@print text and quit program
+Replace@text@replace this subtree with text
+Set@name value@set variable \fIname\fP to \fIvalue\fP
+SpecID@spec-id@unique Spec ID (int) of this spec
+StartText@text@text for start of element
+.TE
+'br\" labeled list
+.IP "\fBAction:\fP \fIspec-id\fP"
+Use the actions of the spec identified by the \fBSpecID\fP with matching identifier \fIspec-id\fP.
+.IP "\fBEndText:\fP \fItext\fP"
+This specifies text to be output when the end tag is processed.
+.IP "\fBIgnore:\fP \fIkey\fP"
+This specifies that the data or children for this element are to be ignored.
+Set \fIkey\fP to \fBall\fP to ignore the element (data and child elements),
+to \fBdata\fP to ignore the immediate character data content (child elements are still descended into),
+and to \fBchildren\fP to process the immediate character data content but not descended into child elements.
+Other actions specified in this transpec are still performed, however.
+.IP "\fBIncrement:\fP \fIname\fP"
+This is used to increment a variable whose value is a number.
+If the variable is not a number, no action will be taken.
+The variable must have been previously defined. This action is done immediately before \fBEndText\fP.
+There may be more than one of these lines for each transpec.
+.IP "\fBMessage:\fP \fItext\fP"
+This specifies a string to be printed to the standard error when the matching element is processed.
+It is intended for informing the user of the progress of the translation.
+It is also used for validation of instances (see the \fB-v\fP flag of \fBinstant\fP(1));
+a spec would be written to recognize a construct that is not allowed.
+This action is done immediately after \fBStartText\fP.
+Messages are also useful for debugging spec files; one is able to easily tell when a matching spec is processed,
+without looking at the actual output of the translation.
+Note that the spec writer is responsible for putting newlines (\fB\en\fP) in the message text.
+.IP "\fBReplace:\fP \fItext\fP"
+This specifies text to replace the current subtree with.
+This is equivalent to \fBStartText\fP and \fBIgnore\fP.
+.IP "\fBQuit:\fP \fItext\fP"
+This specifies text to be printed to the standard error. The program then terminates with exit status 1.
+This is intended for bailing out when an undesirable instance is encountered
+(such as when it is known that the formatting application can never handle a class of components, like tables).
+.IP "\fBSet:\fP \fIname\fP \fIvalue\fP"
+This is used to set a variable whose name is \fIname\fP and value is \fIvalue\fP.
+Names that would be valid for GIs in the document instance are valid for variable names.
+\fIvalue\fP is the rest of the line and may be any string. This action is done immediately before \fBEndText\fP.
+There may be more than one of these lines for each transpec.
+See the discussion on variables below.
+.IP "\fBSpecID:\fP \fIspec-id\fP"
+This names the spec with the number \fIspec-id\fP. Other specs may refer to this one by this number by an \fBAction\fP field or an \fB_action\fP special variable.
+This is used for cases where several specs to perform the exact same action.
+.IP "\fBStartText:\fP \fItext\fP"
+This specifies text to be output when the start tag is processed.
+'br\" labeled list end
+.SS "Other Fields"
+.PP
+These fields may appear anywhere. The action occurs when the translation spec file is read, before any elements are translated.
+Theses are independent of any element processing.
+'br\" labeled list
+.IP "\fBVar:\fP \fIname\fP \fIvalue\fP"
+This is used to define a variable whose name is \fIname\fP and value is \fIvalue\fP.
+It is similar to \fBSet\fP, but it may occur anywhere in the file and takes effect when the spec file is read.
+'br\" labeled list end
+.SS "Text Strings"
+.PP
+The \fItext\fP referred to in the \fBStartText\fP, \fBEndText\fP, \fBReplace\fP,
+and \fBMessage\fP actions is more than simple character strings.
+Special sequences allow more complex output.
+.PP
+One type of special sequence is for C-style string processing.
+Most special characters are escaped with a \e (backslash). Like in C or shell programs, to print a \e (backslash), you must escape it with another backslash. These special character strings are:
+'br\" labeled list
+.IP "\fB\en (backslash-n)\fP"
+This specifies that a newline character is to be printed to the output stream.
+.IP "\fB\er (backslash-r)\fP"
+This specifies that a carriage return character is to be printed to the output stream.
+.IP "\fB\et (backslash-t)\fP"
+This specifies that a tab character is to be printed to the output stream.
+.IP "\fB\es (backslash-s)\fP"
+This specifies that a space is to be printed to the output stream.
+This is useful for the end of a transpec line, where it can be difficult to tell if a blank is present at the end.
+.IP "\fB\e007 (backslash-007)\fP"
+This specifies that the character whose octal value is 007 is to be printed to the output stream.
+This works for any octal character value.
+.IP "\fB^ (caret)\fP"
+This specifies the that position in the string will be at the start of a line in the output stream.
+'br\" labeled list end
+.PP
+If the first token of the text string is \fB#include\fP, then the second token is taken to be a file name and that file is included.
+If the file is not found, the library directory, as mentioned above, is searched.
+If the text string starts with a \fB!\fP (exclamation point), the rest of the line is taken to be a command and the output of that command is inserted.
+.PP
+An element's attributes may also be used in the text of output fields.
+To use an attribute value, precede its name with a \fB${\fP (dollar sign-left curly bracket) and follow it with a \fB}\fP (right curly bracket).
+(This style is followed by the Bourne shell.) For example, \fB${TYPE}\fP.
+If the attribute is not set (not IMPLIED), nothing will be printed to the output stream.
+To specify a value to use if the attribute is not set, place the value after the attribute name, separated by a space.
+To return the attribute value in lower-case, add a colon followed by
+lower-case l (\fB${TYPE:l}\fP.
+.SH "Variables"
+.PP
+Variables in \fBinstant\fP are similar to those in many other string-oriented programming languages, such as \fBsh\fP and \fBawk\fP.
+They are set by: \fBVar:\fP \fIname\fP \fIvalue\fP and \fBSet:\fP \fIname\fP \fIvalue\fP.
+Values may be set and reset to any string.
+In a \fBVar\fP line, if the value begins with a \fB!\fP,
+then the rest of the line is executed as a command, and its output is taken as the \fIvalue\fP.
+.PP
+A reference to the value of a variable follows the same syntax as
+a reference to the value of an attribute: \fB${\fIname\fB}\fR.
+If that variable has not been defined, a null value will be returned.
+A default value can be returned instead of null for an undefined variable
+by using the form: \fB${\fIname default\fB}\fR.
+.PP
+Variables may be used as attributes are, that is in any of the text strings mentioned above.
+In fact, if an attribute name is referred to and it is not set for a given element,
+\fBinstant\fP looks for a variable with the same name. This way global defaults can be set.
+If you want to be sure that you are accessing a local variable value, not an attribute value, you can use lower or mixed case names.
+Attribute names, as passed by \fBsgmls\fP, are in upper case.
+.PP
+Any number of \fBVar\fP actions may appear in the spec file. These set the values of the variables before any translation takes place.
+The \fBSet\fP actions within transpecs are performed when that spec is processed when an element matches the given criteria.
+.SS "Preset Variables"
+.PP
+Several variables are preset by \fBinstant\fP upon start of the program.
+Their values may be overridden in transpec files or on the command line.
+'br\" labeled list
+.IP "\fBdate\fP"
+This is the date and time that the program started. The format is: \f(CWTue 10 Aug 1993, 16:52\fP.
+.IP "\fBhost\fP"
+This is the name of the host where the program is run. It is what is returned by the \fBgethostname\fP library call.
+.IP "\fBtranspec\fP"
+This is the translation spec filename.
+.IP "\fBuser\fP"
+This is the login name of the user running the program.
+'br\" labeled list end
+.SS "Special Variables"
+.PP
+There is a collection of special variables called \fIspecial variables\fP.
+These are identified by starting the names with a \fB_\fP (underscore).
+This is a summary of the special variables. A complete discussion of each special variable follows.
+\fBspec-id\fP refers to a number specified in a \fBSpecID\fP field.
+When used in a special variable, it means to perform the action in that translation spec.
+.PP
+Note that when a \fIspec-id\fR is given in a special variable,
+the default is to perform the translation spec named by the \fIspec-id\fR ignoring
+of any criteria statements found there.
+For most special variables that use a \fIspec-id\fP, postpending a "\fBt\fR" to
+the \fIspec-id\fR (with no spaces between them, eg,
+"\fB${_followrel child TITLE 15t}\fR"), will cause the criteria statements
+in the named translation spec to evaluate successfully before that translation
+spec will be processed.
+.P
+.TS
+tab(@);
+l l.
+\fBVariable Usage\fR@\fBDescription\fR
+\fB_action\fP \fIspec-id\fP@do spec with id spec-id
+\fB_allatts\fP@print all attribute/value pairs
+\fB_attval\fP \fIatt\fP [\fIvalue\fP] \fIspec-id\fP@use spec-id if attribute matches
+\fB_chasetogi\fP \fIgi\fP \fIspec-id\fP@follow IDREFs until gi found
+\fB_eachatt\fP \fIatt\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id for each word of attribute value
+\fB_eachcon\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id for each word of content
+\fB_env\fP \fIenv-variable\fP@return value of env variable
+\fB_filename\fP@filename of notation
+\fB_find\fP \fIrel\fP \fIgi\fP \fIspec-id\fP@find gi based on relationship
+\fB_followlink\fP [\fIattname\fP] \fIspec-id\fP@follow IDREFs [attname] and use spec-id
+\fB_followrel\fP \fIrel\fP \fIgi\fP \fIspec-id\fP@do spec-id on rel if it matches
+\fB_gi\fP [\fBM|L|U\fP]@return GI name; M, L, U case
+\fB_id\fP \fIid [\fP\fIspec-id\fP]@find element with ID and use spec-id
+\fB_include\fP \fIfilename\fP@insert file here
+\fB_infile\fP [\fBline\fP]@instance filename [and line number]
+\fB_insertnode\fP S|E \fIspec-id\fP@do spec-id when element is traversed
+\fB_isset\fP \fIvar\fP [\fIvalue\fP] \fIspec-id\fP@do spec-id if variable matches
+\fB_location\fP@print location of current element
+\fB_namelist\fP \fIspec-id\fP [\fIspec-id\fP]@content is namelist, do spec-id for each
+\fB_nchild\fP [\fIgi\fP]@number of child elements [named \fIattname\fP]
+\fB_osftable\fP \fIformat\fP [\fIflag\fP]@print table format specification
+\fB_path\fP@print path to current element
+\fB_pattr\fP \fIattname\fP@value of parent's attribute
+\fB_pfind\fP \fIargs ...\fP@same as \fB_find\fP, but start at parent
+\fB_relation\fP \fIrel\fP \fIgi\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id if relation matches
+\fB_set\fP \fIvar\fP \fIvalue\fP@set variable to value
+\fB_!\fP\fIcommand\fP@command to run
+.TE
+'br\" labeled list
+.IP "\fB_action\fP \fIspec-id\fP"
+Use the actions of the spec identified by the \fBSpecID\fP with matching identifier \fIspec-id\fP.
+This behaves similarly to the \fBAction\fP action, but is in addition to the present translation spec.
+.IP "\fB_allatts\fP"
+Print all attribute name-value pairs of the current element to the output stream.
+The name and value are separated by a \fB=\fP (equals sign), and the value is surrounded by quotes.
+This can be useful for creating a normalized version of the instance.
+.IP "\fB_attval\fP \fIattname\fP [\fIvalue\fP] \fIspec-id\fP"
+If the current element has an attribute named \fIattname\fP, optionally whose value matches \fIvalue\fP,
+use the actions of the transpec identified by \fIspec-id\fP.
+.IP "\fB_chasetogi\fP \fIgi\fP \fIspec-id\fP"
+Follow IDREF attributes until if finds an element whose GI is \fIgi\fP or which has a child element with that GI.
+It will apply the transpec \fIspec-id\fP to that element.
+By default, \fBinstant\fP assumes the attributes named \fBLINKEND\fP, \fBLINKENDS\fP, and \fBIDREF\fP are of type IDREF or IDREFS.
+(This corresponds with the OSF DTDs.)
+You can change this by setting the variable \fBlink_atts\fP to a space-separated list of attribute names.
+.IP "\fB_eachatt\fP \fIatt\fP \fIspec-id\fP [\fIspec-id2\fP]"
+The transpec named by \fIspec-id\fR is invoked once per each word
+found in the value of the attribute \fIatt\fR.
+Inside the target transpec, the current word being processed
+is available in the variable named \fBeach_A\fR (\fB${each_A}\fR).
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first word
+in the attribute and \fIspec-id2\fP for the others.
+.IP "\fB_eachcon\fP \fIspec-id\fP [\fIspec-id2\fP]"
+The transpec named by \fIspec-id\fR is invoked once per each word
+found in the content of the current element.
+Inside the target transpec, the current word being processed
+is available in the variable named \fBeach_C\fR (\fB${each_C}\fR).
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first word
+in the content and \fIspec-id2\fP for the others.
+.IP "\fB_env\fP \fIenv-variable\fP"
+Print the value of the environment variable \fIenv-variable\fP to the output stream.
+.IP "\fB_filename\fP"
+Print the filename of the notation associated with this element, if any.
+This is used to get the filename of an external notation entity reference.
+For example, to print the filename in the latex macro from the epsf macro package, use \f(CW\e\eepsfboxi{${_filename}}\fP.
+.IP "\fB_find\fP [\fBtop\fP] \fIrelationship\fP \fIargs ...\fP \fIspec-id\fP"
+Descend the document hierarchy finding elements that match one of several criteria.
+When one is found, the action specified by \fIspec-id\fP is performed.
+If \fBtop\fP is specified, the search starts at the top of the document hierarchy, rather than at the current element.
+The possible values for \fIrelationship\fP are \fBgi\fP, \fBgi-parent\fP, \fBparent\fP, and \fBattr\fP,
+and take different arguments.
+Explanations may be best done by example:
+\fB_find gi CHAPTER 123\fP means to find elements whose GI is CHAPTER, and perform action 123;
+\fB_find gi-parent TITLE CHAPTER 124\fP means to find elements whose GI is TITLE and whose parent is CHAPTER, and perform action 124;
+\fB_find parent BODY 125\fP means to find elements whose parent's GI is BODY, and perform action 125;
+\fB_find attr TYPE UGLY 125\fP means to find elements whose attribute named TYPE is set to UGLY, and perform action 126.
+.IP "\fB_followlink\fP [\fIattname\fP] \fIspec-id\fP"
+When processing an element, \fBinstant\fP will follow the IDREF attributes until an element with no IDREF attributes is found.
+It will then apply the transpec specified by \fIspec-id\fP to that element.
+If specified, it will follow the link pointed to by \fIattname\fP.
+By default, \fBinstant\fP assumes the attributes named \fBLINKEND\fP and \fBLINKENDS\fP are if type IDREF or IDREFS.
+You can change this by setting the variable \fBlink_atts\fP to a space-separated list of attribute names.
+.IP "\fB_followrel\fP \fIrelationship\fP \fIgi\fP \fIspec-id\fP"
+If the \fIgi\fP has the specified \fIrelationship\fP to the current element,
+perform the action specified by \fIspec-id\fP on the related element.
+See the discussion of the criteria field \fBRelation\fP for acceptable relationship names.
+.IP "\fB_gi\fP [\fBM|L|U\fP]"
+Print the name of the current GI to the output stream.
+If specified, \fBM\fP, \fBL\fP, or \fBU\fP will ensure the GI name is printed in mixed, lower, or upper case, respectively.
+.IP "\fB_id\fP \fIid\fP [\fIspec-id\fP]"
+Find the element with \fIid\fP and use \fIspec-id\fP, if set. If not set, use the spec for that element's context.
+.IP "\fB_include\fP \fIfilename\fP"
+Insert the file \fIfilename\fP into the output stream.
+.IP "\fB_infile\fP [\fBline\fP]"
+Print the name of the sgml instance file to the output stream. If \fBline\fP is specified, also print the line number.
+This depends on \fBsgmls\fP being called with the \fB-l\fP option.
+.IP "\fB_insertnode\fP \fBS\fP|\fBE\fP \fIspec-id\fP"
+Do \fIspec-id\fP when the current element is traversed at a later pass.
+This can be considered inserting a node, without content, into the hierarchy.
+This is only useful if done to elements \fIbefore\fP they are processed.
+Typically \fB_chasetogi\fP or \fB_followlink\fP is specified early in an instance's processing,
+so that when the elements found by one of these actions are processed in their turn, the added actions are performed.
+\fB_insertnode\fP would be specified as the action of a \fIspec-id\fP pointed to in a \fB_chasetogi\fP or \fB_followlink\fP usage.
+.IP "\fB_location\fP"
+The location of the current element is printed to the output stream in several ways: the path to the element (see \fB_path\fP),
+a position hint, which is the nearest title, the line number, if the ESIS (output from \fBsgmls\fP) contains line numbers,
+and the ID of the element, if it has one.
+This is especially useful when using the \fBMessage\fP action to validate an instance.
+.IP "\fB_namelist\fP \fIspec-id\fP [\fIspec-id2\fP]"
+This assumes that the content of the current element is a namelist (a list of element IDs),
+and applies the action based on \fIspec-id\fP for each element pointed to.
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first ID in the namelist and \fIspec-id2\fP for the others.
+.IP "\fB_nchild\fP [\fIgi\fP]"
+Print the number of child elements of the element to the output stream.
+If \fIgi\fP is specified, print the number of child element with that name.
+.IP "\fB_osftable\fP \fBtex\fP|\fBtbl\fP|\fBcheck\fP [\fIflag\fP]"
+Print table markup into the output stream. The format depends on whether \fBtex\fP or \fBtbl\fP is specified.
+The \fIflag\fP may be one of \fBcellstart\fP, \fBcellend\fP, \fBrowstart\fP, \fBrowend\fP, \fBtop\fP, or \fBbottom\fP.
+The value determines what markup or text will be generated.
+If \fBcellstart\fP is specified, the correct markup for the beginning of a cell is output.
+If \fBtop\fP, \fBbottom\fP, or \fBrowend\fP are specified,
+the correct markup for the end of the appropriate position is printed to the output stream.
+If \fBcheck\fP is specified, the attributes and child elements are checked for errors and consistency.
+.IP "\fB_path\fP"
+Print the path to current GI to the output stream. A path is each element, going down the tree from the topmost element.
+A number in parentheses after each element name shows which child element the next one is in the order of children for that element.
+Ordering starts at 0.
+For example: \f(CWOSF-BOOK(3) BODY(0) CHAPTER(4) SECTION\fP.
+This says the path is \fB<OSF-BOOK>\fP's third child, \fB<BODY>\fP's zeroth,
+and \fB<CHAPTER>\fP's fourth, which is named \fB<SECTION>\fP.
+.IP "\fB_pattr\fP \fIname\fP"
+Print the value of parent's attribute whose name is \fIname\fP to the output stream.
+.IP "\fB_pfind\fP \fIrel\fP \fIgi\fP \fIspec-id\fP"
+This is exactly the same as \fB_find\fP except that the search starts at the current element's parent.
+.IP "\fB_relation\fP \fIrelationship\fP \fIgi\fP \fIspec-id\fP [\fIspec-id2\fP]"
+If the \fIgi\fP has the specified \fIrelationship\fP to the current element,
+perform the action specified by \fIspec-id\fP on the current element.
+If the relationship test fails and \fIspec-id2\fP is specified, perform that action.
+See the discussion of the criteria field \fBRelation\fP for acceptable relationship names.
+.IP "\fB_set\fP \fIvarname\fP \fIvalue\fP"
+Set the value of the variable \fIvarname\fP to \fIvalue\fP.
+.IP "\fB_isset\fP \fIvarname\fP [\fIvalue\fP] \fIspec-id\fP"
+If the value of the variable \fIvarname\fP is set to \fIvalue\fP, then perform action referred to by \fIspec-id\fP.
+If \fIvalue\fP is not specified, action will be performed if \fIvarname\fP is set to any value.
+.IP "\fB_!\fP \fIcommand\fP"
+Run the command \fIcommand\fP, directing its standard output into the output stream.
+'br\" labeled list end
+.SS "Immediate Variables"
+.PP
+\fIImmediate variables\fR are like special variables, except that they
+are expanded when the transpec is originally processed (special
+variables are processed later, near when the final output is being generated).
+The general syntax of immediate variables is \fB${+\fIimmediate_variable\ ...\fB}\fR.
+.PP
+There is currently only one immediate variable defined:
+.IP "\fB+content\fP"
+This special variable is replaced by the data content of the current element.
+.SH "Examples"
+.PP
+The following will output the given string for elements whose generic identifier is \fBP\fP (for paragraph).
+At the start of processing this element, the program ensures that the output starts on a new line,
+the \fBtroff\fP macro \fB<.P>\fP is output, then a newline.
+At the end of this element processing, the program ensures that the output starts on a new line.
+.DS
+.nf
+.ft CW
+GI: P
+StartText: ^.P^
+EndText: ^
+-
+.ft R
+.fi
+.DE
+.PP
+The following will output the given string for elements whose generic identifier is \fBCMD-ARGUMENT\fP and which have an
+attribute \fBPRESENCE\fP set to the value \fBOPTIONAL\fP.
+.DS
+.nf
+.ft CW
+GI: CMD-ARGUMENT
+AttValue: PRESENCE OPTIONAL
+StartText: $\e\e[
+EndText: $\e\e]
+-
+.ft R
+.fi
+.DE
+.PP
+The following prints the section number, title, and page number of the target of a cross reference.
+Assume the cross reference points to a section element, which contains a title element.
+The criteria for this spec to match is that the attribute \fBOSFROLE\fP is set to the value \fBgetfull\fP.
+The action is to replace the content of the \fB<XREF>\fP element with the given string.
+When processing the string, \fBinstant\fP will follow the IDREF attributes of \fB<XREF>\fP
+until an element with no IDREF attributes is found. It will then apply the transpec numbered \fB87\fP to that element,
+which will print the name of the GI in mixed case into the output stream.
+It will then print the LaTeX reference instruction with the value of the \fBLINKEND\fP attribute as an argument.
+(This will become the section number after processing by LaTeX.)
+It will then follow IDREFs until if finds an element whose GI is \fBTITLE\fP or which has a child element with that GI.
+It will apply the transpec numbered \fB1\fP to that element, which copies the title into the output stream where the cross reference occurs.
+Finally, it will print the word \fBpage\fP followed by the LaTeX instruction to obtain the page number of a reference.
+.DS
+.nf
+.ft CW
+GI: XREF
+AttValue: OSFROLE getfull
+Replace: ${_followlink 87} \e\eref{${LINKEND}},\es
+ {\e\ebf ${_chasetogi TITLE 1}}, page \e\epageref{${LINKEND}}
+-
+# Print GI name, in mixed case
+GI: _pr_gi_name
+SpecID: 87
+Ignore: 1
+EndText: ${_gi M}
+-
+GI: _pass-text
+SpecID: 1
+-
+.ft R
+.fi
+.DE
+.SH "Related Information"
+.PP
+\fBinstant\fP(1), \fBsgmls\fP(1), \fBegrep\fP(1).
diff --git a/usr.bin/sgmls/instant/tranvar.c b/usr.bin/sgmls/instant/tranvar.c
new file mode 100644
index 0000000..c8778a8
--- /dev/null
+++ b/usr.bin/sgmls/instant/tranvar.c
@@ -0,0 +1,757 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+/* ________________________________________________________________________
+ *
+ * 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 =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/tranvar.c,v 1.5 1996/06/11 22:43:15 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 <regexp.h>
+#include "general.h"
+#include "translate.h"
+
+static char **idrefs; /* list of IDREF att names to follow */
+static char *def_idrefs[] = { "LINKEND", "LINKENDS", "IDREF", 0 };
+static char *each_A = 0; /* last seen _eachatt */
+static char *each_C = 0; /* last seen _eachcon */
+
+/* forward references */
+void ChaseIDRefs(Element_t *, char *, char *, FILE *);
+void Find(Element_t *, int, char **, FILE *);
+void GetIDREFnames();
+
+/* ______________________________________________________________________ */
+/* 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).
+ */
+
+void
+ExpandSpecialVar(
+ char *name,
+ Element_t *e,
+ FILE *fp,
+ int track_pos
+)
+{
+ FILE *infile;
+ char buf[LINESIZE], *cp, *atval;
+ char **tok;
+ int ntok, n, i, actioni;
+ char *action, *action1;
+ Element_t *ep;
+ Trans_t *t, *tt;
+
+ /* 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;
+ }
+
+ /* 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 && each_A) {
+ OutputString(each_A, fp, track_pos);
+ return;
+ }
+ if (*name == 'C' && name[1] == EOS && each_C) {
+ OutputString(each_C, fp, track_pos);
+ return;
+ }
+
+ 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;
+ }
+ while (fgets(buf, LINESIZE, infile)) fputs(buf, fp);
+ fclose(infile);
+ }
+ else fprintf(stderr, "No file name specified for include\n");
+ return;
+ }
+
+ /* 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 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;
+ }
+ if (!e->entity->fname) {
+ fprintf(stderr, "Expected filename (internal error? bug?):\n");
+ PrintLocation(e, stderr);
+ return;
+ }
+ 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;
+ }
+ 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")) {
+ TranTByAction(e, tok[1], fp);
+ }
+
+ /* Number of child elements of this element.
+ * Format: _nchild */
+ else if (StrEq(tok[0], "nchild")) {
+ if (ntok > 1) {
+ for (n=0,i=0; i<e->necont; 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;
+ }
+ actioni = atoi(tok[2]);
+ if (actioni) ChaseIDRefs(e, tok[1], tok[2], 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) {
+ if ((atval = FindAttValByName(e, tok[1]))) {
+ if ((ep = FindElemByID(atval))) {
+ TranTByAction(ep, tok[2], fp);
+ return;
+ }
+ }
+ else fprintf(stderr, "Error: Did not find attr: %s.\n", tok[1]);
+ return;
+ }
+ 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]))) {
+ TranTByAction(ep, tok[1], fp);
+ return;
+ }
+ 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;
+ }
+
+ /* 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 = tok[1];
+ if (ntok > 2) action = tok[2];
+ else action = action1;
+ for (i=0; i<e->ndcont; i++) {
+ n = 0;
+ tok = Split(e->dcont[i], &n, S_STRDUP);
+ for (id=0; id<n; id++) {
+ if (fold_case)
+ for (cp=tok[id]; *cp; cp++)
+ if (islower(*cp)) *cp = toupper(*cp);
+ if ((e = FindElemByID(tok[id]))) {
+ if (id) TranTByAction(e, action, fp);
+ else TranTByAction(e, action1, fp); /* first one */
+ }
+ else fprintf(stderr, "Error: Can not find ID: %s.\n", tok[id]);
+ }
+ }
+ }
+
+ /* For each word in the element's content, do action.
+ * Format: _eachcon action [action] */
+ else if (StrEq(tok[0], "eachcon")) {
+ int id;
+ action1 = tok[1];
+ if (ntok > 3) action = tok[2];
+ else action = action1;
+ for (i=0; i<e->ndcont; i++) {
+ n = 0;
+ tok = Split(e->dcont[i], &n, S_STRDUP|S_ALVEC);
+ for (id=0; id<n; id++) {
+ each_C = tok[id];
+ TranTByAction(e, action, fp);
+ }
+ free(*tok);
+ }
+ }
+ /* For each word in the given attribute's value, do action.
+ * Format: _eachatt attname action [action] */
+ else if (StrEq(tok[0], "eachatt")) {
+ int id;
+ action1 = tok[2];
+ if (ntok > 3) action = 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<n; id++) {
+ each_A = tok[id];
+ if (id) TranTByAction(e, action, fp);
+ else TranTByAction(e, action1, fp); /* first one */
+ }
+ free(*tok);
+ }
+ }
+
+ /* Do action on this element if element has [relationship] with gi.
+ * Format: _relation relationship gi action [action] */
+ else if (StrEq(tok[0], "relation")) {
+ if (ntok >= 4) {
+ if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Current)) {
+ /* action not done, see if alt action specified */
+ if (ntok >= 5)
+ TranTByAction(e, 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 ((ep = FindElemByID(tok[1]))) {
+ if (ntok > 2) TranTByAction(ep, tok[2], fp);
+ else {
+ t = FindTrans(ep, 0);
+ 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) TranTByAction(e, tok[2], fp);
+ else if (ntok > 3 && !strcmp(cp, tok[2]))
+ TranTByAction(e, tok[3], fp);
+ }
+ }
+
+ /* 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")) {
+ actioni = atoi(tok[2]);
+ if (*tok[1] == 'S') e->gen_trans[0] = actioni;
+ else if (*tok[1] == 'E') e->gen_trans[1] = actioni;
+ }
+
+ /* Do an CALS 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 instant.
+ *
+ * This was originally written for the OSF DTDs and recoded by FLD for
+ * CALS tables (since no one will ever use the OSF tables). Although
+ * TeX was addressed first, it seems that a fresh approach was required,
+ * and so, tbl is the first to be really *fixed*. Once tbl is stable,
+ * and there is a need for TeX again, that part will be recoded.
+ *
+ * *Obsolete* form (viz, for TeX):
+ * Format: _calstable [clear|check|tex]
+ * [cellstart|cellend|rowstart|rowend|top|bottom]
+ *
+ * New, good form:
+ *
+ * Format: _calstable [tbl]
+ * [tablestart|tableend|tablegroup|tablefoot|rowstart|
+ * rowend|entrystart|entryend]
+ */
+
+ else if (StrEq(tok[0], "calstable")) {
+ CALStable(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) TranTByAction(e, tok[2], fp);
+ else if (ntok > 3 && !strcmp(atval, tok[2]))
+ TranTByAction(e, 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) {
+ TranTByAction(e, tok[2], fp);
+ }
+ if (ntok > 3 && !strcmp(atval, tok[2]))
+ TranTByAction(e, tok[3], fp);
+ }
+ }
+
+ /* Print each attribute and value for the current element, hopefully
+ * in a legal sgml form: <elem-name att1="value1" att2="value2:> .
+ * Format: _allatts */
+ else if (StrEq(tok[0], "allatts")) {
+ for (i=0; i<e->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"))
+ fprintf(fp, " %d", e->lineno);
+ }
+ return;
+ }
+ 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);
+ }
+ }
+
+ /* If the element is not empty do specid.
+ * Format: _notempty spec-id */
+ else if (StrEq(tok[0], "notempty")) {
+ if (ntok > 1 && e->ncont) {
+ TranTByAction(e, tok[1], fp);
+ }
+ }
+
+ /* 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;
+}
+
+/* ______________________________________________________________________ */
+/* return the value for the special variables _A (last processed _eachatt)
+ * and _C (last processed _eachcon)
+ */
+
+char *
+Get_A_C_value(const char * name)
+{
+ if ( !strcmp(name, "each_A") ) {
+ if ( each_A ) {
+ return each_A;
+ } else {
+ fprintf(stderr, "Requested value for unset _A variable\n");
+ }
+ } else
+ if ( !strcmp(name, "each_C") ) {
+ if ( each_C ) {
+ return each_C;
+ } else {
+ fprintf(stderr, "Requested value for unset _C variable\n");
+ }
+ } else {
+ fprintf(stderr, "Requested value for unknown special variable '%s'\n",
+ name);
+ }
+ return "";
+}
+
+/* ______________________________________________________________________ */
+/* 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()
+{
+ 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,
+ char * 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)) {
+ TranTByAction(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; i<ntok; i++) {
+ /* get element pointed to */
+ if ((e = FindElemByID(tok[i]))) {
+ /* OK, we found a matching GI name */
+ if (StrEq(e->gi, gi)) {
+ /* process using named action */
+ TranTByAction(e, action, fp);
+ return;
+ }
+ else {
+ /* this elem itself did not match, try its children */
+ for (ei=0; ei<e->necont; ei++) {
+ if (StrEq(e->econt[ei]->gi, gi)) {
+ TranTByAction(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;
+ char action[10];
+ 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[0]) TranTByAction(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[0]) TranTByAction(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))
+ TranTByAction(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[0]) TranTByAction(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];
+ strcpy(DS.action, 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];
+ strcpy(DS.action, 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];
+ strcpy(DS.action, 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];
+ strcpy(DS.action, av[4]);
+ DescendTree(e, tr_find_attr, 0, 0, &DS);
+ }
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/util.c b/usr.bin/sgmls/instant/util.c
new file mode 100644
index 0000000..eb6015d
--- /dev/null
+++ b/usr.bin/sgmls/instant/util.c
@@ -0,0 +1,1109 @@
+/*
+ * 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.
+ */
+/*
+ * 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.
+ */
+/* ________________________________________________________________________
+ *
+ * General utility functions for 'instant' program. These are used
+ * throughout the rest of the program.
+ *
+ * Entry points for this module:
+ * Split(s, &n, flags) split string into n tokens
+ * NewMap(slot_incr) create a new mapping structure
+ * FindMapping(map, name) find mapping by name; return mapping
+ * FindMappingVal(map, name) find mapping by name; return value
+ * SetMapping(map, s) set mapping based on string
+ * OpenFile(filename) open file, looking in inst path
+ * FilePath(filename) find path to a file
+ * FindElementPath(elem, s) find path to element
+ * PrintLocation(ele, fp) print location of element in tree
+ * NearestOlderElem(elem, name) find prev elem up tree with name
+ * OutputString(s, fp, track_pos) output string
+ * AddElemName(name) add elem to list of known elements
+ * AddAttName(name) add att name to list of known atts
+ * FindAttByName(elem, name) find an elem's att by name
+ * FindContext(elem, lev, context) find context of elem
+ * QRelation(elem, name, rel_flag) find relation elem has to named elem
+ * DescendTree(elem, enter_f, leave_f, data_f, dp) descend doc tree,
+ * calling functions for each elem/node
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/util.c,v 1.4 1996/06/02 21:47:32 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <regexp.h>
+/* CSS don't have it and I don't see where it's used
+#include <values.h>
+*/
+
+#include "general.h"
+#include "translate.h"
+
+/* ______________________________________________________________________ */
+/* "Split" a string into tokens. Given a string that has space-separated
+ * (space/tab) tokens, return a pointer to an array of pointers to the
+ * tokens. Like what the shell does with *argv[]. The array can be is
+ * static or allocated. Space can be allocated for string, or allocated.
+ * Arguments:
+ * Pointer to string to pick apart.
+ * Pointer to max number of tokens to find; actual number found is
+ * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
+ * code). If more tokens than the number specified, make last token be
+ * a single string composed of the rest of the tokens (includes spaces).
+ * Flag. Bit 0 says whether to make a copy of input string (since we'll
+ * clobber parts of it). To free the string, use the pointer to
+ * the first token returned by the function (or *ret_value).
+ * Bit 1 says whether to allocate the vector itself. If not, use
+ * (and return) a static vector.
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+char **
+Split(
+ char *s, /* input string */
+ int *ntok, /* # of tokens desired (input)/found (return) */
+ int flag /* dup string? allocate a vector? */
+)
+{
+ int maxnt, i=0;
+ int n_alloc;
+ char **tokens;
+ static char *local_tokens[100];
+
+ /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
+ if (ntok == NULL)
+ maxnt = 100;
+ else {
+ if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
+ else maxnt = *ntok;
+ *ntok = 0;
+ }
+
+ if (!s) return 0; /* no string */
+
+ /* Point to 1st token (there may be initial space) */
+ while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
+ if (*s == EOS) return 0; /* none found? */
+
+ /* See if caller wants us to copy the input string. */
+ if (flag & S_STRDUP) s = strdup(s);
+
+ /* See if caller wants us to allocate the returned vector. */
+ if (flag & S_ALVEC) {
+ n_alloc = 20;
+ Malloc(n_alloc, tokens, char *);
+ /* if caller did not specify max tokens to find, set to more than
+ * there will possibly ever be */
+ if (!ntok || !(*ntok)) maxnt = 10000;
+ }
+ else tokens = local_tokens;
+
+ i = 0; /* index into vector */
+ tokens[0] = s; /* s already points to 1st token */
+ while (i<maxnt) {
+ tokens[i] = s; /* point vector member at start of token */
+ i++;
+ /* If we allocated vector, see if we need more space. */
+ if ((flag & S_ALVEC) && i >= n_alloc) {
+ n_alloc += 20;
+ Realloc(n_alloc, tokens, char *);
+ }
+ if (i >= maxnt) break; /* is this the last one? */
+ while (*s && !IsWhite(*s)) s++; /* skip past end of token */
+ if (*s == EOS) break; /* at end of input string? */
+ if (*s) *s++ = EOS; /* terminate token string */
+ while (*s && IsWhite(*s)) s++; /* skip space - to next token */
+ }
+ if (ntok) *ntok = i; /* return number of tokens found */
+ tokens[i] = 0; /* null-terminate vector */
+ return tokens;
+}
+
+/* ______________________________________________________________________ */
+/* Mapping routines. These are used for name-value pairs, like attributes,
+ * variables, and counters. A "Map" is an opaque data structure used
+ * internally by these routines. The caller gets one when creating a new
+ * map, then hands it to other routines that need it. A "Mapping" is a
+ * name/value pair. The user has access to this.
+ * Here's some sample usage:
+ *
+ * Map *V;
+ * V = NewMap(20);
+ * SetMappingNV(V, "home", "/users/bowe");
+ * printf("Home: %s\n", FindMappingVal(V, "home");
+ */
+
+/* Allocate new map structure. Only done once for each map/variable list.
+ * Arg:
+ * Number of initial slots to allocate space for. This is also the
+ * "chunk size" - how much to allocate when we use up the given space.
+ * Return:
+ * Pointer to the (opaque) map structure. (User passes this to other
+ * mapping routines.)
+ */
+Map_t *
+NewMap(
+ int slot_increment
+)
+{
+ Map_t *M;
+ Calloc(1, M, Map_t);
+ /* should really do the memset's in Calloc/Malloc/Realloc
+ macros, but that will have to wait until time permits -CSS */
+ memset((char *)M, 0, sizeof(Map_t));
+ if (!slot_increment) slot_increment = 1;
+ M->slot_incr = slot_increment;
+ return M;
+}
+
+/* Given pointer to a Map and a name, find the mapping.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Variable name.
+ * Return:
+ * Pointer to the matching mapping structure, or null if not found.
+ */
+Mapping_t *
+FindMapping(
+ Map_t *M,
+ const char *name
+)
+{
+ int i;
+ Mapping_t *m;
+
+ if (!M || M->n_used == 0) return NULL;
+ for (m=M->maps,i=0; i<M->n_used; i++)
+ if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
+ return NULL;
+
+}
+
+/* Given pointer to a Map and a name, return string value of the mapping.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Variable name.
+ * Return:
+ * Pointer to the value (string), or null if not found.
+ */
+char *
+FindMappingVal(
+ Map_t *M,
+ const char *name
+)
+{
+ Mapping_t *m;
+
+ if ( !strcmp(name, "each_A") || !strcmp(name, "each_C") ) {
+ return Get_A_C_value(name);
+ }
+
+ /*
+ if (!M || M->n_used == 0) return NULL;
+ if ((m = FindMapping(M, name))) return m->sval;
+ return NULL;
+ */
+ if (!M || M->n_used == 0) {
+ return NULL;
+ }
+ if ((m = FindMapping(M, name))) {
+ return m->sval;
+ }
+ return NULL;
+
+}
+
+/* Set a mapping/variable in Map M. Input string is a name-value pair where
+ * there is some amount of space after the name. The correct mapping is done.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Pointer to variable name (string).
+ * Pointer to variable value (string).
+ */
+void
+SetMappingNV(
+ Map_t *M,
+ const char *name,
+ const char *value
+)
+{
+ FILE *pp;
+ char buf[LINESIZE], *cp;
+ int i;
+ Mapping_t *m;
+
+ /* First, look to see if it's a "well-known" variable. */
+ if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
+ if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
+ if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
+
+ m = FindMapping(M, name); /* find existing mapping (if set) */
+
+ /* OK, we have a string mapping */
+ if (m) { /* exists - just replace value */
+ free(m->sval);
+ if (value) m->sval = strdup(value);
+ else m->sval = NULL;
+ }
+ else {
+ if (name) { /* just in case */
+ /* Need more slots for mapping structures? Allocate in clumps. */
+ if (M->n_used == 0) {
+ M->n_alloc = M->slot_incr;
+ Malloc(M->n_alloc, M->maps, Mapping_t);
+ }
+ else if (M->n_used >= M->n_alloc) {
+ M->n_alloc += M->slot_incr;
+ Realloc(M->n_alloc, M->maps, Mapping_t);
+ }
+
+ m = &M->maps[M->n_used];
+ M->n_used++;
+ m->name = strdup(name);
+ if (value) m->sval = strdup(value);
+ else m->sval = NULL;
+ }
+ }
+
+ if (value)
+ {
+ /* See if the value is a command to run. If so, run the command
+ * and replace the value with the output.
+ */
+ if (*value == '!') {
+ if ((pp = popen(value+1, "r"))) { /* run cmd, read its output */
+ i = 0;
+ cp = buf;
+ while (fgets(cp, LINESIZE-i, pp)) {
+ i += strlen(cp);
+ cp = &buf[i];
+ if (i >= LINESIZE) {
+ fprintf(stderr,
+ "Prog execution of variable '%s' too long.\n",
+ m->name);
+ break;
+ }
+ }
+ free(m->sval);
+ stripNL(buf);
+ m->sval = strdup(buf);
+ pclose(pp);
+ }
+ else {
+ sprintf(buf, "Could not start program '%s'", value+1);
+ perror(buf);
+ }
+ }
+ }
+}
+
+/* Separate name and value from input string, then pass to SetMappingNV.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Pointer to variable name and value (string), in form "name value".
+ */
+void
+SetMapping(
+ Map_t *M,
+ const char *s
+)
+{
+ char buf[LINESIZE];
+ char *name, *val;
+
+ if (!M) {
+ fprintf(stderr, "SetMapping: Map not initialized.\n");
+ return;
+ }
+ strcpy(buf, s);
+ name = val = buf;
+ while (*val && !IsWhite(*val)) val++; /* point past end of name */
+ if (*val) {
+ *val++ = EOS; /* terminate name */
+ while (*val && IsWhite(*val)) val++; /* point to value */
+ }
+ if (name) SetMappingNV(M, name, val);
+}
+
+/* ______________________________________________________________________ */
+/* Opens a file for reading. If not found in current directory, try
+ * lib directories (from TPT_LIB env variable, or -l option).
+ * Arguments:
+ * Filename (string).
+ * Return:
+ * FILE pointer to open file, or null if it not found or can't open.
+ */
+
+FILE *
+OpenFile(
+ char *filename
+)
+{
+ FILE *fp;
+
+ filename = FilePath(filename);
+ if ((fp=fopen(filename, "r"))) return fp;
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Opens a file for reading. If not found in current directory, try
+ * lib directories (from TPT_LIB env variable, or -l option).
+ * Arguments:
+ * Filename (string).
+ * Return:
+ * FILE pointer to open file, or null if it not found or can't open.
+ */
+
+char *
+FilePath(
+ char *filename
+)
+{
+ FILE *fp;
+ static char buf[LINESIZE];
+ int i;
+ static char **libdirs;
+ static int nlibdirs = -1;
+
+ if ((fp=fopen(filename, "r")))
+ {
+ fclose(fp);
+ strncpy(buf, filename, LINESIZE);
+ return buf;
+ }
+
+ if (*filename == '/') return NULL; /* full path specified? */
+
+ if (nlibdirs < 0) {
+ char *cp, *s;
+ if (tpt_lib) {
+ s = strdup(tpt_lib);
+ for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
+ nlibdirs = 0;
+ libdirs = Split(s, &nlibdirs, S_ALVEC);
+ }
+ else nlibdirs = 0;
+ }
+ for (i=0; i<nlibdirs; i++) {
+ sprintf(buf, "%s/%s", libdirs[i], filename);
+ if ((fp=fopen(buf, "r")))
+ {
+ fclose(fp);
+ return buf;
+ }
+ }
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* This will find the path to an tag. The format is the:
+ * tag1(n1):tag2(n2):tag3
+ * where the tags are going down the tree and the numbers indicate which
+ * child (the first is numbered 1) the next tag is.
+ * Returns pointer to the string just written to (so you can use this
+ * function as a printf arg).
+ * Arguments:
+ * Pointer to element under consideration.
+ * String to write path into (provided by caller).
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+char *
+FindElementPath(
+ Element_t *e,
+ char *s
+)
+{
+ Element_t *ep;
+ int i, e_path[MAX_DEPTH];
+ char *cp;
+
+ /* Move up the tree, noting "birth order" of each element encountered */
+ for (ep=e; ep; ep=ep->parent)
+ e_path[ep->depth-1] = ep->my_eorder;
+ /* Move down the tree, printing the element names to the string. */
+ for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
+ sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
+ cp += strlen(cp);
+ }
+ sprintf(cp, "%s", e->gi);
+ return s;
+}
+
+/* ______________________________________________________________________ */
+/* Print some location info about a tag. Helps user locate error.
+ * Messages are indented 2 spaces (convention for multi-line messages).
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer of where to print.
+ */
+
+void
+PrintLocation(
+ Element_t *e,
+ FILE *fp
+)
+{
+ char *s, buf[LINESIZE];
+
+ if (!e || !fp) return;
+ fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
+ if ((s=NearestOlderElem(e, "TITLE")))
+ fprintf(fp, " Position hint: TITLE='%s'\n", s);
+ if (e->lineno) {
+ if (e->infile)
+ fprintf(fp, " At or near instance file: %s, line: %d\n",
+ e->infile, e->lineno);
+ else
+ fprintf(fp, " At or near instance line: %d\n", e->lineno);
+ }
+ if (e->id)
+ fprintf(fp, " ID: %s\n", e->id);
+}
+
+/* ______________________________________________________________________ */
+/* Finds the data part of the nearest "older" tag (up the tree, and
+ * preceding) whose tag name matches the argument, or "TITLE", if null.
+ * Returns a pointer to the first chunk of character data.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Name (GI) of element we'll return data from.
+ * Return:
+ * Pointer to that element's data content.
+ */
+char *
+NearestOlderElem(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ Element_t *ep;
+
+ if (!e) return 0;
+ if (!name) name = "TITLE"; /* useful default */
+
+ for (; e->parent; e=e->parent) /* move up tree */
+ for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
+ ep = e->parent;
+ if (!strcmp(name, ep->econt[i]->gi))
+ return ep->econt[i]->ndcont ?
+ ep->econt[i]->dcont[0] : "-empty-";
+ }
+
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Expands escaped strings in the input buffer (things like tabs, newlines,
+ * octal characters - using C style escapes).
+ */
+
+char *ExpandString(
+ char *s
+)
+{
+ char c, *sdata, *cp, *ns;
+ int len, pos, addn;
+
+ if (!s) return s;
+
+ len = strlen(s);
+ pos = 0;
+ Malloc(len + 1, ns, char);
+ ns[pos] = EOS;
+
+ for ( ; *s; s++) {
+ c = *s;
+ cp = NULL;
+
+ /* Check for escaped characters from sgmls. */
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case 'n':
+ c = NL;
+ break;
+
+ case '\\':
+ c = '\\';
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /* for octal numbers (C style) of the form \012 */
+ c = *s++ - '0';
+ if (*s >= '0' && *s <= '7') {
+ c = c * 8 + (*s++ - '0');
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s - '0');
+ }
+ break;
+
+ case '|': /* SDATA */
+ s++; /* point past \| */
+ sdata = s;
+ /* find matching/closing \| */
+ cp = s;
+ while (*cp && *cp != '\\' && cp[1] != '|')
+ cp++;
+ if (!*cp)
+ break;
+
+ *cp = EOS; /* terminate sdata string */
+ cp++;
+ s = cp; /* s now points to | */
+
+ cp = LookupSDATA(sdata);
+ if (!cp)
+ cp = sdata;
+ c = 0;
+ break;
+
+ /* This shouldn't happen. */
+ default:
+ s--;
+ break;
+ }
+ }
+
+ /* Check for character re-mappings. */
+ if (nCharMap && c) {
+ int i;
+
+ for (i = 0; i < nCharMap; i++) {
+ if (c != CharMap[i].name[0])
+ continue;
+ cp = CharMap[i].sval;
+ c = 0;
+ break;
+ }
+ }
+
+ /* See if there is enough space for the data. */
+ /* XXX this should be MUCH smarter about predicting
+ how much extra memory it should allocate */
+ if (c)
+ addn = 1;
+ else
+ addn = strlen(cp);
+
+ /* If not, make some. */
+ if (addn > len - pos) {
+ len += addn - (len - pos);
+ Realloc(len + 1, ns, char);
+ }
+
+ /* Then copy the data. */
+ if (c)
+ ns[pos] = c;
+ else
+ strcpy(&ns[pos], cp);
+
+ pos += addn;
+ ns[pos] = EOS;
+ }
+ return(ns);
+}
+
+/* ______________________________________________________________________ */
+/* Expands escaped strings in the input buffer (things like tabs, newlines,
+ * octal characters - using C style escapes) and outputs buffer to specified
+ * fp. The hat/anchor character forces that position to appear at the
+ * beginning of a line. The cursor position is kept track of (optionally)
+ * so that this can be done.
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer of where to print.
+ * Flag saying whether or not to keep track of our position in the output
+ * stream. (We want to when writing to a file, but not for stderr.)
+ */
+
+void
+OutputString(
+ char *s,
+ FILE *fp,
+ int track_pos
+)
+{
+ char c;
+ static int char_pos = 0; /* remembers our character position */
+ char *p;
+
+ if (!fp) return;
+ if (!s) s = "^"; /* no string - go to start of line */
+
+ for (p = s; *p; p++) {
+ c = *p;
+ /* If caller wants us to track position, see if it's an anchor
+ * (ie, align at a newline). */
+ if (track_pos) {
+ if (c == ANCHOR && (p == s || *(p + 1) == EOS)) {
+ /* If we're already at the start of a line, don't do
+ * another newline. */
+ if (char_pos != 0) c = NL;
+ else c = 0;
+ }
+ else char_pos++;
+ if (c == NL) char_pos = 0;
+ }
+ else if (c == ANCHOR && (p == s || *(p + 1) == EOS)) c = NL;
+ if (c) putc(c, fp);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Figure out value of SDATA entity.
+ * We rememeber lookup hits in a "cache" (a shorter list), and look in
+ * cache before general list. Typically there will be LOTS of entries
+ * in the general list and only a handful in the hit list. Often, if an
+ * entity is used once, it'll be used again.
+ * Arguments:
+ * Pointer to SDATA entity token in ESIS.
+ * Return:
+ * Mapped value of the SDATA entity.
+ */
+
+char *
+LookupSDATA(
+ char *s
+)
+{
+ char *v;
+ static Map_t *Hits; /* remember lookup hits */
+
+ /* If we have a hit list, check it. */
+ if (Hits) {
+ if ((v = FindMappingVal(Hits, s))) return v;
+ }
+
+ v = FindMappingVal(SDATAmap, s);
+
+ /* If mapping found, remember it, then return it. */
+ if ((v = FindMappingVal(SDATAmap, s))) {
+ if (!Hits) Hits = NewMap(IMS_sdatacache);
+ SetMappingNV(Hits, s, v);
+ return v;
+ }
+
+ fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Add tag 'name' of length 'len' to list of tag names (if not there).
+ * This is a list of null-terminated strings so that we don't have to
+ * keep using the name length.
+ * Arguments:
+ * Pointer to element name (GI) to remember.
+ * Return:
+ * Pointer to the SAVED element name (GI).
+ */
+
+char *
+AddElemName(
+ char *name
+)
+{
+ int i;
+ static int n_alloc=0; /* number of slots allocated so far */
+
+ /* See if it's already in the list. */
+ for (i=0; i<nUsedElem; i++)
+ if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
+ return UsedElem[i];
+
+ /* Allocate slots in blocks of N, so we don't have to call malloc
+ * so many times. */
+ if (n_alloc == 0) {
+ n_alloc = IMS_elemnames;
+ Calloc(n_alloc, UsedElem, char *);
+ }
+ else if (nUsedElem >= n_alloc) {
+ n_alloc += IMS_elemnames;
+ Realloc(n_alloc, UsedElem, char *);
+ }
+ UsedElem[nUsedElem] = strdup(name);
+ return UsedElem[nUsedElem++];
+}
+/* ______________________________________________________________________ */
+/* Add attrib name to list of attrib names (if not there).
+ * This is a list of null-terminated strings so that we don't have to
+ * keep using the name length.
+ * Arguments:
+ * Pointer to attr name to remember.
+ * Return:
+ * Pointer to the SAVED attr name.
+ */
+
+char *
+AddAttName(
+ char *name
+)
+{
+ int i;
+ static int n_alloc=0; /* number of slots allocated so far */
+
+ /* See if it's already in the list. */
+ for (i=0; i<nUsedAtt; i++)
+ if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
+ return UsedAtt[i];
+
+ /* Allocate slots in blocks of N, so we don't have to call malloc
+ * so many times. */
+ if (n_alloc == 0) {
+ n_alloc = IMS_attnames;
+ Calloc(n_alloc, UsedAtt, char *);
+ }
+ else if (nUsedAtt >= n_alloc) {
+ n_alloc += IMS_attnames;
+ Realloc(n_alloc, UsedAtt, char *);
+ }
+ UsedAtt[nUsedAtt] = strdup(name);
+ return UsedAtt[nUsedAtt++];
+}
+
+/* ______________________________________________________________________ */
+/* Find an element's attribute value given element pointer and attr name.
+ * Typical use:
+ * a=FindAttByName("TYPE", t);
+ * do something with a->val;
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to attribute name.
+ * Return:
+ * Pointer to the value of the attribute.
+ */
+
+/*
+Mapping_t *
+FindAttByName(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ if (!e) return NULL;
+ for (i=0; i<e->natts; i++)
+ if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
+ return &(e->atts[i]);
+ return NULL;
+}
+*/
+
+char *
+FindAttValByName(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ if (!e) return NULL;
+ for (i=0; i<e->natts; i++)
+ if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
+ return e->atts[i].sval;
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Find context of a tag, 'levels' levels up the tree.
+ * Space for string is passed by caller.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Number of levels to look up tree.
+ * String to write path into (provided by caller).
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+char *
+FindContext(
+ Element_t *e,
+ int levels,
+ char *con
+)
+{
+ char *s;
+ Element_t *ep;
+ int i;
+
+ if (!e) return NULL;
+ s = con;
+ *s = EOS;
+ for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
+ if (i != 0) *s++ = ' ';
+ strcpy(s, ep->gi);
+ s += strlen(s);
+ }
+ return con;
+}
+
+
+/* ______________________________________________________________________ */
+/* Tests relationship (specified by argument/flag) between given element
+ * (structure pointer) and named element.
+ * Returns pointer to matching tag if found, null otherwise.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to name of elem whose relationsip we are trying to determine.
+ * Relationship we are testing.
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+Element_t *
+QRelation(
+ Element_t *e,
+ char *s,
+ Relation_t rel
+)
+{
+ int i;
+ Element_t *ep;
+
+ if (!e) return 0;
+
+ /* we'll call e the "given element" */
+ switch (rel)
+ {
+ case REL_Parent:
+ if (!e->parent || !e->parent->gi) return 0;
+ if (!strcmp(e->parent->gi, s)) return e->parent;
+ break;
+ case REL_Child:
+ for (i=0; i<e->necont; i++)
+ if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
+ break;
+ case REL_Ancestor:
+ if (!e->parent || !e->parent->gi) return 0;
+ for (ep=e->parent; ep; ep=ep->parent)
+ if (!strcmp(ep->gi, s)) return ep;
+ break;
+ case REL_Descendant:
+ if (e->necont == 0) return 0;
+ /* check immediate children first */
+ for (i=0; i<e->necont; i++)
+ if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
+ /* then children's children (recursively) */
+ for (i=0; i<e->necont; i++)
+ if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
+ return ep;
+ break;
+ case REL_Sibling:
+ if (!e->parent) return 0;
+ ep = e->parent;
+ for (i=0; i<ep->necont; i++)
+ if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
+ return ep->econt[i];
+ break;
+ case REL_Preceding:
+ if (!e->parent || e->my_eorder == 0) return 0;
+ ep = e->parent;
+ for (i=0; i<e->my_eorder; i++)
+ if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
+ break;
+ case REL_ImmPreceding:
+ if (!e->parent || e->my_eorder == 0) return 0;
+ ep = e->parent->econt[e->my_eorder-1];
+ if (!strcmp(s, ep->gi)) return ep;
+ break;
+ case REL_Following:
+ if (!e->parent || e->my_eorder == (e->parent->necont-1))
+ return 0; /* last? */
+ ep = e->parent;
+ for (i=(e->my_eorder+1); i<ep->necont; i++)
+ if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
+ break;
+ case REL_ImmFollowing:
+ if (!e->parent || e->my_eorder == (e->parent->necont-1))
+ return 0; /* last? */
+ ep = e->parent->econt[e->my_eorder+1];
+ if (!strcmp(s, ep->gi)) return ep;
+ break;
+ case REL_Cousin:
+ if (!e->parent) return 0;
+ /* Now, see if element's parent has that thing as a child. */
+ return QRelation(e->parent, s, REL_Child);
+ break;
+ case REL_None:
+ case REL_Unknown:
+ fprintf(stderr, "You can not query 'REL_None' or 'REL_Unknown'.\n");
+ break;
+ }
+ return NULL;
+}
+
+/* Given a relationship name (string), determine enum symbol for it.
+ * Arguments:
+ * Pointer to relationship name.
+ * Return:
+ * Relation_t enum.
+ */
+Relation_t
+FindRelByName(
+ char *relname
+)
+{
+ if (!strcmp(relname, "?")) {
+ fprintf(stderr, "Supported query/relationships %s\n%s.\n",
+ "child, parent, ancestor, descendant,",
+ "sibling, sibling+, sibling+1, sibling-, sibling-1");
+ return REL_None;
+ }
+ else if (StrEq(relname, "child")) return REL_Child;
+ else if (StrEq(relname, "parent")) return REL_Parent;
+ else if (StrEq(relname, "ancestor")) return REL_Ancestor;
+ else if (StrEq(relname, "descendant")) return REL_Descendant;
+ else if (StrEq(relname, "sibling")) return REL_Sibling;
+ else if (StrEq(relname, "sibling-")) return REL_Preceding;
+ else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
+ else if (StrEq(relname, "sibling+")) return REL_Following;
+ else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
+ else if (StrEq(relname, "cousin")) return REL_Cousin;
+ else fprintf(stderr, "Unknown relationship: %s\n", relname);
+ return REL_Unknown;
+}
+
+/* ______________________________________________________________________ */
+/* This will descend the element tree in-order. (enter_f)() is called
+ * upon entering the node. Then all children (data and child elements)
+ * are operated on, calling either DescendTree() with a pointer to
+ * the child element or (data_f)() for each non-element child node.
+ * Before leaving the node (ascending), (leave_f)() is called. enter_f
+ * and leave_f are passed a pointer to this node and data_f is passed
+ * a pointer to the data/content (which includes the data itself and
+ * type information). dp is an opaque pointer to any data the caller
+ * wants to pass.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to procedure to call when entering element.
+ * Pointer to procedure to call when leaving element.
+ * Pointer to procedure to call for each "chunk" of content data.
+ * Void data pointer, passed to the avobe 3 procedures.
+ */
+
+void
+DescendTree(
+ Element_t *e,
+ void (*enter_f)(),
+ void (*leave_f)(),
+ void (*data_f)(),
+ void *dp
+)
+{
+ int i;
+ if (enter_f) (enter_f)(e, dp);
+ for (i=0; i<e->ncont; i++) {
+ if (e->cont[i].type == CMD_OPEN)
+ DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
+ else
+ if (data_f) (data_f)(&e->cont[i], dp);
+ }
+ if (leave_f) (leave_f)(e, dp);
+}
+
+/* ______________________________________________________________________ */
+/* Add element, 'e', whose ID is 'idval', to a list of IDs.
+ * This makes it easier to find an element by ID later.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Element's ID attribute value (a string).
+ */
+
+void
+AddID(
+ Element_t *e,
+ char *idval
+)
+{
+ static ID_t *id_last;
+
+ if (!IDList) {
+ Malloc(1, id_last, ID_t);
+ IDList = id_last;
+ }
+ else {
+ Malloc(1, id_last->next, ID_t);
+ id_last = id_last->next;
+ }
+ id_last->elem = e;
+ id_last->id = idval;
+}
+
+/* ______________________________________________________________________ */
+/* Return pointer to element who's ID is given.
+ * Arguments:
+ * Element's ID attribute value (a string).
+ * Return:
+ * Pointer to element whose ID matches.
+ */
+
+Element_t *
+FindElemByID(
+ char *idval
+)
+{
+ ID_t *id;
+ for (id=IDList; id; id=id->next)
+ if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+
OpenPOWER on IntegriCloud