/* * Copyright (C) 2000, Matias Atria * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "mdvi.h" #include "private.h" #if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H) #include #endif typedef struct _DviSpecial { struct _DviSpecial *next; struct _DviSpecial *prev; char *label; char *prefix; size_t plen; #ifdef WITH_REGEX_SPECIALS regex_t reg; int has_reg; #endif DviSpecialHandler handler; } DviSpecial; static ListHead specials = {NULL, NULL, 0}; #define SPECIAL(x) \ void x __PROTO((DviContext *, const char *, const char *)) static SPECIAL(sp_layer); extern SPECIAL(epsf_special); extern SPECIAL(do_color_special); static struct { char *label; char *prefix; char *regex; DviSpecialHandler handler; } builtins[] = { {"Layers", "layer", NULL, sp_layer}, {"EPSF", "psfile", NULL, epsf_special} }; #define NSPECIALS (sizeof(builtins) / sizeof(builtins[0])) static int registered_builtins = 0; static void register_builtin_specials(void) { int i; ASSERT(registered_builtins == 0); for(i = 0; i < NSPECIALS; i++) mdvi_register_special( builtins[i].label, builtins[i].prefix, builtins[i].regex, builtins[i].handler, 1 /* replace if exists */); registered_builtins = 1; } static DviSpecial *find_special_prefix(const char *prefix) { DviSpecial *sp; /* should have a hash table here, but I'm so lazy */ for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) { if(STRCEQ(sp->prefix, prefix)) break; } return sp; } int mdvi_register_special(const char *label, const char *prefix, const char *regex, DviSpecialHandler handler, int replace) { DviSpecial *sp; int newsp = 0; if(!registered_builtins) register_builtin_specials(); sp = find_special_prefix(prefix); if(sp == NULL) { sp = xalloc(DviSpecial); sp->prefix = mdvi_strdup(prefix); newsp = 1; } else if(!replace) return -1; else { mdvi_free(sp->label); sp->label = NULL; } #ifdef WITH_REGEX_SPECIALS if(!newsp && sp->has_reg) { regfree(&sp->reg); sp->has_reg = 0; } if(regex && regcomp(&sp->reg, regex, REG_NOSUB) != 0) { if(newsp) { mdvi_free(sp->prefix); mdvi_free(sp); } return -1; } sp->has_reg = (regex != NULL); #endif sp->handler = handler; sp->label = mdvi_strdup(label); sp->plen = strlen(prefix); if(newsp) listh_prepend(&specials, LIST(sp)); DEBUG((DBG_SPECIAL, "New \\special handler `%s' with prefix `%s'\n", label, prefix)); return 0; } int mdvi_unregister_special(const char *prefix) { DviSpecial *sp; sp = find_special_prefix(prefix); if(sp == NULL) return -1; mdvi_free(sp->prefix); #ifdef WITH_REGEX_SPECIALS if(sp->has_reg) regfree(&sp->reg); #endif listh_remove(&specials, LIST(sp)); mdvi_free(sp); return 0; } #define IS_PREFIX_DELIMITER(x) (strchr(" \t\n:=", (x)) != NULL) int mdvi_do_special(DviContext *dvi, char *string) { char *prefix; char *ptr; DviSpecial *sp; if(!registered_builtins) { } if(!string || !*string) return 0; /* skip leading spaces */ while(*string && isspace(*string)) string++; DEBUG((DBG_SPECIAL, "Looking for a handler for `%s'\n", string)); /* now try to find a match */ ptr = string; for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) { #ifdef WITH_REGEX_SPECIALS if(sp->has_reg && !regexec(&sp->reg, ptr, 0, 0, 0)) break; #endif /* check the prefix */ if(STRNCEQ(sp->prefix, ptr, sp->plen)) { ptr += sp->plen; break; } } if(sp == NULL) { DEBUG((DBG_SPECIAL, "None found\n")); return -1; } /* extract the prefix */ if(ptr == string) { prefix = NULL; DEBUG((DBG_SPECIAL, "REGEX match with `%s' (arg `%s')\n", sp->label, ptr)); } else { if(*ptr) *ptr++ = 0; prefix = string; DEBUG((DBG_SPECIAL, "PREFIX match with `%s' (prefix `%s', arg `%s')\n", sp->label, prefix, ptr)); } /* invoke the handler */ sp->handler(dvi, prefix, ptr); return 0; } void mdvi_flush_specials(void) { DviSpecial *sp, *list; for(list = (DviSpecial *)specials.head; (sp = list); ) { list = sp->next; if(sp->prefix) mdvi_free(sp->prefix); if(sp->label) mdvi_free(sp->label); #ifdef WITH_REGEX_SPECIALS if(sp->has_reg) regfree(&sp->reg); #endif mdvi_free(sp); } specials.head = NULL; specials.tail = NULL; specials.count = 0; } /* some builtin specials */ void sp_layer(DviContext *dvi, const char *prefix, const char *arg) { if(STREQ("push", arg)) dvi->curr_layer++; else if(STREQ("pop", arg)) { if(dvi->curr_layer) dvi->curr_layer--; else warning(_("%s: tried to pop top level layer\n"), dvi->filename); } else if(STREQ("reset", arg)) dvi->curr_layer = 0; DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer)); }