1 /*
2  *	*rc file parser
3  *	Copyright
4  *		(C) 1992 Joseph H. Allen;
5  *
6  *	This file is part of JOE (Joe's Own Editor)
7  */
8 #include "types.h"
9 
10 /* Validate joerc file */
11 
validate_rc()12 int validate_rc()
13 {
14 	KMAP *k;
15 	if (!(k = ngetcontext("main")) || kmap_empty(k)) {
16 		logerror_0(joe_gettext(_("Missing or empty :main keymap\n")));
17 		return -1;
18 	}
19 
20 	if (!(k = ngetcontext("prompt")) || kmap_empty(k)) {
21 		logerror_0(joe_gettext(_("Missing or empty :prompt keymap\n")));
22 		return -1;
23 	}
24 
25 	if (!(k = ngetcontext("query")) || kmap_empty(k)) {
26 		logerror_0(joe_gettext(_("Missing or empty :query keymap\n")));
27 		return -1;
28 	}
29 
30 	if (!(k = ngetcontext("querya")) || kmap_empty(k)) {
31 		logerror_0(joe_gettext(_("Missing or empty :querya keymap\n")));
32 		return -1;
33 	}
34 
35 	if (!(k = ngetcontext("querysr")) || kmap_empty(k)) {
36 		logerror_0(joe_gettext(_("Missing or empty :querysr keymap\n")));
37 		return -1;
38 	}
39 
40 	if (!(k = ngetcontext("shell")) || kmap_empty(k)) {
41 		logerror_0(joe_gettext(_("Missing or empty :shell keymap\n")));
42 	}
43 
44 	if (!(k = ngetcontext("vtshell")) || kmap_empty(k)) {
45 		logerror_0(joe_gettext(_("Missing or empty :vtshell keymap\n")));
46 	}
47 
48 	return 0;
49 }
50 
51 /* Parse a macro- allow it to cross lines */
52 
multiparse(JFILE * fd,int * refline,char * buf,ptrdiff_t * ofst,int * referr,char * name)53 static MACRO *multiparse(JFILE *fd, int *refline, char *buf, ptrdiff_t *ofst, int *referr, char *name)
54 {
55 	MACRO *m;
56 	ptrdiff_t x = *ofst;
57 	int err = *referr;
58 	int line = *refline;
59 	m = 0;
60 	for (;;) {
61 		m = mparse(m, buf + x, &x, 0);
62 		if (x == -1) { /* Error */
63 			err = -1;
64 			logerror_2(joe_gettext(_("%s %d: Unknown command in macro\n")), name, line);
65 			break;
66 		} else if (x == -2) { /* Get more input */
67 			jfgets(buf, 1024, fd);
68 			++line;
69 			x = 0;
70 		} else /* We're done */
71 			break;
72 	}
73 	*referr = err;
74 	*refline = line;
75 	*ofst = x;
76 	return m;
77 }
78 
79 /* Process rc file
80  * Returns 0 if the rc file was successfully processed
81  *        -1 if the rc file couldn't be opened
82  *         1 if there was a syntax error in the file
83  */
84 
procrc(CAP * cap,char * name)85 int procrc(CAP *cap, char *name)
86 {
87 	OPTIONS *o = &fdefault;	/* Current options */
88 	KMAP *context = NULL;	/* Current context */
89 	struct rc_menu *current_menu = NULL;
90 	char buf[1024];	/* Input buffer */
91 	char buf1[1024];	/* Input buffer */
92 	JFILE *fd;		/* rc file */
93 	int line = 0;		/* Line number */
94 	int err = 0;		/* Set to 1 if there was a syntax error */
95 
96 	zlcpy(buf, SIZEOF(buf), name);
97 #ifdef __MSDOS__
98 	fd = jfopen(buf, "rt");
99 #else
100 	fd = jfopen(buf, "r");
101 #endif
102 
103 	if (!fd)
104 		return -1;	/* Return if we couldn't open the rc file */
105 
106 	logmessage_1(joe_gettext(_("Processing '%s'...\n")), name);
107 
108 	while (jfgets(buf, SIZEOF(buf), fd)) {
109 		line++;
110 		switch (buf[0]) {
111 		case ' ':
112 		case '\t':
113 		case '\n':
114 		case '\f':
115 		case 0:
116 			break;	/* Skip comment lines */
117 
118 		case '[':	/* Select file types for file-type dependent options */
119 			{
120 				int x;
121 
122 				o = (OPTIONS *)joe_malloc(SIZEOF(OPTIONS));
123 				*o = fdefault;
124 				o->match = 0;
125 				for (x = 0; buf[x] && buf[x] != ']' && buf[x] != '\r' && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t'; ++x) ;
126 				buf[x] = 0;
127 				o->next = options_list;
128 				options_list = o;
129 				o->ftype = zdup(buf + 1);
130 			}
131 			break;
132 		case '*':	/* Select file types for file-type dependent options */
133 			{ /* Space and tab introduce comments- which means we can't have them in the regex */
134 				if (o) {
135 					struct options_match *m;
136 					int x;
137 					for (x = 0; buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t'; ++x) ;
138 					buf[x] = 0;
139 					m = (struct options_match *)joe_malloc(SIZEOF(struct options_match));
140 					m->next = 0;
141 					m->name_regex = zdup(buf);
142 					m->contents_regex = 0;
143 					m->r_contents_regex = 0;
144 					m->next = o->match;
145 					o->match = m;
146 				}
147 			}
148 			break;
149 		case '+':	/* Set file contents match regex */
150 			{ /* No comments allowed- entire line used. */
151 				int x;
152 
153 				for (x = 0; buf[x] && buf[x] != '\n' && buf[x] != '\r'; ++x) ;
154 				buf[x] = 0;
155 				if (o && o->match) {
156 					if (o->match->contents_regex) {
157 						struct options_match *m = (struct options_match *)joe_malloc(SIZEOF(struct options_match));
158 						*m = *o->match;
159 						m->next = o->match;
160 						o->match = m;
161 					}
162 					o->match->contents_regex = zdup(buf+1);
163 				}
164 			}
165 			break;
166 		case '-':	/* Set an option */
167 			{ /* parse option and arg.  arg goes to end of line.  This is bad. */
168 				char *opt = buf + 1;
169 				int x;
170 				char *arg = NULL;
171 
172 				for (x = 0; buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t'; ++x) ;
173 				if (buf[x] && buf[x] != '\n') {
174 					buf[x] = 0;
175 					for (arg = buf + ++x; buf[x] && buf[x] != '\n'; ++x) ;
176 				}
177 				buf[x] = 0;
178 				if (!glopt(opt, arg, o, 2)) {
179 					err = 1;
180 					logerror_3(joe_gettext(_("%s %d: Unknown option %s\n")), name, line, opt);
181 				}
182 			}
183 			break;
184 		case '{':	/* Process help text.  No comment allowed after {name */
185 			{	/* everything after } is ignored. */
186 				line = help_init(fd,buf,line);
187 			}
188 			break;
189 		case ':':	/* Select context */
190 			{
191 				ptrdiff_t x, c;
192 				char ch;
193 
194 				for (x = 1; !joe_isspace_eos(locale_map,buf[x]); ++x) ;
195 				ch = buf[x];
196 				buf[x] = 0;
197 				if (x != 1)
198 					if (!zcmp(buf + 1, "def")) {
199 						ptrdiff_t y;
200 
201 						for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
202 						for (y = x; !joe_isspace_eos(locale_map,buf[y]); ++y) ;
203 						ch = buf[y];
204 						buf[y] = 0;
205 						zlcpy(buf1, SIZEOF(buf1), buf + x);
206 						if (y != x) {
207 							ptrdiff_t sta = y + 1;
208 							MACRO *m;
209 
210 							if (joe_isblank(locale_map,ch)
211 							    && (m = multiparse(fd, &line, buf, &sta, &err, name)))
212 								addcmd(buf1, m);
213 							else {
214 								err = 1;
215 								logerror_2(joe_gettext(_("%s %d: macro missing from :def\n")), name, line);
216 							}
217 						} else {
218 							err = 1;
219 							logerror_2(joe_gettext(_("%s %d: command name missing from :def\n")), name, line);
220 						}
221 					} else if (!zcmp(buf + 1, "inherit")) {
222 						if (context) {
223 							for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
224 							for (c = x; !joe_isspace_eos(locale_map,buf[c]); ++c) ;
225 							buf[c] = 0;
226 							if (c != x)
227 								kcpy(context, kmap_getcontext(buf + x));
228 							else {
229 								err = 1;
230 								logerror_2(joe_gettext(_("%s %d: context name missing from :inherit\n")), name, line);
231 							}
232 						} else {
233 							err = 1;
234 							logerror_2(joe_gettext(_("%s %d: No context selected for :inherit\n")), name, line);
235 						}
236 					} else if (!zcmp(buf + 1, "include")) {
237 						for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
238 						for (c = x; !joe_isspace_eos(locale_map,buf[c]); ++c) ;
239 						buf[c] = 0;
240 						if (c != x) {
241 							char bf[1024];
242 							char *p = getenv("HOME");
243 							int rtn = -1;
244 							bf[0] = 0;
245 							if (p && buf[x] != '/') {
246 								joe_snprintf_2(bf,SIZEOF(bf),"%s/.joe/%s",p,buf + x);
247 								rtn = procrc(cap, bf);
248 							}
249 							if (rtn == -1 && buf[x] != '/') {
250 								joe_snprintf_2(bf,SIZEOF(bf),"%s%s",JOERC,buf + x);
251 								rtn = procrc(cap, bf);
252 							}
253 							if (rtn == -1 && buf[x] != '/') {
254 								joe_snprintf_1(bf,SIZEOF(bf),"*%s",buf + x);
255 								rtn = procrc(cap, bf);
256 							}
257 							if (rtn == -1 && buf[x] == '/') {
258 								joe_snprintf_1(bf,SIZEOF(bf),"%s",buf + x);
259 								rtn = procrc(cap, bf);
260 							}
261 							switch (rtn) {
262 							case 1:
263 								err = 1;
264 								break;
265 							case -1:
266 								logerror_3(joe_gettext(_("%s %d: Couldn't open %s\n")), name, line, bf);
267 								err = 1;
268 								break;
269 							}
270 							context = 0;
271 							o = &fdefault;
272 						} else {
273 							err = 1;
274 							logerror_2(joe_gettext(_("%s %d: :include missing file name\n")), name, line);
275 						}
276 					} else if (!zcmp(buf + 1, "delete")) {
277 						if (context) {
278 							ptrdiff_t y;
279 
280 							for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
281 							for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1]
282 															!= ' '); ++y) ;
283 							buf[y] = 0;
284 							kdel(context, buf + x);
285 						} else {
286 							err = 1;
287 							logerror_2(joe_gettext(_("%s %d: No context selected for :delete\n")), name, line);
288 						}
289 					} else if (!zcmp(buf + 1, "defmap")) {
290 						for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
291 						for (c = x; !joe_isspace_eos(locale_map,buf[c]); ++c) ;
292 						buf[c] = 0;
293 						if (c != x) {
294 							context = kmap_getcontext(buf + x);
295 							current_menu = 0;
296 						} else {
297 							err = 1;
298 							logerror_2(joe_gettext(_("%s %d: :defmap missing name\n")), name, line);
299 						}
300 					} else if (!zcmp(buf + 1, "defmenu")) {
301 						MACRO *m = 0;
302 						char d;
303 						ptrdiff_t y;
304 						for (buf[x] = ch; joe_isblank(locale_map,buf[x]); ++x) ;
305 						for (c = x; !joe_isspace_eos(locale_map,buf[c]); ++c) ;
306 						d = buf[c];
307 						buf[c] = 0;
308 						zlcpy(buf1, SIZEOF(buf1), buf + x);
309 						buf[c] = d;
310 						for (y = c; joe_isblank(locale_map, buf[y]); ++y);
311 						if (!joe_isspace_eos(locale_map, buf[y]))
312 							m = multiparse(fd, &line, buf, &y, &err, name);
313 						current_menu = create_menu(buf1, m);
314 						context = 0;
315 					} else {
316 						context = kmap_getcontext(buf + 1);
317 						current_menu = 0;
318 						/* err = 1;
319 						logerror_2(joe_gettext(_("%s %d: unknown :command\n")), name, line);*/
320 					}
321 				else {
322 					err = 1;
323 					logerror_2(joe_gettext(_("%s %d: Invalid context name\n")), name, line);
324 				}
325 			}
326 			break;
327 		default:	/* Get key-sequence to macro binding */
328 			{
329 				ptrdiff_t x, y;
330 				MACRO *m;
331 
332 				if (!context && !current_menu) {
333 					err = 1;
334 					logerror_2(joe_gettext(_("%s %d: No context selected for macro to key-sequence binding\n")), name, line);
335 					break;
336 				}
337 
338 				x = 0;
339 				m = multiparse(fd, &line, buf, &x, &err, name);
340 				if (x == -1)
341 					break;
342 				if (!m)
343 					break;
344 
345 				/* Skip to end of key sequence */
346 				for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1] != ' '); ++y) ;
347 				buf[y] = 0;
348 
349 				if (current_menu) {
350 					/* Add menu entry */
351 					add_menu_entry(current_menu, buf + x, m);
352 				} else {
353 					/* Add binding to context */
354 					if (kadd(cap, context, buf + x, m) == -1) {
355 						logerror_3(joe_gettext(_("%s %d: Bad key sequence '%s'\n")), name, line, buf + x);
356 						err = 1;
357 					}
358 				}
359 			}
360 			break;
361 		}
362 	}
363 	jfclose(fd);		/* Close rc file */
364 
365 	/* Print proper ending string */
366 	logmessage_1(joe_gettext(_("Finished processing %s\n")), name);
367 
368 	return err;		/* 0 for success, 1 for syntax error */
369 }
370