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