1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include "jove.h"
9 #include "jctype.h"
10 #include "fp.h"
11 #include "chars.h"
12 #include "disp.h"
13 #include "ask.h"
14 #include "commands.h"
15 #include "macros.h"
16 #include "extend.h"
17 #include "fmt.h"
18 /* #include "util.h" */
19 
20 private void
21 	pop_macro_stack proto((void));
22 
23 private struct macro
24 	*ask_macname proto((const char *, int));
25 
26 private bool	UnsavedMacros = NO;	/* are there any macros that need saving to a file? */
27 
28 struct macro	*macros = NULL;		/* macros */
29 bool	InMacDefine = NO;
30 
31 private void
32 add_mac(new)
33 struct macro	*new;
34 {
35 	register struct macro	*mp,
36 				*prev = NULL;
37 
38 	for (mp = macros; mp != NULL; prev = mp, mp = mp->m_nextm)
39 		if (mp == new)
40 			return;
41 
42 	if (prev)
43 		prev->m_nextm = new;
44 	else
45 		macros = new;
46 	new->m_nextm = NULL;
47 	new->Type = MACRO;
48 }
49 
50 /* To execute a macro, we have a "stack" of running macros.  Whenever
51    we execute a macro, we push it on the stack, run it, then pop it
52    from the stack.  */
53 struct m_thread {
54 	struct m_thread	*mt_prev;
55 	struct macro	*mt_mp;
56 	int	mt_offset,
57 		mt_count;
58 };
59 
60 private struct m_thread	*mac_stack = NULL;
61 
62 private struct m_thread *
63 alloc_mthread()
64 {
65 	return (struct m_thread *) emalloc(sizeof (struct m_thread));
66 }
67 
68 private void
69 free_mthread(t)
70 struct m_thread	*t;
71 {
72 	free((UnivPtr) t);
73 }
74 
75 void
76 unwind_macro_stack()
77 {
78 	while (mac_stack != NULL)
79 		pop_macro_stack();
80 }
81 
82 private void
83 pop_macro_stack()
84 {
85 	register struct m_thread	*m;
86 
87 	if ((m = mac_stack) == NULL)
88 		return;
89 	mac_stack = m->mt_prev;
90 	free_mthread(m);
91 }
92 
93 private void
94 push_macro_stack(m, count)
95 register struct macro	*m;
96 int	count;
97 {
98 	register struct m_thread	*t;
99 
100 	for (t = mac_stack; t != NULL; t = t->mt_prev)
101 		if (t->mt_mp == m)
102 			complain("[Cannot execute macro recusively]");
103 	if (count <= 0)
104 		complain("[Cannot execute macro a negative number of times]");
105 	t = alloc_mthread();
106 	t->mt_prev = mac_stack;
107 	mac_stack = t;
108 	t->mt_offset = 0;
109 	t->mt_mp = m;
110 	t->mt_count = count;
111 }
112 
113 void
114 do_macro(mac)
115 struct macro	*mac;
116 {
117 	push_macro_stack(mac, arg_value());
118 }
119 
120 private struct macro	KeyMacro = {	/* Macro used for defining */
121 	MACRO, "keyboard-macro", 0, NULL, NULL
122 };
123 
124 private int	kmac_len;
125 private int	kmac_buflen = 0;
126 
127 void
128 mac_init()
129 {
130 	add_mac(&KeyMacro);
131 }
132 
133 void
134 mac_putc(c)
135 char	c;
136 {
137 	if (kmac_len >= kmac_buflen) {
138 		KeyMacro.m_body = erealloc((UnivPtr) KeyMacro.m_body, (size_t) kmac_buflen + 16);
139 		kmac_buflen += 16;
140 	}
141 	KeyMacro.m_body[kmac_len++] = c;
142 }
143 
144 void
145 note_dispatch()
146 {
147 	if (kmac_len > 0)
148 		KeyMacro.m_len = kmac_len - 1;
149 }
150 
151 bool
152 in_macro()
153 {
154 	return (mac_stack != NULL);
155 }
156 
157 ZXchar
158 mac_getc()
159 {
160 	struct m_thread	*mthread;
161 	struct macro	*m;
162 
163 	if ((mthread = mac_stack) == NULL)
164 		return EOF;
165 	m = mthread->mt_mp;
166 	if (mthread->mt_offset == m->m_len) {
167 		mthread->mt_offset = 0;
168 		if (--mthread->mt_count == 0)
169 			pop_macro_stack();
170 		return mac_getc();
171 	}
172 	return ZXC(m->m_body[mthread->mt_offset++]);
173 }
174 
175 private void
176 MacDef(m, name, len, body)
177 struct macro	*m;	/* NULL, or def to overwrite */
178 char	*name;	/* must be stable if m isn't NULL */
179 int	len;
180 char	*body;
181 {
182 	if (m == NULL) {
183 		m = (struct macro *) emalloc(sizeof *m);
184 		m->Name = name;
185 	} else {
186 		if (m->m_body != NULL)
187 			free((UnivPtr) m->m_body);
188 	}
189 	m->m_len = len;
190 	if (len == 0) {
191 		m->m_body = NULL;
192 	} else {
193 		m->m_body = emalloc((size_t) len);
194 		byte_copy(body, m->m_body, (size_t) len);
195 	}
196 	add_mac(m);
197 	if (!InJoverc)
198 		UnsavedMacros = YES;
199 }
200 
201 void
202 NameMac()
203 {
204 	char	*name = NULL;
205 	struct macro	*m;
206 
207 	if (KeyMacro.m_len == 0)
208 		complain("[No keyboard macro to name!]");
209 	if (in_macro() || InMacDefine)
210 		complain("[Can't name while defining/executing]");
211 	if ((m = ask_macname(ProcFmt, ALLOW_OLD | ALLOW_INDEX | ALLOW_NEW)) == NULL)
212 		name = copystr(Minibuf);
213 	if (m == &KeyMacro)
214 		complain("[Can't name it that!]");
215 	MacDef(m, name, KeyMacro.m_len, KeyMacro.m_body);
216 }
217 
218 void
219 RunMacro()
220 {
221 	do_macro((struct macro *) findmac(ProcFmt));
222 }
223 
224 private void
225 pr_putc(c, fp)
226 ZXchar	c;
227 File	*fp;
228 {
229 	if (c == '\\' || c == '^') {
230 		f_putc('\\', fp);
231 		f_putc(c, fp);
232 	} else {
233 		char	buf[PPWIDTH];
234 		char	*p;
235 
236 		PPchar(c, buf);
237 		for (p = buf; *p != '\0'; p++)
238 			f_putc(*p, fp);
239 	}
240 }
241 
242 void
243 WriteMacs()
244 {
245 	struct macro	*m;
246 	char
247 		fnamebuf[FILESIZE];
248 	File	*fp;
249 	int	i;
250 
251 	(void) ask_file((char *)NULL, (char *)NULL, fnamebuf);
252 	fp = open_file(fnamebuf, iobuff, F_WRITE, YES);
253 
254 	/* Don't write the keyboard macro which is always the first */
255 	for (m = macros->m_nextm; m != NULL; m = m->m_nextm) {
256 		fwritef(fp, "define-macro %s ", m->Name);
257 		for (i = 0; i < m->m_len; i++)
258 			pr_putc(ZXC(m->m_body[i]), fp);
259 #ifdef USE_CRLF
260 		f_putc('\r', fp);
261 #endif /* USE_CRLF */
262 		f_putc(EOL, fp);
263 	}
264 	close_file(fp);
265 	UnsavedMacros = NO;
266 }
267 
268 void
269 DefKBDMac()
270 {
271 	struct macro	*m = ask_macname(ProcFmt,
272 		ALLOW_OLD | ALLOW_INDEX | ALLOW_NEW);
273 	ZXchar	c;
274 	char
275 		*macro_name = m == NULL? copystr(Minibuf) : m->Name,
276 		*macro_body,
277 		macro_buffer[LBSIZE];
278 	int	len;
279 
280 	if (m == &KeyMacro)
281 		complain("[Can't name it that!]");
282 	/* ??? I hope that this ask doesn't change *m! */
283 	macro_body = ask(NullStr, ": %f %s enter body: ", macro_name);
284 	len = 0;
285 	while ((c = ZXC(*macro_body++)) != '\0') {
286 		if (c == '\\' || c == '^')
287 			c = DecodePair(c, ZXC(*macro_body++));
288 		if (len >= LBSIZE)
289 			complain("Macro to large");
290 		macro_buffer[len++] = c;
291 	}
292 	MacDef(m, macro_name, len, macro_buffer);
293 }
294 
295 void
296 Remember()
297 {
298 	/* We're already executing the macro; ignore any attempts
299 	   to define the keyboard macro while we are executing. */
300 	if (in_macro())
301 		return;
302 	if (InMacDefine)
303 		message("[Already defining ... continue with definition]");
304 	else {
305 		UpdModLine = YES;
306 		InMacDefine = YES;
307 		kmac_len = KeyMacro.m_len = 0;
308 		message("Defining...");
309 	}
310 }
311 
312 void
313 Forget()
314 {
315 	UpdModLine = YES;
316 	if (InMacDefine) {
317 		message("Keyboard macro defined.");
318 		InMacDefine = NO;
319 	} else
320 		complain("[end-kbd-macro: not currently defining macro!]");
321 }
322 
323 void
324 ExecMacro()
325 {
326 	do_macro(&KeyMacro);
327 }
328 
329 void
330 MacInter()
331 {
332 	if (Asking)
333 		Interactive = YES;
334 }
335 
336 bool
337 ModMacs()
338 {
339 	return UnsavedMacros;
340 }
341 
342 /* Ask for macro name, with completion.
343  * Flags is passed directly to complete.  If ALLOW_NEW is on,
344  * the name might be new, in which case NULL is returned and the
345  * actual name in in Minibuf.
346  */
347 
348 private struct macro *
349 ask_macname(prompt, flags)
350 const char	*prompt;
351 int flags;
352 {
353 	char	*strings[100];
354 	register char	**strs = strings;
355 	register int	com;
356 	register struct macro	*m;
357 
358 	for (m = macros; m != NULL; m = m->m_nextm) {
359 		if (strs == &strings[elemsof(strings)-1])
360 			complain("[too many macros]");
361 		*strs++ = m->Name;
362 	}
363 	*strs = NULL;
364 
365 	if ((com = complete(strings, (char *)NULL, prompt, flags)) < 0)
366 		return NULL;
367 	m = macros;
368 	while (--com >= 0)
369 		m = m->m_nextm;
370 	return m;
371 }
372 
373 data_obj *
374 findmac(prompt)
375 const char	*prompt;
376 {
377 	return (data_obj *)ask_macname(prompt, ALLOW_OLD | ALLOW_INDEX);
378 }
379