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