4 * (C) 1992 Joseph H. Allen
6 * This file is part of JOE (Joe's Own Editor)
11 __RCSID("$MirOS: contrib/code/jupp/macro.c,v 1.19 2017/12/08 02:28:05 tg Exp $");
32 MACRO *freemacros = NULL;
36 MACRO *mkmacro(int k, int arg, int n, CMD *cmd)
43 /* FIXME: why limit to 64? */
44 macro = calloc(64, sizeof(MACRO));
45 for (x = 0; x != 64; ++x) {
46 macro[x].steps = (MACRO **) freemacros;
47 freemacros = macro + x;
51 freemacros = (MACRO *) macro->steps;
61 /* Eliminate a macro */
63 void rmmacro(MACRO *macro)
69 for (x = 0; x != macro->n; ++x)
70 rmmacro(macro->steps[x]);
73 macro->steps = (MACRO **) freemacros;
78 /* Add a step to block macro */
80 void addmacro(MACRO *macro, MACRO *m)
82 if (macro->n == macro->size) {
84 macro->steps = realloc(macro->steps, (macro->size += 8) * sizeof(MACRO *));
86 macro->steps = calloc((macro->size = 8), sizeof(MACRO *));
88 macro->steps[macro->n++] = m;
91 /* Duplicate a macro */
93 MACRO *dupmacro(MACRO *mac)
95 MACRO *m = mkmacro(mac->k, mac->arg, mac->n, mac->cmd);
100 m->steps = calloc((m->size = mac->n), sizeof(MACRO *));
101 for (x = 0; x != m->n; ++x)
102 m->steps[x] = dupmacro(mac->steps[x]);
107 /* Set key part of macro */
109 MACRO *macstk(MACRO *m, int k)
115 /* Set arg part of macro */
117 MACRO *macsta(MACRO *m, int a)
123 /* Parse text into a macro
124 * sta is set to: ending position in buffer for no error.
125 * -1 for syntax error
126 * -2 for need more input
129 MACRO *mparse(MACRO *m, unsigned char *buf, int *sta)
135 /* Skip whitespace */
136 while (joe_isblank(locale_map,buf[x]))
139 /* If the buffer is only whitespace then treat as unknown command */
145 /* Do we have a string? */
146 if (buf[x] == '\"') {
148 while (buf[x] && buf[x] != '\"') {
149 if (buf[x] == '\\' && buf[x + 1]) {
171 x += 1 + ustoc_hex(buf + x + 1, &c, USTOC_MAX);
183 x += ustoc_oct(buf + x, &c, USTOC_MAX);
191 m = mkmacro(-1, 1, 0, NULL);
195 m = mkmacro(-1, 1, 0, NULL);
196 addmacro(m, mkmacro(buf[x], 1, 0, findcmd(UC "type")));
203 /* Do we have a command? */
205 for (y = x; buf[y] && buf[y] != ',' && buf[y] != ' ' && buf[y] != '\t' && buf[y] != '\n' && buf[x] != '\r'; ++y) ;
211 cmd = findcmd(buf + x);
219 m = mkmacro(-1, 1, 0, NULL);
222 addmacro(m, mkmacro(-1, 1, 0, cmd));
224 m = mkmacro(-1, 1, 0, cmd);
229 /* Skip whitespace */
230 while (joe_isblank(locale_map,buf[x]))
233 /* Do we have a comma? */
236 while (joe_isblank(locale_map,buf[x]))
238 if (buf[x] && buf[x] != '\r' && buf[x] != '\n')
249 /* Convert macro to text */
251 static unsigned char *ptr;
255 static unsigned char *unescape(unsigned char *uptr, int c)
260 } else if (c == '\\') {
263 } else if (c == '\'') {
266 } else if (c < 32 || c > 126) {
267 /* FIXME: what if c > 256 or c < 0 ? */
270 *uptr++ = "0123456789ABCDEF"[c >> 4];
271 *uptr++ = "0123456789ABCDEF"[c & 15];
277 static void domtext(MACRO *m)
284 for (x = 0; x != m->n; ++x)
285 domtext(m->steps[x]);
287 if (instr && strcmp(m->cmd->name, "type")) {
295 if (!strcmp(m->cmd->name, "type")) {
300 ptr = unescape(ptr, m->k);
302 for (x = 0; m->cmd->name[x]; ++x)
303 *ptr++ = m->cmd->name[x];
304 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")) {
307 ptr = unescape(ptr, m->k);
314 unsigned char *mtext(unsigned char *s, MACRO *m)
326 /* Keyboard macro recorder */
328 static MACRO *kbdmacro[10];
329 static int playmode[10];
331 struct recmac *recmac = NULL;
333 static void unmac(void)
336 rmmacro(recmac->m->steps[--recmac->m->n]);
341 if (recmac && recmac->m->n)
342 recmac->m->steps[recmac->m->n - 1]->k = 3;
345 static void record(MACRO *m)
348 addmacro(recmac->m, dupmacro(m));
351 /* Query for user input */
356 struct recmac *tmp = recmac;
364 /* Macro execution */
366 MACRO *curmacro = NULL; /* Set if we're in a macro */
368 static int arg = 0; /* Repeat argument */
369 static int argset = 0; /* Set if 'arg' is set */
371 int exmacro(MACRO *m, int u)
374 /*XXX why is this local here and global below? */
389 ; /* dead store: negarg = 0; */
396 cmd = findcmd(cmd->negarg);
406 if (m->steps || larg != 1 || !(cmd->flag & EMINOR)
407 || maint->curwin->watom->what == TYPEQW /* Undo work right for s & r */
413 while (larg-- && !leave && !ret)
415 MACRO *tmpmac = curmacro;
416 int tmpptr = macroptr;
420 while (m && x != m->n && !leave && !ret) {
435 ret = execmd(cmd, m->k);
447 /* Execute a macro */
452 return exmacro(m, 1);
455 /* Keyboard macro user routines */
457 static int dorecord(BW *bw, int c, void *object, int *notify)
464 if (c > '9' || c < '0') {
468 for (n = 0; n != 10; ++n)
471 r = malloc(sizeof(struct recmac));
473 r->m = mkmacro(0, 1, 0, NULL);
480 int urecord(BW *bw, int c)
482 if (c >= '0' && c <= '9')
483 return dorecord(bw, c, NULL, NULL);
484 else if (mkqw(bw->parent, sc("Macro to record (0-9 or ^C to abort): "), dorecord, NULL, NULL, NULL))
490 extern volatile int dostaupd;
496 struct recmac *r = recmac;
502 rmmacro(kbdmacro[r->n]);
503 kbdmacro[r->n] = r->m;
505 record(m = mkmacro(r->n + '0', 1, 0, findcmd(UC "play"))), rmmacro(m);
511 static int doplay(BW *bw, int c, void *object, int *notify)
515 if (c >= '0' && c <= '9') {
519 if (playmode[c] || !kbdmacro[c])
522 ret = exmacro(kbdmacro[c], 0);
534 unsigned char buf[1024];
536 p_goto_eol(bw->cursor);
537 for (x = 0; x != 10; ++x)
539 mtext(buf, kbdmacro[x]);
540 binss(bw->cursor, buf);
541 p_goto_eol(bw->cursor);
542 joe_snprintf_2((char *)buf, JOE_MSGBUFSIZE, "\t^K %c\tMacro %d", x + '0', x);
543 binss(bw->cursor, buf);
544 p_goto_eol(bw->cursor);
545 binsc(bw->cursor, '\n');
551 int uplay(BW *bw, int c)
553 if (c >= '0' && c <= '9')
554 return doplay(bw, c, NULL, NULL);
555 else if (mkqwna(bw->parent, sc("Play-"), doplay, NULL, NULL, NULL))
561 /* Repeat-count setting */
563 static int doarg(BW *bw, unsigned char *s, void *object, int *notify)
572 msgnw(bw->parent, merrt);
583 if (wmkpw(bw->parent, UC "No. times to repeat next command (^C to abort): ", NULL, doarg, NULL, NULL, utypebw, NULL, NULL, locale_map))
592 static int douarg(BW *bw, int c, void *object, int *notify)
596 else if (c >= '0' && c <= '9')
597 unaarg = unaarg * 10 + c - '0';
598 else if (c == 'U' - '@')
603 else if (c == 7 || c == 3 || c == 32) {
622 joe_snprintf_2((char *)msgbuf, JOE_MSGBUFSIZE, "Repeat %s%d", negarg ? "-" : "", unaarg);
623 if (mkqwna(bw->parent, sz(msgbuf), douarg, NULL, NULL, notify))
629 int uuarg(BW *bw, int c)
633 if ((c >= '0' && c <= '9') || c == '-')
634 return douarg(bw, c, NULL, NULL);
635 else if (mkqwna(bw->parent, sc("Repeat"), douarg, NULL, NULL, NULL))