freebsd_amp_hwpstate/usr.bin/sgmls/sgmlsasp/replace.c

469 lines
8.6 KiB
C

/* replace.c
Parse ASP style replacement file.
Written by James Clark (jjc@jclark.com). */
#include "sgmlsasp.h"
#include "replace.h"
#define TABLE_SIZE 251
struct table_entry {
enum event_type type;
char *gi;
struct replacement replacement;
struct table_entry *next;
};
struct replacement_table {
struct table_entry *table[TABLE_SIZE];
};
struct buffer {
char *s;
unsigned len;
unsigned size;
};
/* Tokens returned by get_token(). */
#define STRING 1
#define STAGO 2
#define ETAGO 3
#define PLUS 4
static int get P((void));
static int peek P((void));
static int get_token P((void));
static void scan_name P((struct buffer *, int));
static struct replacement *define_replacement
P((struct replacement_table *, enum event_type, char *));
static struct replacement_item **parse_string
P((struct replacement_item **, int));
static UNIV xmalloc P((unsigned));
static UNIV xrealloc P((UNIV, unsigned));
static struct replacement_item **add_replacement_data
P((struct replacement_item **, char *, unsigned));
static struct replacement_item **add_replacement_attr
P((struct replacement_item **, char *));
static int hash P((enum event_type, char *));
static NO_RETURN void parse_error VP((char *,...));
static VOID buffer_init P((struct buffer *));
static VOID buffer_append P((struct buffer *, int));
static char *buffer_extract P((struct buffer *));
#if 0
static VOID buffer_free P((struct buffer *));
#endif
#define buffer_length(buf) ((buf)->len)
#define NEW(type) ((type *)xmalloc(sizeof(type)))
static int current_lineno;
static char *current_file;
static FILE *fp;
struct replacement_table *make_replacement_table()
{
int i;
struct replacement_table *tablep;
tablep = NEW(struct replacement_table);
for (i = 0; i < TABLE_SIZE; i++)
tablep->table[i] = 0;
return tablep;
}
void load_replacement_file(tablep, file)
struct replacement_table *tablep;
char *file;
{
int tok;
struct buffer name;
buffer_init(&name);
errno = 0;
fp = fopen(file, "r");
if (!fp) {
if (errno)
error("can't open `%s': %s", file, strerror(errno));
else
error("can't open `%s'", file);
}
current_lineno = 1;
current_file = file;
tok = get_token();
while (tok != EOF) {
struct replacement *p;
struct replacement_item **tail;
enum event_type type;
if (tok != STAGO && tok != ETAGO)
parse_error("syntax error");
type = tok == STAGO ? START_ELEMENT : END_ELEMENT;
scan_name(&name, '>');
p = define_replacement(tablep, type, buffer_extract(&name));
tok = get_token();
if (tok == PLUS) {
if (p)
p->flags |= NEWLINE_BEGIN;
tok = get_token();
}
tail = p ? &p->items : 0;
while (tok == STRING) {
tail = parse_string(tail, type == START_ELEMENT);
tok = get_token();
}
if (tok == PLUS) {
if (p)
p->flags |= NEWLINE_END;
tok = get_token();
}
}
fclose(fp);
}
static
struct replacement_item **parse_string(tail, recog_attr)
struct replacement_item **tail;
int recog_attr;
{
struct buffer buf;
unsigned len;
buffer_init(&buf);
for (;;) {
int c = get();
if (c == '\"')
break;
if (recog_attr && c == '[') {
if (buffer_length(&buf)) {
len = buffer_length(&buf);
tail = add_replacement_data(tail, buffer_extract(&buf), len);
}
scan_name(&buf, ']');
tail = add_replacement_attr(tail, buffer_extract(&buf));
}
else {
if (c == '\\') {
c = get();
switch (c) {
case EOF:
parse_error("unfinished string at end of file");
case 's':
buffer_append(&buf, ' ');
break;
case 'n':
buffer_append(&buf, '\n');
break;
case 't':
buffer_append(&buf, '\t');
break;
case 'r':
buffer_append(&buf, '\r');
break;
case 'f':
buffer_append(&buf, '\f');
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int val = c - '0';
c = peek();
if ('0' <= c && c <= '7') {
(void)get();
val = val*8 + (c - '0');
c = peek();
if ('0' <= c && c <= '7') {
(void)get();
val = val*8 + (c - '0');
}
}
buffer_append(&buf, val);
break;
}
default:
buffer_append(&buf, c);
break;
}
}
else
buffer_append(&buf, c);
}
}
len = buffer_length(&buf);
if (len > 0)
tail = add_replacement_data(tail, buffer_extract(&buf), len);
return tail;
}
static
struct replacement_item **add_replacement_data(tail, buf, n)
struct replacement_item **tail;
char *buf;
unsigned n;
{
if (!tail)
free(buf);
else {
*tail = NEW(struct replacement_item);
(*tail)->type = DATA_REPL;
(*tail)->u.data.n = n;
(*tail)->next = 0;
(*tail)->u.data.s = buf;
tail = &(*tail)->next;
}
return tail;
}
static
struct replacement_item **add_replacement_attr(tail, name)
struct replacement_item **tail;
char *name;
{
if (!tail)
free(name);
else {
*tail = NEW(struct replacement_item);
(*tail)->type = ATTR_REPL;
(*tail)->next = 0;
(*tail)->u.attr = name;
tail = &(*tail)->next;
}
return tail;
}
static
int get_token()
{
int c;
for (;;) {
c = get();
while (isspace(c))
c = get();
if (c != '%')
break;
do {
c = get();
if (c == EOF)
return EOF;
} while (c != '\n');
}
switch (c) {
case '+':
return PLUS;
case '<':
c = peek();
if (c == '/') {
(void)get();
return ETAGO;
}
return STAGO;
case '"':
return STRING;
case EOF:
return EOF;
default:
parse_error("bad input character `%c'", c);
}
return EOF;
}
static
void scan_name(buf, term)
struct buffer *buf;
int term;
{
int c;
for (;;) {
c = get();
if (c == term)
break;
if (c == '\n' || c == EOF)
parse_error("missing `%c'", term);
if (fold_general_names) {
if (islower((unsigned char)c))
c = toupper((unsigned char)c);
}
buffer_append(buf, c);
}
if (buffer_length(buf) == 0)
parse_error("empty name");
buffer_append(buf, '\0');
}
static
int get()
{
int c = getc(fp);
if (c == '\n')
current_lineno++;
return c;
}
static
int peek()
{
int c = getc(fp);
if (c != EOF)
ungetc(c, fp);
return c;
}
struct replacement *lookup_replacement(tablep, type, name)
struct replacement_table *tablep;
enum event_type type;
char *name;
{
int h = hash(type, name);
struct table_entry *p;
for (p = tablep->table[h]; p; p = p->next)
if (strcmp(name, p->gi) == 0 && type == p->type)
return &p->replacement;
return 0;
}
/* Return 0 if already defined. */
static
struct replacement *define_replacement(tablep, type, name)
struct replacement_table *tablep;
enum event_type type;
char *name;
{
int h = hash(type, name);
struct table_entry *p;
for (p = tablep->table[h]; p; p = p->next)
if (strcmp(name, p->gi) == 0 && type == p->type)
return 0;
p = NEW(struct table_entry);
p->next = tablep->table[h];
tablep->table[h] = p;
p->type = type;
p->gi = name;
p->replacement.flags = 0;
p->replacement.items = 0;
return &p->replacement;
}
static
VOID buffer_init(buf)
struct buffer *buf;
{
buf->size = buf->len = 0;
buf->s = 0;
}
static
char *buffer_extract(buf)
struct buffer *buf;
{
char *s = buf->s;
buf->s = 0;
buf->len = 0;
buf->size = 0;
return s;
}
#if 0
static
VOID buffer_free(buf)
struct buffer *buf;
{
if (buf->s) {
free((UNIV)buf->s);
buf->s = 0;
buf->size = buf->size = 0;
}
}
#endif
static
VOID buffer_append(buf, c)
struct buffer *buf;
int c;
{
if (buf->len >= buf->size) {
if (!buf->size)
buf->s = (char *)xmalloc(buf->size = 10);
else
buf->s = (char *)xrealloc((UNIV)buf->s, buf->size *= 2);
}
buf->s[buf->len] = c;
buf->len += 1;
}
static
int hash(type, s)
enum event_type type;
char *s;
{
unsigned long h = 0, g;
while (*s != 0) {
h <<= 4;
h += *s++;
if ((g = h & 0xf0000000) != 0) {
h ^= g >> 24;
h ^= g;
}
}
h ^= (int)type;
return (int)(h % TABLE_SIZE);
}
static
UNIV xmalloc(n)
unsigned n;
{
UNIV p = (UNIV)malloc(n);
if (!p)
parse_error("out of memory");
return p;
}
static
UNIV xrealloc(p, size)
UNIV p;
unsigned size;
{
p = (UNIV)realloc(p, size);
if (!p)
parse_error("out of memory");
return p;
}
static NO_RETURN
#ifdef VARARGS
void parse_error(va_alist) va_dcl
#else
void parse_error(char *message,...)
#endif
{
char buf[512];
#ifdef VARARGS
char *message;
#endif
va_list ap;
#ifdef VARARGS
va_start(ap);
message = va_arg(ap, char *);
#else
va_start(ap, message);
#endif
vsprintf(buf, message, ap);
va_end(ap);
error("%s:%d: %s", current_file, current_lineno, buf);
}