/MirOS/dist/jupp/joe-3.1jupp30.tgz
[alioth/jupp.git] / macro.c
1 /* $MirOS: contrib/code/jupp/macro.c,v 1.7 2012/12/22 00:06:11 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         /*XXX why is this local here and global below? */
390         int negarg = 0;
391         int flg = 0;
392         CMD *cmd = NULL;
393         int ret = 0;
394
395         if (argset) {
396                 larg = arg;
397                 arg = 0;
398                 argset = 0;
399                 if (larg < 0) {
400                         negarg = 1;
401                         larg = -larg;
402                 }
403                 if (m->steps) {
404                         ; /* dead store: negarg = 0; */
405                 } else {
406                         cmd = m->cmd;
407                         if (!cmd->arg)
408                                 larg = 0;
409                         else if (negarg) {
410                                 if (cmd->negarg)
411                                         cmd = findcmd(cmd->negarg);
412                                 else
413                                         larg = 0;
414                         }
415                 }
416         } else {
417                 cmd = m->cmd;
418                 larg = 1;
419         }
420
421         if (m->steps || larg != 1 || !(cmd->flag & EMINOR)
422             || maint->curwin->watom->what == TYPEQW     /* Undo work right for s & r */
423             )
424                 flg = 1;
425
426         if (flg && u)
427                 umclear();
428         while (larg-- && !leave && !ret)
429                 if (m->steps) {
430                         MACRO *tmpmac = curmacro;
431                         int tmpptr = macroptr;
432                         int x = 0;
433                         int stk = nstack;
434
435                         while (m && x != m->n && !leave && !ret) {
436                                 MACRO *d;
437
438                                 d = m->steps[x++];
439                                 curmacro = m;
440                                 macroptr = x;
441                                 ret = exmacro(d, 0);
442                                 m = curmacro;
443                                 x = macroptr;
444                         }
445                         curmacro = tmpmac;
446                         macroptr = tmpptr;
447                         while (nstack > stk)
448                                 upop(NULL);
449                 } else
450                         ret = execmd(cmd, m->k);
451         if (leave)
452                 return ret;
453         if (flg && u)
454                 umclear();
455
456         if (u)
457                 undomark();
458
459         return ret;
460 }
461
462 /* Execute a macro */
463
464 int exemac(MACRO *m)
465 {
466         record(m);
467         return exmacro(m, 1);
468 }
469
470 /* Keyboard macro user routines */
471
472 static int dorecord(BW *bw, int c, void *object, int *notify)
473 {
474         int n;
475         struct recmac *r;
476
477         if (notify)
478                 *notify = 1;
479         if (c > '9' || c < '0') {
480                 nungetc(c);
481                 return -1;
482         }
483         for (n = 0; n != 10; ++n)
484                 if (playmode[n])
485                         return -1;
486         r = (struct recmac *) joe_malloc(sizeof(struct recmac));
487
488         r->m = mkmacro(0, 1, 0, NULL);
489         r->next = recmac;
490         r->n = c - '0';
491         recmac = r;
492         return 0;
493 }
494
495 int urecord(BW *bw, int c)
496 {
497         if (c >= '0' && c <= '9')
498                 return dorecord(bw, c, NULL, NULL);
499         else if (mkqw(bw->parent, sc("Macro to record (0-9 or ^C to abort): "), dorecord, NULL, NULL, NULL))
500                 return 0;
501         else
502                 return -1;
503 }
504
505 extern volatile int dostaupd;
506
507 int ustop(void)
508 {
509         unmac();
510         if (recmac) {
511                 struct recmac *r = recmac;
512                 MACRO *m;
513
514                 dostaupd = 1;
515                 recmac = r->next;
516                 if (kbdmacro[r->n])
517                         rmmacro(kbdmacro[r->n]);
518                 kbdmacro[r->n] = r->m;
519                 if (recmac)
520                         record(m = mkmacro(r->n + '0', 1, 0, findcmd(US "play"))), rmmacro(m);
521                 joe_free(r);
522         }
523         return 0;
524 }
525
526 static int doplay(BW *bw, int c, void *object, int *notify)
527 {
528         if (notify)
529                 *notify = 1;
530         if (c >= '0' && c <= '9') {
531                 int ret;
532
533                 c -= '0';
534                 if (playmode[c] || !kbdmacro[c])
535                         return -1;
536                 playmode[c] = 1;
537                 ret = exmacro(kbdmacro[c], 0);
538                 playmode[c] = 0;
539                 return ret;
540         } else {
541                 nungetc(c);
542                 return -1;
543         }
544 }
545
546 int umacros(BW *bw)
547 {
548         int x;
549         unsigned char buf[1024];
550
551         p_goto_eol(bw->cursor);
552         for (x = 0; x != 10; ++x)
553                 if (kbdmacro[x]) {
554                         mtext(buf, kbdmacro[x]);
555                         binss(bw->cursor, buf);
556                         p_goto_eol(bw->cursor);
557                         joe_snprintf_2((char *)buf, JOE_MSGBUFSIZE, "\t^K %c\tMacro %d", x + '0', x);
558                         binss(bw->cursor, buf);
559                         p_goto_eol(bw->cursor);
560                         binsc(bw->cursor, '\n');
561                         pgetc(bw->cursor);
562                 }
563         return 0;
564 }
565
566 int uplay(BW *bw, int c)
567 {
568         if (c >= '0' && c <= '9')
569                 return doplay(bw, c, NULL, NULL);
570         else if (mkqwna(bw->parent, sc("Play-"), doplay, NULL, NULL, NULL))
571                 return 0;
572         else
573                 return -1;
574 }
575
576 /* Repeat-count setting */
577
578 static int doarg(BW *bw, unsigned char *s, void *object, int *notify)
579 {
580         long num;
581
582         if (notify)
583                 *notify = 1;
584         num = calc(bw, s);
585         if (merr) {
586                 msgnw(bw->parent, merr);
587                 return -1;
588         }
589         arg = num;
590         argset = 1;
591         vsrm(s);
592         return 0;
593 }
594
595 int uarg(BW *bw)
596 {
597         if (wmkpw(bw->parent, US "No. times to repeat next command (^C to abort): ", NULL, doarg, NULL, NULL, utypebw, NULL, NULL, locale_map))
598                 return 0;
599         else
600                 return -1;
601 }
602
603 int unaarg;
604 int negarg;
605
606 static int douarg(BW *bw, int c, void *object, int *notify)
607 {
608         if (c == '-')
609                 negarg = !negarg;
610         else if (c >= '0' && c <= '9')
611                 unaarg = unaarg * 10 + c - '0';
612         else if (c == 'U' - '@')
613                 if (unaarg)
614                         unaarg *= 4;
615                 else
616                         unaarg = 16;
617         else if (c == 7 || c == 3 || c == 32) {
618                 if (notify)
619                         *notify = 1;
620                 return -1;
621         } else {
622                 nungetc(c);
623                 if (unaarg)
624                         arg = unaarg;
625                 else if (negarg)
626                         arg = 1;
627                 else
628                         arg = 4;
629                 if (negarg)
630                         arg = -arg;
631                 argset = 1;
632                 if (notify)
633                         *notify = 1;
634                 return 0;
635         }
636         joe_snprintf_2((char *)msgbuf, JOE_MSGBUFSIZE, "Repeat %s%d", negarg ? "-" : "", unaarg);
637         if (mkqwna(bw->parent, sz(msgbuf), douarg, NULL, NULL, notify))
638                 return 0;
639         else
640                 return -1;
641 }
642
643 int uuarg(BW *bw, int c)
644 {
645         unaarg = 0;
646         negarg = 0;
647         if ((c >= '0' && c <= '9') || c == '-')
648                 return douarg(bw, c, NULL, NULL);
649         else if (mkqwna(bw->parent, sc("Repeat"), douarg, NULL, NULL, NULL))
650                 return 0;
651         else
652                 return -1;
653 }