diff options
author | jfieber <jfieber@FreeBSD.org> | 1996-09-08 01:55:10 +0000 |
---|---|---|
committer | jfieber <jfieber@FreeBSD.org> | 1996-09-08 01:55:10 +0000 |
commit | 38d12975246b6eddde95da3643affaa704a15ce9 (patch) | |
tree | 0e70312e4c44553d986f47d7fdbe2d735fb990a3 /usr.bin | |
download | FreeBSD-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/Makefile | 15 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/README | 150 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/browse.c | 462 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/general.h | 329 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/hyper.c | 100 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/info.c | 300 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/instant.1 | 183 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/main.c | 710 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/tables.c | 2013 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/traninit.c | 577 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/translate.c | 881 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/translate.h | 153 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/transpec.5 | 526 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/tranvar.c | 757 | ||||
-rw-r--r-- | usr.bin/sgmls/instant/util.c | 1109 |
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; +} + +/* ______________________________________________________________________ */ + |