use cross-gcc automatically; move configure args into variable
[alioth/jupp.git] / macro.c
1 /* $MirOS: contrib/code/jupp/macro.c,v 1.6 2010/04/08 15:31:01 tg Exp $ */
2 /*
3  *      Keyboard macros
4  *      Copyright
5  *              (C) 1992 Joseph H. Allen
6  *
7  *      This file is part of JOE (Joe's Own Editor)
8  */
9 #include "config.h"
10 #include "types.h"
11
12 #include <stdio.h>
13 #include <string.h>
14 #ifdef HAVE_STDLIB_H
15 #include <stdlib.h>
16 #endif
17
18 #include "b.h"
19 #include "cmd.h"
20 #include "main.h"
21 #include "pw.h"
22 #include "qw.h"
23 #include "tty.h"
24 #include "ublock.h"
25 #include "uedit.h"
26 #include "umath.h"
27 #include "undo.h"
28 #include "utils.h"
29 #include "vs.h"
30 #include "utf8.h"
31 #include "charmap.h"
32 #include "w.h"
33
34 MACRO *freemacros = NULL;
35
36 /* Create a macro */
37
38 MACRO *mkmacro(int k, int arg, int n, CMD *cmd)
39 {
40         MACRO *macro;
41
42         if (!freemacros) {
43                 int x;
44
45                 macro = (MACRO *) joe_malloc(sizeof(MACRO) * 64);
46                 for (x = 0; x != 64; ++x) {     /* FIXME: why limit to 64? */
47                         macro[x].steps = (MACRO **) freemacros;
48                         freemacros = macro + x;
49                 }
50         }
51         macro = freemacros;
52         freemacros = (MACRO *) macro->steps;
53         macro->steps = NULL;
54         macro->size = 0;
55         macro->arg = arg;
56         macro->n = n;
57         macro->cmd = cmd;
58         macro->k = k;
59         return macro;
60 }
61
62 /* Eliminate a macro */
63
64 void rmmacro(MACRO *macro)
65 {
66         if (macro) {
67                 if (macro->steps) {
68                         int x;
69
70                         for (x = 0; x != macro->n; ++x)
71                                 rmmacro(macro->steps[x]);
72                         joe_free(macro->steps);
73                 }
74                 macro->steps = (MACRO **) freemacros;
75                 freemacros = macro;
76         }
77 }
78
79 /* Add a step to block macro */
80
81 void addmacro(MACRO *macro, MACRO *m)
82 {
83         if (macro->n == macro->size) {
84                 if (macro->steps)
85                         macro->steps = (MACRO **) joe_realloc(macro->steps, (macro->size += 8) * sizeof(MACRO *));
86                 else
87                         macro->steps = (MACRO **) joe_malloc((macro->size = 8) * sizeof(MACRO *));
88         }
89         macro->steps[macro->n++] = m;
90 }
91
92 /* Duplicate a macro */
93
94 MACRO *dupmacro(MACRO *mac)
95 {
96         MACRO *m = mkmacro(mac->k, mac->arg, mac->n, mac->cmd);
97
98         if (mac->steps) {
99                 int x;
100
101                 m->steps = (MACRO **) joe_malloc((m->size = mac->n) * sizeof(MACRO *));
102                 for (x = 0; x != m->n; ++x)
103                         m->steps[x] = dupmacro(mac->steps[x]);
104         }
105         return m;
106 }
107
108 /* Set key part of macro */
109
110 MACRO *macstk(MACRO *m, int k)
111 {
112         m->k = k;
113         return m;
114 }
115
116 /* Set arg part of macro */
117
118 MACRO *macsta(MACRO *m, int a)
119 {
120         m->arg = a;
121         return m;
122 }
123
124 /* Parse text into a macro
125  * sta is set to:  ending position in buffer for no error.
126  *                 -1 for syntax error
127  *                 -2 for need more input
128  */
129
130 MACRO *mparse(MACRO *m, unsigned char *buf, int *sta)
131 {
132         int y, c, x = 0;
133
134       macroloop:
135
136         /* Skip whitespace */
137         while (joe_isblank(locale_map,buf[x]))
138                 ++x;
139
140         /* If the buffer is only whitespace then treat as unknown command */
141         if (!buf[x]) {
142                 *sta = -1;
143                 return NULL;
144         }
145
146         /* Do we have a string? */
147         if (buf[x] == '\"') {
148                 ++x;
149                 while (buf[x] && buf[x] != '\"') {
150                         if (buf[x] == '\\' && buf[x + 1]) {
151                                 ++x;
152                                 switch (buf[x]) {
153                                 case 'n':
154                                         buf[x] = 10;
155                                         break;
156                                 case 'r':
157                                         buf[x] = 13;
158                                         break;
159                                 case 'b':
160                                         buf[x] = 8;
161                                         break;
162                                 case 'f':
163                                         buf[x] = 12;
164                                         break;
165                                 case 'a':
166                                         buf[x] = 7;
167                                         break;
168                                 case 't':
169                                         buf[x] = 9;
170                                         break;
171                                 case 'x':
172                                         c = 0;
173                                         if (buf[x + 1] >= '0' && buf[x + 1] <= '9')
174                                                 c = c * 16 + buf[++x] - '0';
175                                         else if ((buf[x + 1] >= 'a' && buf[x + 1] <= 'f') || (buf[x + 1] >= 'A' && buf[x + 1] <= 'F'))
176                                                 c = c * 16 + (buf[++x] & 0xF) + 9;
177                                         if (buf[x + 1] >= '0' && buf[x + 1] <= '9')
178                                                 c = c * 16 + buf[++x] - '0';
179                                         else if ((buf[x + 1] >= 'a' && buf[x + 1] <= 'f') || (buf[x + 1] >= 'A' && buf[x + 1] <= 'F'))
180                                                 c = c * 16 + (buf[++x] & 0xF) + 9;
181                                         buf[x] = c;
182                                         break;
183                                 case '0':
184                                 case '1':
185                                 case '2':
186                                 case '3':
187                                 case '4':
188                                 case '5':
189                                 case '6':
190                                 case '7':
191                                 case '8':
192                                 case '9':
193                                         c = buf[x] - '0';
194                                         if (buf[x + 1] >= '0' && buf[x + 1] <= '7')
195                                                 c = c * 8 + buf[++x] - '0';
196                                         if (buf[x + 1] >= '0' && buf[x + 1] <= '7')
197                                                 c = c * 8 + buf[++x] - '0';
198                                         buf[x] = c;
199                                         break;
200                                 }
201                         }
202                         if (m) {
203                                 if (!m->steps) {
204                                         MACRO *macro = m;
205
206                                         m = mkmacro(-1, 1, 0, NULL);
207                                         addmacro(m, macro);
208                                 }
209                         } else
210                                 m = mkmacro(-1, 1, 0, NULL);
211                         addmacro(m, mkmacro(buf[x], 1, 0, findcmd(US "type")));
212                         ++x;
213                 }
214                 if (buf[x] == '\"')
215                         ++x;
216         }
217
218         /* Do we have a command? */
219         else {
220                 for (y = x; buf[y] && buf[y] != ',' && buf[y] != ' ' && buf[y] != '\t' && buf[y] != '\n' && buf[x] != '\r'; ++y) ;
221                 if (y != x) {
222                         CMD *cmd;
223
224                         c = buf[y];
225                         buf[y] = 0;
226                         cmd = findcmd(buf + x);
227                         if (!cmd) {
228                                 *sta = -1;
229                                 return NULL;
230                         } else if (m) {
231                                 if (!m->steps) {
232                                         MACRO *macro = m;
233
234                                         m = mkmacro(-1, 1, 0, NULL);
235                                         addmacro(m, macro);
236                                 }
237                                 addmacro(m, mkmacro(-1, 1, 0, cmd));
238                         } else
239                                 m = mkmacro(-1, 1, 0, cmd);
240                         buf[x = y] = c;
241                 }
242         }
243
244         /* Skip whitespace */
245         while (joe_isblank(locale_map,buf[x]))
246                 ++x;
247
248         /* Do we have a comma? */
249         if (buf[x] == ',') {
250                 ++x;
251                 while (joe_isblank(locale_map,buf[x]))
252                         ++x;
253                 if (buf[x] && buf[x] != '\r' && buf[x] != '\n')
254                         goto macroloop;
255                 *sta = -2;
256                 return m;
257         }
258
259         /* Done */
260         *sta = x;
261         return m;
262 }
263
264 /* Convert macro to text */
265
266 static unsigned char *ptr;
267 static int first;
268 static int instr;
269
270 static unsigned char *unescape(unsigned char *uptr, int c)
271 {
272         if (c == '"') {
273                 *uptr++ = '\\';
274                 *uptr++ = '"';
275         } else if (c == '\\') {
276                 *uptr++ = '\\';
277                 *uptr++ = '\\';
278         } else if (c == '\'') {
279                 *uptr++ = '\\';
280                 *uptr++ = '\'';
281         } else if (c < 32 || c > 126) {
282                 /* FIXME: what if c > 256 or c < 0 ? */
283                 *uptr++ = '\\';
284                 *uptr++ = 'x';
285                 *uptr++ = "0123456789ABCDEF"[c >> 4];
286                 *uptr++ = "0123456789ABCDEF"[c & 15];
287         } else
288                 *uptr++ = c;
289         return uptr;
290 }
291
292 static void domtext(MACRO *m)
293 {
294         int x;
295
296         if (!m)
297                 return;
298         if (m->steps)
299                 for (x = 0; x != m->n; ++x)
300                         domtext(m->steps[x]);
301         else {
302                 if (instr && strcmp(m->cmd->name, "type")) {
303                         *ptr++ = '\"';
304                         instr = 0;
305                 }
306                 if (first)
307                         first = 0;
308                 else if (!instr)
309                         *ptr++ = ',';
310                 if (!strcmp(m->cmd->name, "type")) {
311                         if (!instr) {
312                                 *ptr++ = '\"';
313                                 instr = 1;
314                         }
315                         ptr = unescape(ptr, m->k);
316                 } else {
317                         for (x = 0; m->cmd->name[x]; ++x)
318                                 *ptr++ = m->cmd->name[x];
319                         if (!strcmp(m->cmd->name, "play") || !strcmp(m->cmd->name, "gomark") || !strcmp(m->cmd->name, "setmark") || !strcmp(m->cmd->name, "record") || !strcmp(m->cmd->name, "uarg")) {
320                                 *ptr++ = ',';
321                                 *ptr++ = '"';
322                                 ptr = unescape(ptr, m->k);
323                                 *ptr++ = '"';
324                         }
325                 }
326         }
327 }
328
329 unsigned char *mtext(unsigned char *s, MACRO *m)
330 {
331         ptr = s;
332         first = 1;
333         instr = 0;
334         domtext(m);
335         if (instr)
336                 *ptr++ = '\"';
337         *ptr = 0;
338         return s;
339 }
340
341 /* Keyboard macro recorder */
342
343 static MACRO *kbdmacro[10];
344 static int playmode[10];
345
346 struct recmac *recmac = NULL;
347
348 static void unmac(void)
349 {
350         if (recmac)
351                 rmmacro(recmac->m->steps[--recmac->m->n]);
352 }
353
354 void chmac(void)
355 {
356         if (recmac && recmac->m->n)
357                 recmac->m->steps[recmac->m->n - 1]->k = 3;
358 }
359
360 static void record(MACRO *m)
361 {
362         if (recmac)
363                 addmacro(recmac->m, dupmacro(m));
364 }
365
366 /* Query for user input */
367
368 int uquery(BW *bw)
369 {
370         int ret;
371         struct recmac *tmp = recmac;
372
373         recmac = NULL;
374         ret = edloop(1);
375         recmac = tmp;
376         return ret;
377 }
378
379 /* Macro execution */
380
381 MACRO *curmacro = NULL;         /* Set if we're in a macro */
382 static int macroptr;
383 static int arg = 0;             /* Repeat argument */
384 static int argset = 0;          /* Set if 'arg' is set */
385
386 int exmacro(MACRO *m, int u)
387 {
388         int larg;
389         int negarg = 0;
390         int flg = 0;
391         CMD *cmd = NULL;
392         int ret = 0;
393
394         if (argset) {
395                 larg = arg;
396                 arg = 0;
397                 argset = 0;
398                 if (larg < 0) {
399                         negarg = 1;
400                         larg = -larg;
401                 }
402                 if (m->steps)
403                         negarg = 0;
404                 else {
405                         cmd = m->cmd;
406                         if (!cmd->arg)
407                                 larg = 0;
408                         else if (negarg) {
409                                 if (cmd->negarg)
410                                         cmd = findcmd(cmd->negarg);
411                                 else
412                                         larg = 0;
413                         }
414                 }
415         } else {
416                 cmd = m->cmd;
417                 larg = 1;
418         }
419
420         if (m->steps || larg != 1 || !(cmd->flag & EMINOR)
421             || maint->curwin->watom->what == TYPEQW     /* Undo work right for s & r */
422             )
423                 flg = 1;
424
425         if (flg && u)
426                 umclear();
427         while (larg-- && !leave && !ret)
428                 if (m->steps) {
429                         MACRO *tmpmac = curmacro;
430                         int tmpptr = macroptr;
431                         int x = 0;
432                         int stk = nstack;
433
434                         while (m && x != m->n && !leave && !ret) {
435                                 MACRO *d;
436
437                                 d = m->steps[x++];
438                                 curmacro = m;
439                                 macroptr = x;
440                                 ret = exmacro(d, 0);
441                                 m = curmacro;
442                                 x = macroptr;
443                         }
444                         curmacro = tmpmac;
445                         macroptr = tmpptr;
446                         while (nstack > stk)
447                                 upop(NULL);
448                 } else
449                         ret = execmd(cmd, m->k);
450         if (leave)
451                 return ret;
452         if (flg && u)
453                 umclear();
454
455         if (u)
456                 undomark();
457
458         return ret;
459 }
460
461 /* Execute a macro */
462
463 int exemac(MACRO *m)
464 {
465         record(m);
466         return exmacro(m, 1);
467 }
468
469 /* Keyboard macro user routines */
470
471 static int dorecord(BW *bw, int c, void *object, int *notify)
472 {
473         int n;
474         struct recmac *r;
475
476         if (notify)
477                 *notify = 1;
478         if (c > '9' || c < '0') {
479                 nungetc(c);
480                 return -1;
481         }
482         for (n = 0; n != 10; ++n)
483                 if (playmode[n])
484                         return -1;
485         r = (struct recmac *) joe_malloc(sizeof(struct recmac));
486
487         r->m = mkmacro(0, 1, 0, NULL);
488         r->next = recmac;
489         r->n = c - '0';
490         recmac = r;
491         return 0;
492 }
493
494 int urecord(BW *bw, int c)
495 {
496         if (c >= '0' && c <= '9')
497                 return dorecord(bw, c, NULL, NULL);
498         else if (mkqw(bw->parent, sc("Macro to record (0-9 or ^C to abort): "), dorecord, NULL, NULL, NULL))
499                 return 0;
500         else
501                 return -1;
502 }
503
504 extern volatile int dostaupd;
505
506 int ustop(void)
507 {
508         unmac();
509         if (recmac) {
510                 struct recmac *r = recmac;
511                 MACRO *m;
512
513                 dostaupd = 1;
514                 recmac = r->next;
515                 if (kbdmacro[r->n])
516                         rmmacro(kbdmacro[r->n]);
517                 kbdmacro[r->n] = r->m;
518                 if (recmac)
519                         record(m = mkmacro(r->n + '0', 1, 0, findcmd(US "play"))), rmmacro(m);
520                 joe_free(r);
521         }
522         return 0;
523 }
524
525 static int doplay(BW *bw, int c, void *object, int *notify)
526 {
527         if (notify)
528                 *notify = 1;
529         if (c >= '0' && c <= '9') {
530                 int ret;
531
532                 c -= '0';
533                 if (playmode[c] || !kbdmacro[c])
534                         return -1;
535                 playmode[c] = 1;
536                 ret = exmacro(kbdmacro[c], 0);
537                 playmode[c] = 0;
538                 return ret;
539         } else {
540                 nungetc(c);
541                 return -1;
542         }
543 }
544
545 int umacros(BW *bw)
546 {
547         int x;
548         unsigned char buf[1024];
549
550         p_goto_eol(bw->cursor);
551         for (x = 0; x != 10; ++x)
552                 if (kbdmacro[x]) {
553                         mtext(buf, kbdmacro[x]);
554                         binss(bw->cursor, buf);
555                         p_goto_eol(bw->cursor);
556                         joe_snprintf_2((char *)buf, JOE_MSGBUFSIZE, "\t^K %c\tMacro %d", x + '0', x);
557                         binss(bw->cursor, buf);
558                         p_goto_eol(bw->cursor);
559                         binsc(bw->cursor, '\n');
560                         pgetc(bw->cursor);
561                 }
562         return 0;
563 }
564
565 int uplay(BW *bw, int c)
566 {
567         if (c >= '0' && c <= '9')
568                 return doplay(bw, c, NULL, NULL);
569         else if (mkqwna(bw->parent, sc("Play-"), doplay, NULL, NULL, NULL))
570                 return 0;
571         else
572                 return -1;
573 }
574
575 /* Repeat-count setting */
576
577 static int doarg(BW *bw, unsigned char *s, void *object, int *notify)
578 {
579         long num;
580
581         if (notify)
582                 *notify = 1;
583         num = calc(bw, s);
584         if (merr) {
585                 msgnw(bw->parent, merr);
586                 return -1;
587         }
588         arg = num;
589         argset = 1;
590         vsrm(s);
591         return 0;
592 }
593
594 int uarg(BW *bw)
595 {
596         if (wmkpw(bw->parent, US "No. times to repeat next command (^C to abort): ", NULL, doarg, NULL, NULL, utypebw, NULL, NULL, locale_map))
597                 return 0;
598         else
599                 return -1;
600 }
601
602 int unaarg;
603 int negarg;
604
605 static int douarg(BW *bw, int c, void *object, int *notify)
606 {
607         if (c == '-')
608                 negarg = !negarg;
609         else if (c >= '0' && c <= '9')
610                 unaarg = unaarg * 10 + c - '0';
611         else if (c == 'U' - '@')
612                 if (unaarg)
613                         unaarg *= 4;
614                 else
615                         unaarg = 16;
616         else if (c == 7 || c == 3 || c == 32) {
617                 if (notify)
618                         *notify = 1;
619                 return -1;
620         } else {
621                 nungetc(c);
622                 if (unaarg)
623                         arg = unaarg;
624                 else if (negarg)
625                         arg = 1;
626                 else
627                         arg = 4;
628                 if (negarg)
629                         arg = -arg;
630                 argset = 1;
631                 if (notify)
632                         *notify = 1;
633                 return 0;
634         }
635         joe_snprintf_2((char *)msgbuf, JOE_MSGBUFSIZE, "Repeat %s%d", negarg ? "-" : "", unaarg);
636         if (mkqwna(bw->parent, sz(msgbuf), douarg, NULL, NULL, notify))
637                 return 0;
638         else
639                 return -1;
640 }
641
642 int uuarg(BW *bw, int c)
643 {
644         unaarg = 0;
645         negarg = 0;
646         if ((c >= '0' && c <= '9') || c == '-')
647                 return douarg(bw, c, NULL, NULL);
648         else if (mkqwna(bw->parent, sc("Repeat"), douarg, NULL, NULL, NULL))
649                 return 0;
650         else
651                 return -1;
652 }