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 
154 #define IS_PREFIX_DELIMITER(x)	(strchr(" \t\n:=", (x)) != NULL)
155 
mdvi_do_special(DviContext * dvi,char * string)156 int	mdvi_do_special(DviContext *dvi, char *string)
157 {
158 	char	*prefix;
159 	char 	*ptr;
160 	DviSpecial *sp;
161 
162 	if(!registered_builtins) {
163 	}
164 
165 	if(!string || !*string)
166 		return 0;
167 
168 	/* skip leading spaces */
169 	while(*string && isspace(*string))
170 		string++;
171 
172 	DEBUG((DBG_SPECIAL, "Looking for a handler for `%s'\n", string));
173 
174 	/* now try to find a match */
175 	ptr = string;
176 	for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) {
177 #ifdef WITH_REGEX_SPECIALS
178 		if(sp->has_reg && !regexec(&sp->reg, ptr, 0, 0, 0))
179 			break;
180 #endif
181 		/* check the prefix */
182 		if(STRNCEQ(sp->prefix, ptr, sp->plen)) {
183 			ptr += sp->plen;
184 			break;
185 		}
186 	}
187 
188 	if(sp == NULL) {
189 		DEBUG((DBG_SPECIAL, "None found\n"));
190 		return -1;
191 	}
192 
193 	/* extract the prefix */
194 	if(ptr == string) {
195 		prefix = NULL;
196 		DEBUG((DBG_SPECIAL,
197 			"REGEX match with `%s' (arg `%s')\n",
198 			sp->label, ptr));
199 	} else {
200 		if(*ptr) *ptr++ = 0;
201 		prefix = string;
202 		DEBUG((DBG_SPECIAL,
203 			"PREFIX match with `%s' (prefix `%s', arg `%s')\n",
204 			sp->label, prefix, ptr));
205 	}
206 
207 	/* invoke the handler */
208 	sp->handler(dvi, prefix, ptr);
209 
210 	return 0;
211 }
212 
mdvi_flush_specials(void)213 void	mdvi_flush_specials(void)
214 {
215 	DviSpecial *sp, *list;
216 
217 
218 	for(list = (DviSpecial *)specials.head; (sp = list); ) {
219 		list = sp->next;
220 		if(sp->prefix) mdvi_free(sp->prefix);
221 		if(sp->label) mdvi_free(sp->label);
222 #ifdef WITH_REGEX_SPECIALS
223 		if(sp->has_reg)
224 			regfree(&sp->reg);
225 #endif
226 		mdvi_free(sp);
227 	}
228 	specials.head = NULL;
229 	specials.tail = NULL;
230 	specials.count = 0;
231 }
232 
233 /* some builtin specials */
234 
sp_layer(DviContext * dvi,const char * prefix,const char * arg)235 void	sp_layer(DviContext *dvi, const char *prefix, const char *arg)
236 {
237 	if(STREQ("push", arg))
238 		dvi->curr_layer++;
239 	else if(STREQ("pop", arg)) {
240 		if(dvi->curr_layer)
241 			dvi->curr_layer--;
242 		else
243 			mdvi_warning(_("%s: tried to pop top level layer\n"),
244 				     dvi->filename);
245 	} else if(STREQ("reset", arg))
246 		dvi->curr_layer = 0;
247 	DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer));
248 }
249 
250