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