1 /* @(#)macro.c 1.42 09/07/13 Copyright 1984-2009 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)macro.c 1.42 09/07/13 Copyright 1984-2009 J. Schilling";
6 #endif
7 /*
8 * The macro package for VED
9 *
10 * Copyright (c) 1984-2009 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 /*
27 * If a macro is found, it is loaded into 'macstr' and then read from 'macstr'
28 * and executed character by character.
29 * A maximum recursion depth of M_DEPTH macros can be executed at a time.
30 *
31 * The following external routines are available:
32 *
33 * macro_init - Init the package and load the macro file.
34 * vxmac - Look for a macro and push it on execution stack.
35 * Also sets 'mflag' as an intication that 'gmacro()'
36 * should be used to read further characters.
37 * vmac - Load the temporary macro on the execution stack
38 * gmacro - Get the next character from the macro execution stack
39 * Returns '0' and resets 'mflag' at the end of a
40 * translation.
41 * vemac - Edit the macro file from VED and re-init macro pakage
42 * macro_reinit - Check if the macro package should be re-initialized.
43 * Do the re-initialization if needed.
44 *
45 * The global 'mflag' is used in io.c to know whether the input should be
46 * taken from the macro execution stack.
47 */
48
49 #include "ved.h"
50 #include <schily/string.h>
51 #include <schily/errno.h>
52 #include <schily/pwd.h>
53
54 #define M_NAMELEN 256
55 #define M_STRINGLEN 8192
56 #define M_DEPTH 10
57
58 extern int mflag;
59
60 LOCAL int mac_init = 0;
61 LOCAL int mac_level = 0;
62
63 typedef struct {
64 char *m_str;
65 char *m_start;
66 int m_mult;
67 } macs_t;
68
69 LOCAL macs_t macs[M_DEPTH];
70
71 LOCAL char *macstr;
72 LOCAL char *mac_start;
73 LOCAL char *home;
74
75 typedef struct m_node mac_t;
76
77 struct m_node {
78 mac_t *m_next;
79 char *m_name;
80 char *m_string;
81 };
82
83 LOCAL mac_t *first_macro;
84
85 EXPORT char * myhome __PR((void));
86 EXPORT void macro_init __PR((ewin_t *wp));
87 EXPORT void vxmac __PR((ewin_t *wp));
88 EXPORT void vmac __PR((ewin_t *wp));
89 EXPORT int gmacro __PR((void));
90 EXPORT void vemac __PR((ewin_t *wp));
91 EXPORT void macro_reinit __PR((ewin_t *wp));
92 LOCAL void add_node __PR((char *mn, char *ms));
93 LOCAL char * get_macro __PR((ewin_t *wp, int c));
94
95 /*
96 * Try to get the user's home directory.
97 */
98 EXPORT char *
myhome()99 myhome()
100 {
101 #ifndef JOS
102 struct passwd *pw;
103 #else
104 char *fields[7];
105 extern char *getuname();
106 #endif
107
108 if (home)
109 return (home);
110
111 if ((home = getenv("HOME")) == NULL) {
112 #ifdef JOS
113 if (findline("/etc/passwd", ':',
114 getuname(getuid()), 0, fields, 6) == 1)
115 home = fields[5];
116 #else
117 if ((pw = getpwuid(getuid())) != NULL)
118 home = pw->pw_dir;
119 #endif
120 else
121 home = "/tmp";
122 }
123 return (home);
124 }
125
126 /*
127 * Initialize the macro package and load the macro file.
128 */
129 EXPORT void
macro_init(wp)130 macro_init(wp)
131 ewin_t *wp;
132 {
133 register FILE *f;
134 register int state; /* 0 == name, 1 == string, 2 == comment */
135 register int n;
136 register char *s;
137 register char lc;
138 register int c;
139 char fname[1024];
140 char m_name[M_NAMELEN + 1];
141 char m_string[M_STRINGLEN + 1];
142
143 first_macro = 0;
144
145 snprintf(fname, sizeof (fname), "%s%s", myhome(), "/.vedmac");
146 if ((f = fileopen(fname, "rb")) == (FILE *) NULL) {
147 if (geterrno() == ENOENT)
148 return;
149 errmsg("Can not open '%s'.\r\n", fname);
150 sleep(1);
151 return;
152 }
153
154 state = 0; n = M_NAMELEN; lc = 0;
155 s = m_name;
156 for (;;) {
157 c = getc(f);
158 if (c == EOF)
159 break;
160
161 if (state == 0 && c == '\n') { /* empty line / ln without : */
162 if (s != m_name) {
163 *s = '\0';
164 writeerr(wp, "%.8s Bad macro name.", m_name);
165 sleep(1);
166 lc = 0;
167 n = M_NAMELEN;
168 s = m_name;
169 }
170 continue;
171 }
172
173 if (n-- < 0) {
174 *s = '\0';
175 writeerr(wp, "%.8s: Bad macro form.", m_name);
176 sleep(1);
177 /*
178 * Eat rest of line.
179 */
180 while ((c = getc(f)) != EOF) {
181 if (c == '\n')
182 break;
183 }
184 lc = 0; s = m_name; n = M_NAMELEN; state = 0;
185 continue;
186 }
187
188 if (state == 2) { /* comment */
189 if (c == '\n') {
190 lc = 0; s = m_name; n = M_NAMELEN; state = 0;
191 }
192 continue;
193 }
194 if (c == ':') {
195 if (lc != '\\') {
196 *s = 0;
197 if (state == 0)
198 s = m_string;
199 else
200 add_node(m_name, m_string);
201 n = M_STRINGLEN; state++;
202 continue;
203 } else {
204 s--; *s++ = lc = (char)c;
205 }
206 } else {
207 *s++ = lc = (char)c;
208 }
209 }
210 fclose(f);
211 }
212
213 /*
214 * Look for macro and push it on the macro execution stack if found.
215 */
216 EXPORT void
vxmac(wp)217 vxmac(wp)
218 ewin_t *wp;
219 {
220 char *mac;
221
222 wp->eflags &= ~COLUPDATE;
223 if (mac_level >= M_DEPTH) {
224 /*
225 * Overflow on recursion depth.
226 */
227 writeerr(wp, "MACRO ABORTED");
228 sleep(1);
229 return;
230 }
231 if ((mac = get_macro(wp, wp->lastch)) == NULL) {
232 ringbell();
233 } else {
234 macs[mac_level].m_str = macstr;
235 macs[mac_level].m_start = mac_start;
236 macs[mac_level++].m_mult = mflag;
237 mac_start = macstr = mac;
238 mflag = wp->curnum;
239 }
240 }
241
242 /*
243 * Execute the temporary macro (set by ESC : macro ... )
244 */
245 EXPORT void
vmac(wp)246 vmac(wp)
247 ewin_t *wp;
248 {
249 extern char mstr[];
250
251 wp->eflags &= ~COLUPDATE;
252 if (mstr[0] == '\0') {
253 ringbell();
254 } else {
255 macs[mac_level].m_str = macstr;
256 macs[mac_level].m_start = mac_start;
257 macs[mac_level++].m_mult = mflag;
258 mac_start = macstr = mstr;
259 mflag = wp->curnum;
260 }
261 }
262
263 /*
264 * Get the next character from the macro replacement string.
265 * Handle recursion properly.
266 */
267 EXPORT int
gmacro()268 gmacro()
269 {
270 char c;
271
272 if ((c = *macstr++) == 0) {
273 mflag--;
274 if (mflag > 0) {
275 macstr = mac_start;
276 c = *macstr++;
277 } else if (mac_level > 0) {
278 macstr = macs[--mac_level].m_str;
279 mac_start = macs[mac_level].m_start;
280 mflag = macs[mac_level].m_mult;
281 if (mac_level)
282 return (gmacro());
283 }
284 }
285 return ((Uchar)c);
286 }
287
288 /*
289 * Execute "ved -e $(HOME)/.vedmac"
290 * Remember to re-init the macro package later to reflect changes.
291 */
292 /* ARGSUSED */
293 EXPORT void
vemac(wp)294 vemac(wp)
295 ewin_t *wp;
296 {
297 static char command[129];
298
299 mflag = 1;
300 /*
301 * XXX What will happen if we have EBCDIC ?
302 * XXX \015 was ^M before we port to Mac OS X Darwin
303 */
304 snprintf(command, sizeof (command), "ved -e %s%s",
305 home, "/.vedmac\015n");
306 macstr = command;
307 mac_init = 1;
308 }
309
310 /*
311 * RE-initialize the macro package if needed.
312 * Called from execcmds.c
313 */
314 EXPORT void
macro_reinit(wp)315 macro_reinit(wp)
316 ewin_t *wp;
317 {
318 if (mac_init == 1) {
319 macro_init(wp);
320 mac_init = 0;
321 }
322 }
323
324 /*
325 * Add a new macro to the list of known macros.
326 */
327 LOCAL void
add_node(mn,ms)328 add_node(mn, ms)
329 char *mn;
330 char *ms;
331 {
332 register mac_t *np;
333 register mac_t *tn;
334 register mac_t *last;
335 register int cmp;
336 register int ln;
337 register int ls;
338
339 /*
340 * First create and init new macro node.
341 */
342 tn = (mac_t *) malloc(sizeof (*tn));
343 if (tn == (mac_t *)NULL)
344 return;
345 ln = strlen(mn);
346 ls = strlen(ms);
347 if ((tn->m_name = malloc(ln+1)) == NULL) {
348 free(tn);
349 return;
350 }
351 if ((tn->m_string = malloc(ls+1)) == NULL) {
352 free(tn->m_name);
353 free(tn);
354 return;
355 }
356 *movebytes(mn, tn->m_name, ln) = '\0';
357 *movebytes(ms, tn->m_string, ls) = '\0';
358 tn->m_next = NULL;
359
360 if (first_macro == NULL) {
361 first_macro = tn;
362 return;
363 }
364
365 /*
366 * Insert new macro in order.
367 */
368 np = last = first_macro;
369 for (; ; np = np->m_next) {
370
371 if (np == NULL) {
372 /*
373 * Append to end of list
374 */
375 last->m_next = tn;
376 return;
377 }
378
379 cmp = strcmp(mn, np->m_name);
380
381 if (cmp == 0) {
382 /*
383 * Macro is already defined
384 */
385 free(tn->m_name);
386 free(tn->m_string);
387 free(tn);
388 return;
389 }
390 if (cmp < 0) {
391 if (first_macro == np) {
392 /*
393 * Make it the first in list.
394 */
395 tn->m_next = first_macro;
396 first_macro = tn;
397 return;
398 } else {
399 /*
400 * Insert in list
401 */
402 last->m_next = tn;
403 tn->m_next = np;
404 return;
405 }
406 }
407 last = np;
408 }
409 }
410
411 /*
412 * Do a lookup for a macro.
413 * Return the mapped string on success, else return NULL.
414 */
415 LOCAL char *
get_macro(wp,c)416 get_macro(wp, c)
417 ewin_t *wp;
418 Uchar c;
419 {
420 char m_name[M_NAMELEN + 1];
421 register mac_t *tn;
422 register int i;
423 register char *cp;
424 register char *name;
425
426 cp = name = m_name;
427 *cp++ = c;
428 tn = first_macro;
429 for (i = 0; i < M_NAMELEN; i++) {
430 *cp = '\0';
431 for (;;) {
432 if (tn == NULL)
433 return (NULL);
434 if (strcmp(name, tn->m_name) == 0)
435 return (tn->m_string);
436 if (strstr(tn->m_name, name) == tn->m_name)
437 break;
438 tn = tn->m_next;
439 }
440 *cp++ = gchar(wp);
441 }
442 return (NULL);
443 }
444