1 /*
2 * Keyboard macros
3 * Copyright
4 * (C) 1992 Joseph H. Allen
5 *
6 * This file is part of JOE (Joe's Own Editor)
7 */
8 #include "types.h"
9
10 MACRO *freemacros = NULL;
11
12 /* Create a macro */
13
mkmacro(int k,int flg,ptrdiff_t n,CMD * cmd)14 MACRO *mkmacro(int k, int flg, ptrdiff_t n, CMD *cmd)
15 {
16 MACRO *macro;
17
18 if (!freemacros) {
19 ptrdiff_t x;
20
21 macro = (MACRO *) joe_malloc(SIZEOF(MACRO) * 64);
22 for (x = 0; x != 64; ++x) {
23 macro[x].steps = (MACRO **) freemacros;
24 freemacros = macro + x;
25 }
26 }
27 macro = freemacros;
28 freemacros = (MACRO *) macro->steps;
29 macro->steps = NULL;
30 macro->size = 0;
31 macro->flg = flg;
32 macro->n = n;
33 macro->cmd = cmd;
34 macro->k = k;
35 macro->what = 0;
36 return macro;
37 }
38
39 /* Eliminate a macro */
40
rmmacro(MACRO * macro)41 void rmmacro(MACRO *macro)
42 {
43 if (macro) {
44 if (macro->steps) {
45 ptrdiff_t x;
46
47 for (x = 0; x != macro->n; ++x)
48 rmmacro(macro->steps[x]);
49 joe_free(macro->steps);
50 }
51 macro->steps = (MACRO **) freemacros;
52 freemacros = macro;
53 }
54 }
55
56 /* Add a step to block macro */
57
addmacro(MACRO * macro,MACRO * m)58 void addmacro(MACRO *macro, MACRO *m)
59 {
60 if (macro->n == macro->size) {
61 if (macro->steps)
62 macro->steps = (MACRO **) joe_realloc(macro->steps, (macro->size += 8) * SIZEOF(MACRO *));
63 else
64 macro->steps = (MACRO **) joe_malloc((macro->size = 8) * SIZEOF(MACRO *));
65 }
66 macro->steps[macro->n++] = m;
67 }
68
69 /* Duplicate a macro */
70
dupmacro(MACRO * mac)71 MACRO *dupmacro(MACRO *mac)
72 {
73 MACRO *m = mkmacro(mac->k, mac->flg, mac->n, mac->cmd);
74
75 if (mac->steps) {
76 ptrdiff_t x;
77
78 m->steps = (MACRO **) joe_malloc((m->size = mac->n) * SIZEOF(MACRO *));
79 for (x = 0; x != m->n; ++x)
80 m->steps[x] = dupmacro(mac->steps[x]);
81 }
82 return m;
83 }
84
85 /* Set key part of macro */
86
macstk(MACRO * m,int k)87 MACRO *macstk(MACRO *m, int k)
88 {
89 if (k != -1)
90 m->k = k;
91 return m;
92 }
93
94 /* Set flg part of macro */
95
macsta(MACRO * m,int a)96 MACRO *macsta(MACRO *m, int a)
97 {
98 m->flg = a;
99 return m;
100 }
101
102 /* Parse text into a macro
103 * sta is set to: ending position in buffer for no error.
104 * -1 for syntax error
105 * -2 for need more input
106 *
107 * Set secure to allow only commands which being with "shell_".
108 * Also, secure_type will be used instead of type.
109 */
110
mparse(MACRO * m,const char * buf,ptrdiff_t * sta,int secure)111 MACRO *mparse(MACRO *m, const char *buf, ptrdiff_t *sta, int secure)
112 {
113 const char *org = buf;
114 int bf[1024];
115 char bf1[1024];
116 int x;
117
118 macroloop:
119
120 /* Skip whitespace */
121 parse_ws(&buf, 0);
122
123 /* If the buffer is only whitespace then treat as unknown command */
124 if (!*buf) {
125 *sta = -1;
126 return NULL;
127 }
128
129 /* Do we have a string? */
130 if (parse_Zstring(&buf, bf, SIZEOF(bf)/SIZEOF(bf[0])) >= 0) {
131 for (x = 0; bf[x]; ++x) {
132 if (m) {
133 if (!m->steps) {
134 MACRO *macro = m;
135
136 m = mkmacro(NO_MORE_DATA, 0, 0, NULL);
137 addmacro(m, macro);
138 }
139 } else
140 m = mkmacro(NO_MORE_DATA, 0, 0, NULL);
141 addmacro(m, mkmacro(bf[x], 0, 0, secure ? findcmd("secure_type") : findcmd("type")));
142 }
143 } else { /* Do we have a command? */
144 x = 0;
145 while (*buf && *buf != '#' && *buf != '!' && *buf != '~' && *buf !='-' && *buf != ',' &&
146 *buf != ' ' && *buf != '\t' && *buf != '\n' && *buf != '\r') {
147 if (x != SIZEOF(bf1) - 1)
148 bf1[x++] = *buf;
149 ++buf;
150 }
151 bf1[x] = 0;
152 if (x) {
153 CMD *cmd;
154 int flg = 0;
155
156 if (!secure || !zncmp(bf1, "shell_", 6))
157 cmd = findcmd(bf1);
158 else
159 cmd = 0;
160
161 /* Parse flags */
162 while (*buf == '-' || *buf == '!' || *buf == '#' || *buf == '~') {
163 if (*buf == '-') flg |= 1;
164 if (*buf == '!') flg |= 2;
165 if (*buf == '#') flg |= 4;
166 if (*buf == '~') flg |= 8;
167 ++buf;
168 }
169
170 if (!cmd) {
171 *sta = -1;
172 return NULL;
173 } else if (m) {
174 if (!m->steps) {
175 MACRO *macro = m;
176
177 m = mkmacro(NO_MORE_DATA, 0, 0, NULL);
178 addmacro(m, macro);
179 }
180 addmacro(m, mkmacro(NO_MORE_DATA, flg, 0, cmd));
181 } else
182 m = mkmacro(NO_MORE_DATA, flg, 0, cmd);
183 } else { /* not a valid command */
184 *sta = -1;
185 return NULL;
186 }
187 }
188
189 /* Skip whitespace */
190 parse_ws(&buf, 0);
191
192 /* Do we have a comma? */
193 if (*buf == ',') {
194 ++buf;
195 parse_ws(&buf, 0);
196 if (*buf && *buf != '\r' && *buf != '\n')
197 goto macroloop;
198 *sta = -2;
199 return m;
200 }
201
202 /* Done */
203 *sta = buf - org;
204 return m;
205 }
206
207 /* Convert macro to text */
208
unescape(char * ptr,int c)209 char *unescape(char *ptr, int c)
210 {
211 if (c == '"') {
212 *ptr++ = '\\';
213 *ptr++ = '"';
214 } else if (c == '\\') {
215 *ptr++ = '\\';
216 *ptr++ = '\\';
217 } else if (c == '\'') {
218 *ptr++ = '\\';
219 *ptr++ = '\'';
220 } else if (c < 32 || c == 127) {
221 *ptr++ = '\\';
222 *ptr++ = 'x';
223 *ptr++ = "0123456789ABCDEF"[15 & (c >> 4)];
224 *ptr++ = "0123456789ABCDEF"[15 & c];
225 } else {
226 ptr += utf8_encode(ptr, c);
227 }
228 return ptr;
229 }
230
domtext(MACRO * m,char * ptr,int * first,int * instr)231 static char *domtext(MACRO *m, char *ptr, int *first, int *instr)
232 {
233 ptrdiff_t x;
234
235 if (!m)
236 return ptr;
237 if (m->steps) {
238 for (x = 0; x != m->n; ++x)
239 ptr = domtext(m->steps[x], ptr, first, instr);
240 } else {
241 if (*instr && zcmp(m->cmd->name, "type")) {
242 *ptr++ = '\"';
243 *instr = 0;
244 }
245 if (*first)
246 *first = 0;
247 else if (!*instr)
248 *ptr++ = ',';
249 if (!zcmp(m->cmd->name, "type")) {
250 if (!*instr) {
251 *ptr++ = '\"';
252 *instr = 1;
253 }
254 ptr = unescape(ptr, m->k);
255 } else {
256 for (x = 0; m->cmd->name[x]; ++x)
257 *ptr++ = m->cmd->name[x];
258 if (!zcmp(m->cmd->name, "play") || !zcmp(m->cmd->name, "gomark") || !zcmp(m->cmd->name, "setmark") || !zcmp(m->cmd->name, "record") || !zcmp(m->cmd->name, "uarg")) {
259 *ptr++ = ',';
260 *ptr++ = '"';
261 ptr = unescape(ptr, m->k);
262 *ptr++ = '"';
263 }
264 }
265 }
266 return ptr;
267 }
268
mtext(char * s,MACRO * m)269 char *mtext(char *s, MACRO *m)
270 {
271 int first = 1;
272 int instr = 0;
273 char *e = domtext(m, s, &first, &instr);
274 if (instr)
275 *e++ = '\"';
276 *e = 0;
277 return s;
278 }
279
280 /* Keyboard macro recorder */
281
282 static MACRO *kbdmacro[10];
283 static int playmode[10];
284
285 struct recmac *recmac = NULL;
286
unmac(void)287 static void unmac(void)
288 {
289 if (recmac)
290 rmmacro(recmac->m->steps[--recmac->m->n]);
291 }
292
chmac(void)293 void chmac(void)
294 {
295 if (recmac && recmac->m->n)
296 recmac->m->steps[recmac->m->n - 1]->k = 3;
297 }
298
record(MACRO * m,int k)299 static void record(MACRO *m, int k)
300 {
301 if (recmac)
302 addmacro(recmac->m, macstk(dupmacro(m), k));
303 }
304
305 static int ifdepth=0; /* JM: Nesting level of if cmds */
306 static int ifflag=1; /* JM: Truth flag for if */
307 static int iffail=0; /* JM: Depth where ifflag became 0 */
308
309 /* Suspend macro record/play to allow for user input.
310 *
311 * Stop recording current macro, make recursive call to edit loop, which
312 * runs until dialog is complete, then continue with macro recording.
313 *
314 * When the macro is played, edit loop is run as a subroutine until dialog
315 * is complete, then uquery returns, which continues macro execution.
316 *
317 * Completion of a dialog is indicated with 'notify' flag�(look at interactive
318 * dialogs in ufile.c).
319 */
320
uquery(W * w,int k)321 int uquery(W *w, int k)
322 {
323 int ret;
324 int oid=ifdepth, oifl=ifflag, oifa=iffail;
325 struct recmac *tmp = recmac;
326
327 recmac = NULL;
328 ret = edloop(1);
329 recmac = tmp;
330 ifdepth = oid; ifflag = oifl; iffail = oifa;
331 return ret;
332 }
333
334 /* Macro execution */
335
336 MACRO *curmacro = NULL; /* Set if we're in a macro */
337 static int macroptr;
338 static int arg = 0; /* Repeat argument */
339 static int argset = 0; /* Set if 'arg' is set */
340
341 /* Execute a macro which is just a simple command */
342
exsimple(MACRO * m,int myarg,int u,int k)343 static int exsimple(MACRO *m, int myarg, int u, int k)
344 {
345 CMD *cmd = m->cmd;
346 int flg = 0; /* set if we should not try to merge minor changes into single undo record */
347 int ret = 0;
348
349 /* Find command to execute if repeat argument is negative */
350 if (myarg < 0) {
351 myarg = -myarg;
352 if (cmd->negarg)
353 cmd = findcmd(cmd->negarg);
354 else
355 myarg = 0; /* Do not execute */
356 }
357
358 /* Check if command doesn't like an arg... */
359 if (myarg != 1 && !cmd->arg)
360 myarg = 0; /* Do not execute */
361
362 if (myarg != 1 || !(cmd->flag & EMINOR)
363 || maint->curwin->watom->what == TYPEQW) /* Undo work right for s & r */
364 flg = 1;
365
366 if (ifflag || (cmd->flag&EMETA)) {
367 if (flg && u)
368 umclear();
369 /* Repeat... */
370 while (myarg-- && !leave && !ret)
371 ret = execmd(cmd, m->k != NO_MORE_DATA ? m->k : k);
372 if (leave)
373 return ret;
374 if (flg && u)
375 umclear();
376 if (u)
377 undomark();
378 }
379
380 return ret;
381 }
382
383 int current_arg = 1;
384 int current_arg_set = 0;
385
exmacro(MACRO * m,int u,int k)386 int exmacro(MACRO *m, int u, int k)
387 {
388 int larg;
389 int negarg = 0;
390 int oid=0, oifl=0, oifa=0;
391 int ret = 0;
392 int main_ret = 0;
393 int o_arg_set = argset;
394 int o_arg = arg;
395
396 /* Take argument */
397
398 if (argset) {
399 larg = arg;
400 arg = 0;
401 argset = 0;
402 } else {
403 larg = 1;
404 }
405
406 /* Just a simple command? */
407
408 if (!m->steps) {
409 return exsimple(m, larg, u, k);
410 }
411
412 /* Must be a real macro then... */
413
414 if (larg < 0) {
415 larg = -larg;
416 negarg = 1;
417 }
418
419 if (ifflag) {
420 if (u)
421 umclear();
422 /* Repeat... */
423 while (larg && !leave && !ret) {
424 MACRO *tmpmac = curmacro;
425 int tmpptr = macroptr;
426 int x = 0;
427 int stk = nstack;
428
429 /* Steps of macro... */
430 while (m && x != m->n && !leave && !ret) {
431 MACRO *d;
432 int tmp_arg;
433 int tmp_set;
434
435 d = m->steps[x++];
436 curmacro = m;
437 macroptr = x;
438 tmp_arg = current_arg;
439 tmp_set = current_arg_set;
440 current_arg = o_arg;
441 current_arg_set = o_arg_set;
442
443 if(d->steps) oid=ifdepth, oifl=ifflag, oifa=iffail, ifdepth=iffail=0;
444
445 /* If this step wants to know about negative args... */
446 if ((d->flg&4)) {
447 argset = o_arg_set;
448 arg = o_arg;
449 larg = 1;
450 } else if ((d->flg&1) && negarg) {
451 if (argset) {
452 arg = -arg;
453 } else {
454 argset = 1;
455 arg = -1;
456 }
457 }
458
459 if (d->flg&8) {
460 larg = 1;
461 }
462
463 /* This is the key step of the macro... */
464 if (d->flg&2)
465 main_ret = exmacro(d, 0, k);
466 else
467 ret = exmacro(d, 0, k);
468
469 if(d->steps) ifdepth=oid, ifflag=oifl, iffail=oifa;
470 current_arg = tmp_arg;
471 current_arg_set = tmp_set;
472 m = curmacro;
473 x = macroptr;
474 }
475 curmacro = tmpmac;
476 macroptr = tmpptr;
477
478 /* Pop ^KB ^KK stack */
479 while (nstack > stk)
480 upop(NULL, 0);
481 --larg;
482 }
483 ret |= main_ret;
484
485 if (leave)
486 return ret;
487 if (u)
488 umclear();
489 if (u)
490 undomark();
491 }
492
493 return ret;
494 }
495
496 /* Execute a macro - for user typing */
497 /* Records macro in macro recorder, resets if */
498
exemac(MACRO * m,int k)499 int exemac(MACRO *m, int k)
500 {
501 record(m, k);
502 ifflag=1; ifdepth=iffail=0;
503 return exmacro(m, 1, k);
504 }
505
506 /* Keyboard macro user routines */
507
dorecord(W * w,int c,void * object,int * notify)508 static int dorecord(W *w, int c, void *object, int *notify)
509 {
510 int n;
511 struct recmac *r;
512
513 if (notify)
514 *notify = 1;
515 if (c > '9' || c < '0') {
516 nungetc(c);
517 return -1;
518 }
519 for (n = 0; n != 10; ++n)
520 if (playmode[n])
521 return -1;
522 r = (struct recmac *) joe_malloc(SIZEOF(struct recmac));
523
524 r->m = mkmacro(NO_MORE_DATA, 0, 0, NULL);
525 r->next = recmac;
526 r->n = c - '0';
527 recmac = r;
528 return 0;
529 }
530
urecord(W * w,int c)531 int urecord(W *w, int c)
532 {
533 if (c >= '0' && c <= '9')
534 return dorecord(w, c, NULL, NULL);
535 else if (mkqw(w, sz(joe_gettext(_("Macro to record (0-9 or %{abort} to abort): "))), dorecord, NULL, NULL, NULL))
536 return 0;
537 else
538 return -1;
539 }
540
541 extern time_t timer_macro_delay;
542 extern MACRO *timer_macro;
543
dotimer1(W * w,char * s,void * object,int * notify)544 static int dotimer1(W *w, char *s, void *object, int *notify)
545 {
546 BW *bw;
547 long num;
548 if (notify)
549 *notify = 1;
550 WIND_BW(bw, w);
551 num = (long)calc(bw, s, 0);
552 if (merr) {
553 msgnw(w, merr);
554 return -1;
555 }
556 timer_macro_delay = num;
557 vsrm(s);
558 return 0;
559 }
560
dotimer(W * w,int c,void * object,int * notify)561 static int dotimer(W *w, int c, void *object, int *notify)
562 {
563 if (c < '0' || c > '9')
564 return -1;
565 c -= '0';
566 if (kbdmacro[c]) {
567 if (timer_macro) {
568 rmmacro(timer_macro);
569 }
570 timer_macro = dupmacro(kbdmacro[c]);
571 timer_macro_delay = 0;
572 if (wmkpw(w, joe_gettext(_("Delay in seconds between macro invocation (%{abort} to abort): ")), NULL, dotimer1, NULL, NULL, math_cmplt, NULL, NULL, utf8_map,0))
573 return 0;
574 else
575 return -1;
576 } else {
577 return -1;
578 }
579 }
580
utimer(W * w,int k)581 int utimer(W *w, int k)
582 {
583 timer_macro_delay = 0;
584 if (timer_macro) {
585 rmmacro(timer_macro);
586 timer_macro = 0;
587 }
588 if (mkqw(w, sz(joe_gettext(_("Macro to play (0-9 or %{abort} to abort): "))), dotimer, NULL, NULL, NULL))
589 return 0;
590 else
591 return -1;
592 }
593
ustop(W * w,int k)594 int ustop(W *w, int k)
595 {
596 unmac();
597 if (recmac) {
598 struct recmac *r = recmac;
599 MACRO *m;
600
601 dostaupd = 1;
602 recmac = r->next;
603 if (kbdmacro[r->n])
604 rmmacro(kbdmacro[r->n]);
605 kbdmacro[r->n] = r->m;
606 if (recmac)
607 record(m = mkmacro(r->n + '0', 0, 0, findcmd("play")), NO_MORE_DATA), rmmacro(m);
608 joe_free(r);
609 }
610 return 0;
611 }
612
doplay(W * w,int c,void * object,int * notify)613 static int doplay(W *w, int c, void *object, int *notify)
614 {
615 if (notify)
616 *notify = 1;
617 if (c >= '0' && c <= '9') {
618 int ret;
619
620 c -= '0';
621 if (playmode[c] || !kbdmacro[c])
622 return -1;
623 playmode[c] = 1;
624 ret = exmacro(kbdmacro[c], 0, c);
625 playmode[c] = 0;
626 return ret;
627 } else {
628 nungetc(c);
629 return -1;
630 }
631 }
632
umacros(W * w,int k)633 int umacros(W *w, int k)
634 {
635 int x;
636 char buf[1024];
637 BW *bw;
638 WIND_BW(bw, w);
639
640 p_goto_eol(bw->cursor);
641 for (x = 0; x != 10; ++x)
642 if (kbdmacro[x]) {
643 mtext(buf, kbdmacro[x]);
644 binss(bw->cursor, buf);
645 p_goto_eol(bw->cursor);
646 joe_snprintf_2(buf, JOE_MSGBUFSIZE, "\t^K %c\tMacro %d", x + '0', x);
647 binss(bw->cursor, buf);
648 p_goto_eol(bw->cursor);
649 binsc(bw->cursor, '\n');
650 pgetc(bw->cursor);
651 }
652 return 0;
653 }
654
save_macros(FILE * f)655 void save_macros(FILE *f)
656 {
657 int x;
658 char buf[1024];
659 for(x = 0; x!= 10; ++x)
660 if(kbdmacro[x]) {
661 mtext(buf, kbdmacro[x]);
662 fprintf(f," %d ",x);
663 emit_string(f,buf,zlen(buf));
664 fprintf(f,"\n");
665 }
666 fprintf(f,"done\n");
667 }
668
load_macros(FILE * f)669 void load_macros(FILE *f)
670 {
671 char buf[1024];
672 char bf[1024];
673 while(fgets(buf,sizeof(buf),f) && zcmp(buf,"done\n")) {
674 const char *p = buf;
675 int n;
676 ptrdiff_t len;
677 ptrdiff_t sta;
678 parse_ws(&p, '#');
679 if(!parse_int(&p,&n)) {
680 parse_ws(&p, '#');
681 len = parse_string(&p,bf,SIZEOF(bf));
682 if (len>0)
683 kbdmacro[n] = mparse(NULL,bf,&sta,0);
684 }
685 }
686 }
687
uplay(W * w,int c)688 int uplay(W *w, int c)
689 {
690 if (c >= '0' && c <= '9')
691 return doplay(w, c, NULL, NULL);
692 else if (mkqwna(w, sz(joe_gettext(_("Play-"))), doplay, NULL, NULL, NULL))
693 return 0;
694 else
695 return -1;
696 }
697
698 /* Repeat-count setting */
699
doarg(W * w,char * s,void * object,int * notify)700 static int doarg(W *w, char *s, void *object, int *notify)
701 {
702 BW *bw;
703 int num;
704 WIND_BW(bw, w);
705
706 if (notify)
707 *notify = 1;
708 num = (int)calc(bw, s, 1);
709 if (merr) {
710 msgnw(w, merr);
711 return -1;
712 }
713 arg = num;
714 argset = 1;
715 vsrm(s);
716 return 0;
717 }
718
uarg(W * w,int k)719 int uarg(W *w, int k)
720 {
721 if (wmkpw(w, joe_gettext(_("No. times to repeat next command (%{abort} to abort): ")), NULL, doarg, NULL, NULL, math_cmplt, NULL, NULL, utf8_map,0))
722 return 0;
723 else
724 return -1;
725 }
726
doif(W * w,char * s,void * object,int * notify)727 static int doif(W *w,char *s,void *object,int *notify)
728 {
729 int num;
730 BW *bw;
731 if (notify) *notify=1;
732 WIND_BW(bw, w);
733 num = (int)calc(bw, s, 0);
734 if (merr) {
735 msgnw(w, merr);
736 return -1;
737 }
738 ifflag = (num ? 1 : 0);
739 iffail = ifdepth;
740 vsrm(s);
741 return 0;
742 }
743
ifabrt(W * w,void * object)744 static int ifabrt(W *w, void *object)
745 {
746 ifdepth--;
747 return 0;
748 }
749
uif(W * w,int k)750 int uif(W *w, int k)
751 {
752 ifdepth++;
753 if (!ifflag) return 0;
754 if (wmkpw(w,joe_gettext(_("If (%{abort} to abort): ")),NULL,doif,NULL,ifabrt,math_cmplt,NULL,NULL,utf8_map,0)) return 0;
755 else return -1;
756 }
757
uelsif(W * w,int k)758 int uelsif(W *w, int k)
759 {
760 if (!ifdepth) {
761 msgnw(w,joe_gettext(_("Elsif without if")));
762 return -1;
763 } else if(ifflag) {
764 ifflag=iffail=0; /* don't let the next else/elsif get run */
765 } else if(ifdepth == iffail) {
766 ifflag=1; /* so the script can type the condition :) */
767 if(wmkpw(w,joe_gettext(_("Else if: ")),NULL,doif,NULL,NULL,math_cmplt,NULL,NULL,locale_map,0)) return 0;
768 else return -1;
769 }
770 return 0;
771 }
772
uelse(W * w,int k)773 int uelse(W *w, int k)
774 {
775 if (!ifdepth) {
776 msgnw(w,joe_gettext(_("Else without if")));
777 return -1;
778 } else if(ifdepth == iffail) {
779 ifflag = !ifflag;
780 }
781 return 0;
782 }
783
uendif(W * w,int k)784 int uendif(W *w, int k)
785 {
786 if(!ifdepth) {
787 msgnw(w,joe_gettext(_("Endif without if")));
788 return -1;
789 }
790 if(iffail==ifdepth) iffail--, ifflag=1;
791 ifdepth--;
792 if(ifdepth==0) ifflag=1;
793 return 0;
794 }
795
796
797 int unaarg;
798 int negarg;
799
douarg(W * w,int c,void * object,int * notify)800 static int douarg(W *w, int c, void *object, int *notify)
801 {
802 if (c == '-')
803 negarg = !negarg;
804 else if (c >= '0' && c <= '9')
805 unaarg = unaarg * 10 + c - '0';
806 else if (c == 'U' - '@')
807 if (unaarg)
808 unaarg *= 4;
809 else
810 unaarg = 16;
811 else if (c == 7 || c == 3 || c == 32) {
812 if (notify)
813 *notify = 1;
814 return -1;
815 } else {
816 nungetc(c);
817 if (unaarg)
818 arg = unaarg;
819 else if (negarg)
820 arg = 1;
821 else
822 arg = 4;
823 if (negarg)
824 arg = -arg;
825 argset = 1;
826 if (notify)
827 *notify = 1;
828 return 0;
829 }
830 joe_snprintf_2(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("Repeat %s%d")), negarg ? "-" : "", unaarg);
831 if (mkqwna(w, sz(msgbuf), douarg, NULL, NULL, notify))
832 return 0;
833 else
834 return -1;
835 }
836
uuarg(W * w,int c)837 int uuarg(W *w, int c)
838 {
839 unaarg = 0;
840 negarg = 0;
841 if ((c >= '0' && c <= '9') || c == '-')
842 return douarg(w, c, NULL, NULL);
843 else if (mkqwna(w, sz(joe_gettext(_("Repeat"))), douarg, NULL, NULL, NULL))
844 return 0;
845 else
846 return -1;
847 }
848