/* 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); }