1 /*
2 * Copyright (C) 2000, Matias Atria
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <config.h>
20 #include <ctype.h>
21 #include <string.h>
22
23 #include "mdvi.h"
24 #include "private.h"
25
26 #if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H)
27 #include <regex.h>
28 #endif
29
30 typedef struct _DviSpecial {
31 struct _DviSpecial *next;
32 struct _DviSpecial *prev;
33 char *label;
34 char *prefix;
35 size_t plen;
36 #ifdef WITH_REGEX_SPECIALS
37 regex_t reg;
38 int has_reg;
39 #endif
40 DviSpecialHandler handler;
41 } DviSpecial;
42
43 static ListHead specials = {NULL, NULL, 0};
44
45 #define SPECIAL(x) \
46 void x __PROTO((DviContext *, const char *, const char *))
47
48 static SPECIAL(sp_layer);
49 extern SPECIAL(epsf_special);
50 extern SPECIAL(do_color_special);
51
52 static struct {
53 char *label;
54 char *prefix;
55 char *regex;
56 DviSpecialHandler handler;
57 } builtins[] = {
58 {"Layers", "layer", NULL, sp_layer},
59 {"EPSF", "psfile", NULL, epsf_special}
60 };
61 #define NSPECIALS (sizeof(builtins) / sizeof(builtins[0]))
62 static int registered_builtins = 0;
63
register_builtin_specials(void)64 static void register_builtin_specials(void)
65 {
66 int i;
67
68 ASSERT(registered_builtins == 0);
69 registered_builtins = 1;
70 for(i = 0; i < NSPECIALS; i++)
71 mdvi_register_special(
72 builtins[i].label,
73 builtins[i].prefix,
74 builtins[i].regex,
75 builtins[i].handler,
76 1 /* replace if exists */);
77 }
78
find_special_prefix(const char * prefix)79 static DviSpecial *find_special_prefix(const char *prefix)
80 {
81 DviSpecial *sp;
82
83 /* should have a hash table here, but I'm so lazy */
84 for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) {
85 if(STRCEQ(sp->prefix, prefix))
86 break;
87 }
88 return sp;
89 }
90
mdvi_register_special(const char * label,const char * prefix,const char * regex,DviSpecialHandler handler,int replace)91 int mdvi_register_special(const char *label, const char *prefix,
92 const char *regex, DviSpecialHandler handler, int replace)
93 {
94 DviSpecial *sp;
95 int newsp = 0;
96
97 if(!registered_builtins)
98 register_builtin_specials();
99
100 sp = find_special_prefix(prefix);
101 if(sp == NULL) {
102 sp = xalloc(DviSpecial);
103 sp->prefix = mdvi_strdup(prefix);
104 newsp = 1;
105 } else if(!replace)
106 return -1;
107 else {
108 mdvi_free(sp->label);
109 sp->label = NULL;
110 }
111
112 #ifdef WITH_REGEX_SPECIALS
113 if(!newsp && sp->has_reg) {
114 regfree(&sp->reg);
115 sp->has_reg = 0;
116 }
117 if(regex && regcomp(&sp->reg, regex, REG_NOSUB) != 0) {
118 if(newsp) {
119 mdvi_free(sp->prefix);
120 mdvi_free(sp);
121 }
122 return -1;
123 }
124 sp->has_reg = (regex != NULL);
125 #endif
126 sp->handler = handler;
127 sp->label = mdvi_strdup(label);
128 sp->plen = strlen(prefix);
129 if(newsp)
130 listh_prepend(&specials, LIST(sp));
131 DEBUG((DBG_SPECIAL,
132 "New \\special handler `%s' with prefix `%s'\n",
133 label, prefix));
134 return 0;
135 }
136
mdvi_unregister_special(const char * prefix)137 int mdvi_unregister_special(const char *prefix)
138 {
139 DviSpecial *sp;
140
141 sp = find_special_prefix(prefix);
142 if(sp == NULL)
143 return -1;
144 mdvi_free(sp->prefix);
145 #ifdef WITH_REGEX_SPECIALS
146 if(sp->has_reg)
147 regfree(&sp->reg);
148 #endif
149 listh_remove(&specials, LIST(sp));
150 mdvi_free(sp);
151 return 0;
152 }
153
mdvi_do_special(DviContext * dvi,char * string)154 int mdvi_do_special(DviContext *dvi, char *string)
155 {
156 char *prefix;
157 char *ptr;
158 DviSpecial *sp;
159
160 if(!registered_builtins) {
161 }
162
163 if(!string || !*string)
164 return 0;
165
166 /* skip leading spaces */
167 while(*string && isspace(*string))
168 string++;
169
170 DEBUG((DBG_SPECIAL, "Looking for a handler for `%s'\n", string));
171
172 /* now try to find a match */
173 ptr = string;
174 for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) {
175 #ifdef WITH_REGEX_SPECIALS
176 if(sp->has_reg && !regexec(&sp->reg, ptr, 0, 0, 0))
177 break;
178 #endif
179 /* check the prefix */
180 if(STRNCEQ(sp->prefix, ptr, sp->plen)) {
181 ptr += sp->plen;
182 break;
183 }
184 }
185
186 if(sp == NULL) {
187 DEBUG((DBG_SPECIAL, "None found\n"));
188 return -1;
189 }
190
191 /* extract the prefix */
192 if(ptr == string) {
193 prefix = NULL;
194 DEBUG((DBG_SPECIAL,
195 "REGEX match with `%s' (arg `%s')\n",
196 sp->label, ptr));
197 } else {
198 if(*ptr) *ptr++ = 0;
199 prefix = string;
200 DEBUG((DBG_SPECIAL,
201 "PREFIX match with `%s' (prefix `%s', arg `%s')\n",
202 sp->label, prefix, ptr));
203 }
204
205 /* invoke the handler */
206 sp->handler(dvi, prefix, ptr);
207
208 return 0;
209 }
210
mdvi_flush_specials(void)211 void mdvi_flush_specials(void)
212 {
213 DviSpecial *sp, *list;
214
215
216 for(list = (DviSpecial *)specials.head; (sp = list); ) {
217 list = sp->next;
218 if(sp->prefix) mdvi_free(sp->prefix);
219 if(sp->label) mdvi_free(sp->label);
220 #ifdef WITH_REGEX_SPECIALS
221 if(sp->has_reg)
222 regfree(&sp->reg);
223 #endif
224 mdvi_free(sp);
225 }
226 specials.head = NULL;
227 specials.tail = NULL;
228 specials.count = 0;
229 }
230
231 /* some builtin specials */
232
sp_layer(DviContext * dvi,const char * prefix,const char * arg)233 void sp_layer(DviContext *dvi, const char *prefix, const char *arg)
234 {
235 if(STREQ("push", arg))
236 dvi->curr_layer++;
237 else if(STREQ("pop", arg)) {
238 if(dvi->curr_layer)
239 dvi->curr_layer--;
240 else
241 mdvi_warning(_("%s: tried to pop top level layer\n"),
242 dvi->filename);
243 } else if(STREQ("reset", arg))
244 dvi->curr_layer = 0;
245 DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer));
246 }
247
248