1110 lines
31 KiB
C
1110 lines
31 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|
|
/* ______________________________________________________________________ */
|
|
|