1 /*
2  *  cmd2.c  --  back-end for the various #commands
3  *
4  *  (created: Massimiliano Ghilardi (Cosmos), Aug 14th, 1998)
5  *
6  *  Copyright (C) 1998 by Massimiliano Ghilardi
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  */
14 
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <ctype.h>
21 #include <time.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/time.h>
25 #include <unistd.h>
26 #include <errno.h>
27 
28 #ifdef USE_REGEXP
29 # include <regex.h>
30 #endif
31 
32 int strcasecmp();
33 int select();
34 
35 #include "defines.h"
36 #include "main.h"
37 #include "utils.h"
38 #include "beam.h"
39 #include "edit.h"
40 #include "list.h"
41 #include "map.h"
42 #include "tcp.h"
43 #include "tty.h"
44 #include "eval.h"
45 
46 /* anyone knows if ANSI 6429 talks about more than 8 colors? */
47 static char *colornames[] = {
48     "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
49     "BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE", "none"
50 };
51 
52 /*
53  * show defined aliases
54  */
__P0(void)55 void show_aliases __P0 (void)
56 {
57     aliasnode *p;
58     char buf[BUFSIZE];
59 
60     PRINTF("#%s alias%s defined%c\n", sortedaliases ? "the following" : "no",
61 	       (sortedaliases && !sortedaliases->snext) ? " is" : "es are",
62 	       sortedaliases ? ':' : '.');
63     reverse_sortedlist((sortednode **)&sortedaliases);
64     for (p = sortedaliases; p; p = p->snext) {
65 	escape_specials(buf, p->name);
66 	tty_printf("#alias %s%s%s%s=%s\n",
67 			p->active ? "" : "(disabled) ",
68 			buf, group_delim, p->group == NULL ? "*" : p->group, p->subst);
69     }
70     reverse_sortedlist((sortednode **)&sortedaliases);
71 }
72 
73 /*
74  * check if an alias name contains dangerous things.
75  * return 1 if illegal name (and print reason).
76  * if valid, print a warning for unbalanced () {} and ""
77  */
__P1(char *,name)78 static int check_alias __P1 (char *,name)
79 {
80     char *p = name, c;
81     enum { NORM, ESCAPE } state = NORM;
82     int quotes=0, paren=0, braces=0, ok = 1;
83 
84     if (!*p) {
85 	PRINTF("#illegal alias name: is empty!\n");
86 	error = INVALID_NAME_ERROR;
87 	return 1;
88     }
89     if (*p == '{') {
90 	PRINTF("#illegal beginning '{' in alias name: \"%s\"\n", name);
91 	error = INVALID_NAME_ERROR;
92 	return 1;
93     }
94     if (strchr(name, ' ')) {
95 	PRINTF("#illegal spaces in alias name: \"%s\"\n", name);
96 	error = INVALID_NAME_ERROR;
97 	return 1;
98     }
99 
100     for (; ok && (c = *p); p++) switch (state) {
101       case NORM:
102 	if (c == ESC)
103 	    state = ESCAPE;
104 	else if (quotes) {
105 	    if (c == '\"')
106 		quotes = 0;
107 	}
108 	else if (c == '\"')
109 	    quotes = 1;
110 	else if (c == ')')
111 	    paren--;
112 	else if (c == '(')
113 	    paren++;
114 	else if (c == '}')
115 	    braces--;
116 	else if (c == '{')
117 	    braces++;
118 	else if (c == CMDSEP && !paren && !braces)
119 	    ok = 0;
120 	break;
121       case ESCAPE:
122 	if (c == ESC)
123 	    state = ESCAPE;
124 	else /* if (c == ESC2 || c != ESC2) */
125 	    state = NORM;
126       default:
127 	break;
128     }
129 
130     if (!ok) {
131 	PRINTF("#illegal non-escaped ';' in alias name: \"%s\"\n", name);
132 	error = INVALID_NAME_ERROR;
133 	return 1;
134     }
135 
136     if (quotes || paren || braces) {
137 	PRINTF("#warning: unbalanced%s%s%s in alias name \"%s\" may cause problems\n",
138 	       quotes ? " \"\"" : "", paren ? " ()" : "", braces ? " {}" : "", name);
139     }
140 
141     return 0;
142 }
143 
144 
145 /*
146  * parse the #alias command
147  */
__P1(char *,str)148 void parse_alias __P1 (char *,str)
149 {
150     char *left, *right, *group;
151     aliasnode **np, *p;
152 
153     left = str = skipspace(str);
154 
155     str = first_valid(str, '=');
156 
157     if (*str == '=') {
158         *str = '\0';
159         right = ++str;
160         unescape(left);
161 
162     	/* break out group name (if present) */
163         group = strstr( left, group_delim );
164 	if( group ) {
165 	    *group = 0;
166 	    group += strlen( group_delim );
167 	}
168 
169 	if (check_alias(left))
170 	    return;
171         p = *(np = lookup_alias(left));
172         if (!*str) {
173             /* delete alias */
174             if (p) {
175                 if (opt_info) {
176                     PRINTF("#deleting alias: %s=%s\n", left, p->subst);
177                 }
178                 delete_aliasnode(np);
179             } else {
180                 PRINTF("#unknown alias, cannot delete: \"%s\"\n", left);
181             }
182         } else {
183             /* add/redefine alias */
184 
185 	    /* direct recursion is supported (alias CAN be defined by itself) */
186             if (p) {
187                 free(p->subst);
188                 p->subst = my_strdup(right);
189             } else
190                 add_aliasnode(left, right);
191 
192 	    /* get alias again to add group (if needed)
193 	     * don't take the lookup penalty though if not changing groups */
194 	    if( group != NULL ) {
195                 np = lookup_alias(left);
196 		if( (*np)->group != NULL )
197 			free((*np)->group);
198 
199 		if ( *group == '\0' || strcmp(group,"*") == 0 )
200 			group = NULL;
201 
202 	    	(*np)->group = my_strdup(group);
203 	    }
204 
205             if (opt_info) {
206                 PRINTF("#%s alias in group '%s': %s=%s\n", p ? "changed" : "new",
207 			group == NULL ? "*" : group, left, right);
208             }
209         }
210     } else {
211         /* show alias */
212 
213         *str = '\0';
214         unescape(left);
215 	if (check_alias(left))
216 	    return;
217         np = lookup_alias(left);
218         if (*np) {
219 	    char buf[BUFSIZE];
220             escape_specials(buf, left);
221             snprintf(inserted_next, BUFSIZE, "#alias %s%s%s=%s",
222                 buf,
223                 group_delim,
224                 (*np)->group == NULL ? "*" : (*np)->group,
225 		        (*np)->subst);
226         } else {
227             PRINTF("#unknown alias, cannot show: \"%s\"\n", left);
228         }
229     }
230 }
231 
232 /*
233  * delete an action node
234  */
__P1(actionnode **,nodep)235 static void delete_action __P1 (actionnode **,nodep)
236 {
237     if (opt_info) {
238         PRINTF("#deleting action: >%c%s %s\n", (*nodep)->active ?
239 		   '+' : '-', (*nodep)->label, (*nodep)->pattern);
240     }
241     delete_actionnode(nodep);
242 }
243 
244 /*
245  * delete a prompt node
246  */
__P1(actionnode **,nodep)247 static void delete_prompt __P1 (actionnode **,nodep)
248 {
249     if (opt_info) {
250         PRINTF("#deleting prompt: >%c%s %s\n", (*nodep)->active ?
251 		   '+' : '-', (*nodep)->label, (*nodep)->pattern);
252     }
253     delete_promptnode(nodep);
254 }
255 
256 /*
257  * create new action
258  */
__P6(char *,label,char *,pattern,char *,command,int,active,int,type,void *,q)259 static void add_new_action __P6 (char *,label, char *,pattern, char *,command, int,active, int,type, void *,q)
260 {
261     add_actionnode(pattern, command, label, active, type, q);
262     if (opt_info) {
263         PRINTF("#new action: %c%c%s %s=%s\n",
264 		   action_chars[type],
265 		   active ? '+' : '-', label,
266 		   pattern, command);
267     }
268 }
269 
270 /*
271  * create new prompt
272  */
__P6(char *,label,char *,pattern,char *,command,int,active,int,type,void *,q)273 static void add_new_prompt __P6 (char *,label, char *,pattern, char *,command, int,active, int,type, void *,q)
274 {
275     add_promptnode(pattern, command, label, active, type, q);
276     if (opt_info) {
277         PRINTF("#new prompt: %c%c%s %s=%s\n",
278 		   action_chars[type],
279 		   active ? '+' : '-', label,
280 		   pattern, command);
281     }
282 }
283 
284 /*
285  * add an action with numbered label
286  */
__P4(char *,pattern,char *,command,int,type,void *,q)287 static void add_anonymous_action __P4 (char *,pattern, char *,command, int,type, void *,q)
288 {
289     static int last = 0;
290     char label[16];
291     do {
292         sprintf(label, "%d", ++last);
293     } while (*lookup_action(label));
294     add_new_action(label, pattern, command, 1, type, q);
295 }
296 
297 #define ONPROMPT (onprompt ? "prompt" : "action")
298 
299 /*
300  * change fields of an existing action node
301  * pattern or commands can be NULL if no change
302  */
__P6(actionnode *,node,char *,pattern,char *,command,int,type,void *,q,int,onprompt)303 static void change_actionorprompt __P6 (actionnode *,node, char *,pattern, char *,command, int,type, void *,q, int,onprompt)
304 {
305 #ifdef USE_REGEXP
306     if (node->type == ACTION_REGEXP && node->regexp) {
307 	regfree((regex_t *)(node->regexp));
308 	free(node->regexp);
309     }
310     node->regexp = q;
311 #endif
312     if (pattern) {
313         free(node->pattern);
314         node->pattern = my_strdup(pattern);
315 	node->type = type;
316     }
317     if (command) {
318         free(node->command);
319         node->command = my_strdup(command);
320     }
321 
322     if (opt_info) {
323         PRINTF("#changed %s %c%c%s %s=%s\n", ONPROMPT,
324 		   action_chars[node->type],
325 		   node->active ? '+' : '-',
326 		   node->label, node->pattern, node->command);
327     }
328 }
329 
330 /*
331  * show defined actions
332  */
__P0(void)333 void show_actions __P0 (void)
334 {
335     actionnode *p;
336 
337     PRINTF("#%s action%s defined%c\n", actions ? "the following" : "no",
338 	       (actions && !actions->next) ? " is" : "s are", actions ? ':' : '.');
339     for (p = actions; p; p = p->next)
340 	tty_printf("#action %c%c%s%s%s %s=%s\n",
341 		   action_chars[p->type],
342 		   p->active ? '+' : '-', p->label,
343            group_delim,
344 		   p->group == NULL ? "*" : p->group,
345 		   p->pattern,
346 		   p->command);
347 }
348 
349 /*
350  * show defined prompts
351  */
__P0(void)352 void show_prompts __P0 (void)
353 {
354     promptnode *p;
355 
356     PRINTF("#%s prompt%s defined%c\n", prompts ? "the following" : "no",
357 	       (prompts && !prompts->next) ? " is" : "s are", prompts ? ':' : '.');
358     for (p = prompts; p; p = p->next)
359 	tty_printf("#prompt %c%c%s %s=%s\n",
360 		   action_chars[p->type],
361 		   p->active ? '+' : '-', p->label,
362 		   p->pattern, p->command);
363 }
364 
365 /*
366  * parse the #action and #prompt commands
367  * this function is too damn complex because of the hairy syntax. it should be
368  * split up or rewritten as an fsm instead.
369  */
__P2(char *,str,int,onprompt)370 void parse_action __P2 (char *,str, int,onprompt)
371 {
372     char *p, label[BUFSIZE], pattern[BUFSIZE], *command, *group;
373     actionnode **np = NULL;
374     char sign, assign, hastail;
375     char active, type = ACTION_WEAK, kind;
376     void *regexp = 0;
377 
378     sign = *(p = skipspace(str));
379     if (!sign) {
380 	PRINTF("%s: no arguments given\n", ONPROMPT);
381 	return;
382     }
383 
384     str = p + 1;
385 
386     switch (sign) {
387       case '+':
388       case '-':		/* edit */
389       case '<':		/* delete */
390       case '=':		/* list */
391 	assign = sign;
392 	break;
393       case '%': /* action_chars[ACTION_REGEXP] */
394 	type = ACTION_REGEXP;
395 	/* falltrough */
396       case '>': /* action_chars[ACTION_WEAK] */
397 
398 	/* define/edit */
399 	assign = '>';
400 	sign = *(p + 1);
401 	if (!sign) {
402 	    PRINTF("#%s: label expected\n", ONPROMPT);
403 	    return;
404 	} else if (sign == '+' || sign == '-')
405 	    str++;
406 	else
407 	    sign = '+';
408 	break;
409       default:
410 	assign = 0;	/* anonymous action */
411 	str = p;
412 	break;
413     }
414 
415     /* labelled action: */
416     if (assign != 0) {
417         p = first_regular(str, ' ');
418 	if ((hastail = *p))
419 	    *p = '\0';
420 
421         /* break out group name (if present) */
422 	group = strstr( str, group_delim );
423 	if( group ) {
424 		*group = 0;
425 		group += strlen( group_delim );
426 	}
427 
428 	my_strncpy(label, str, BUFSIZE-1);
429 	if (hastail)
430 	    *p++ = ' ';	/* p points to start of pattern, or to \0 */
431 
432 	if (!*label) {
433 	    PRINTF("#%s: label expected\n", ONPROMPT);
434 	    return;
435 	}
436 
437 
438 	if (onprompt)
439 	    np = lookup_prompt(label);
440 	else
441 	    np = lookup_action(label);
442 
443 	/* '<' : remove action */
444         if (assign == '<') {
445             if (!np || !*np) {
446                 PRINTF("#no %s, cannot delete label: \"%s\"\n",
447 		       ONPROMPT, label);
448             }
449             else {
450 		if (onprompt)
451 		    delete_prompt(np);
452 		else
453 		    delete_action(np);
454 	    }
455 
456 	    /* '>' : define action */
457         } else if (assign == '>') {
458 #ifndef USE_REGEXP
459 	    if (type == ACTION_REGEXP) {
460 		PRINTF("#error: regexp not allowed\n");
461 		return;
462 	    }
463 #endif
464 
465             if (sign == '+')
466 		active = 1;
467 	    else
468 		active = 0;
469 
470 	    if (!*label) {
471 		PRINTF("#%s: label expected\n", ONPROMPT);
472 		return;
473 	    }
474 
475             p = skipspace(p);
476             if (*p == '(') {
477 		ptr pbuf = (ptr)0;
478 		p++;
479 		kind = evalp(&pbuf, &p);
480 		if (!REAL_ERROR && kind != TYPE_TXT)
481 		    error=NO_STRING_ERROR;
482 		if (REAL_ERROR) {
483 		    PRINTF("#%s: ", ONPROMPT);
484 		    print_error(error=NO_STRING_ERROR);
485 		    ptrdel(pbuf);
486 		    return;
487 		}
488 		if (pbuf) {
489 		    my_strncpy(pattern, ptrdata(pbuf), BUFSIZE-1);
490 		    ptrdel(pbuf);
491 		} else
492 		    pattern[0] = '\0';
493 		if (*p)
494 		    p = skipspace(++p);
495 		if ((hastail = *p == '='))
496 		    p++;
497 	    }
498             else {
499 		p = first_regular(command = p, '=');
500 		if ((hastail = *p))
501 		    *p = '\0';
502 		my_strncpy(pattern, command, BUFSIZE-1);
503 
504 		if (hastail)
505 		    *p++ = '=';
506 	    }
507 
508 	    if (!*pattern) {
509 		PRINTF("#error: pattern of #%ss must be non-empty.\n", ONPROMPT);
510 		return;
511 	    }
512 
513 #ifdef USE_REGEXP
514 	    if (type == ACTION_REGEXP && hastail) {
515 		int errcode;
516 		char unesc_pat[BUFSIZE];
517 
518 		/*
519 		 * HACK WARNING:
520 		 * we unescape regexp patterns now, instead of doing
521 		 * jit+unescape at runtime, as for weak actions.
522 		 */
523 		strcpy(unesc_pat, pattern);
524 		unescape(unesc_pat);
525 
526 		regexp = malloc(sizeof(regex_t));
527 		if (!regexp) {
528 		    errmsg("malloc");
529 		    return;
530 		}
531 
532 		if ((errcode = regcomp((regex_t *)regexp, unesc_pat, REG_EXTENDED))) {
533 		    int n;
534 		    char *tmp;
535 		    n = regerror(errcode, (regex_t *)regexp, NULL, 0);
536 		    tmp = (char *)malloc(n);
537 		    if (tmp) {
538 			if (!regerror(errcode, (regex_t *)regexp, tmp, n))
539 			    errmsg("regerror");
540 			else {
541 			    PRINTF("#regexp error: %s\n", tmp);
542 			}
543 			free(tmp);
544 		    } else {
545 			error = NO_MEM_ERROR;
546 			errmsg("malloc");
547 		    }
548 		    regfree((regex_t *)regexp);
549 		    free(regexp);
550 		    return;
551 		}
552 	    }
553 #endif
554             command = p;
555 
556             if (hastail) {
557                 if (np && *np) {
558 		    change_actionorprompt(*np, pattern, command, type, regexp, onprompt);
559                     (*np)->active = active;
560                 } else {
561 		    if (onprompt)
562 			add_new_prompt(label, pattern, command, active,
563 				       type, regexp);
564 		    else
565 			add_new_action(label, pattern, command, active,
566 				       type, regexp);
567 		}
568 
569 		if( group != NULL ) {
570 			/* I don't know why but we need to clip this because somehow
571 			 * the original string is restored to *p at some point instead
572 			 * of the null-clipped one we used waaaay at the top. */
573         		p = first_regular(group, ' ');
574 			*p = '\0';
575 	    		np = lookup_action(label);
576 			if( (*np)->group != NULL )
577 				free( (*np)->group );
578 
579 			if ( *group == '\0' || strcmp(group,"*") == 0 )
580 				group = NULL;
581 
582 			(*np) -> group = my_strdup( group );
583 		}
584             }
585 
586 	    /* '=': list action */
587         } else if (assign == '='){
588             if (np && *np) {
589 		int len = (int)strlen((*np)->label);
590 		sprintf(inserted_next, "#%s %c%c%.*s %.*s=%.*s", ONPROMPT,
591 			action_chars[(*np)->type], (*np)->active ? '+' : '-',
592 			BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7, (*np)->label,
593 			BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len, (*np)->pattern,
594 			BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len - (int)strlen((*np)->pattern),
595 			(*np)->command);
596             } else {
597                 PRINTF("#no %s, cannot list label: \"%s\"\n", ONPROMPT, label);
598             }
599 
600 	    /* '+', '-': turn action on/off */
601         } else {
602             if (np && *np) {
603                 (*np)->active = (sign == '+');
604                 if (opt_info) {
605                     PRINTF("#%s %c%s %s is now o%s.\n", ONPROMPT,
606 			   action_chars[(*np)->type],
607 			   label,
608 			   (*np)->pattern, (sign == '+') ? "n" : "ff");
609                 }
610             } else {
611                 PRINTF("#no %s, cannot turn o%s label: \"%s\"\n", ONPROMPT,
612 		       (sign == '+') ? "n" : "ff", label);
613             }
614         }
615 
616 	/* anonymous action, cannot be regexp */
617     } else {
618 
619 	if (onprompt) {
620 	    PRINTF("#anonymous prompts not supported.\n#please use labelled prompts.\n");
621 	    return;
622 	}
623 
624         command = first_regular(str, '=');
625 
626         if (*command == '=') {
627             *command = '\0';
628 
629 	    my_strncpy(pattern, str, BUFSIZE-1);
630             *command++ = '=';
631             np = lookup_action_pattern(pattern);
632             if (*command)
633 		if (np && *np)
634 		change_actionorprompt(*np, NULL, command, ACTION_WEAK, NULL, 0);
635 	    else
636 		add_anonymous_action(pattern, command, ACTION_WEAK, NULL);
637             else if (np && *np)
638 		delete_action(np);
639             else {
640                 PRINTF("#no action, cannot delete pattern: \"%s\"\n",
641 		       pattern);
642                 return;
643             }
644         } else {
645             np = lookup_action_pattern(str);
646             if (np && *np) {
647                 sprintf(inserted_next, "#action %.*s=%.*s",
648 			BUFSIZE - 10, (*np)->pattern,
649 			BUFSIZE - (int)strlen((*np)->pattern) - 10,
650 			(*np)->command);
651             } else {
652                 PRINTF("#no action, cannot show pattern: \"%s\"\n", str);
653             }
654         }
655     }
656 }
657 
658 #undef ONPROMPT
659 
660 /*
661  * display attribute syntax
662  */
__P0(void)663 void show_attr_syntax __P0 (void)
664 {
665     int i;
666     PRINTF("#attribute syntax:\n\tOne or more of:\tbold, blink, underline, inverse\n\tand/or\t[foreground] [ON background]\n\tColors: ");
667     for (i = 0; i < COLORS; i++)
668 	tty_printf("%s%s", colornames[i],
669 		 (i == LOWCOLORS - 1 || i == COLORS - 1) ? "\n\t\t" : ",");
670     tty_printf("%s\n", colornames[i]);
671 }
672 
673 /*
674  * put escape sequences to turn on/off an attribute in given buffers
675  */
__P3(int,attrcode,char *,begin,char *,end)676 void attr_string __P3 (int,attrcode, char *,begin, char *,end)
677 {
678     int fg = FOREGROUND(attrcode), bg = BACKGROUND(attrcode),
679       tok = ATTR(attrcode);
680     char need_end = 0;
681     *begin = *end = '\0';
682 
683     if (tok > (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE)
684 	|| fg > COLORS || bg > COLORS || attrcode == NOATTRCODE)
685       return;    /* do nothing */
686 
687     if (fg < COLORS) {
688 	if (bg < COLORS) {
689 	    sprintf(begin, "\033[%c%d;%s%dm",
690 		    fg<LOWCOLORS ? '3' : '9', fg % LOWCOLORS,
691 		    bg<LOWCOLORS ? "4" :"10", bg % LOWCOLORS);
692 #ifdef TERM_LINUX
693 	    strcpy(end, "\033[39;49m");
694 #endif
695 	} else {
696 	    sprintf(begin, "\033[%c%dm",
697 		    fg<LOWCOLORS ? '3' : '9', fg % LOWCOLORS);
698 #ifdef TERM_LINUX
699 	    strcpy(end, "\033[39m");
700 #endif
701 	}
702     } else if (bg < COLORS) {
703 	sprintf(begin, "\033[%s%dm",
704 		bg<LOWCOLORS ? "4" : "10", bg % LOWCOLORS);
705 #ifdef TERM_LINUX
706 	strcpy(end, "\033[49m");
707 #endif
708     }
709 
710 #ifndef TERM_LINUX
711     if (fg < COLORS || bg < COLORS)
712  	need_end = 1;
713 #endif
714 
715     if (tok & ATTR_BOLD) {
716 	if (tty_modebold[0]) {
717 	    strcat(begin, tty_modebold);
718 #ifdef TERM_LINUX
719 	    strcat(end, "\033[21m");
720 #else
721 	    need_end = 1;
722 #endif
723 	} else {
724 	    strcat(begin, tty_modestandon);
725 	    strcpy(end, tty_modestandoff);
726 	}
727     }
728 
729     if (tok & ATTR_BLINK) {
730 	if (tty_modeblink[0]) {
731 	    strcat(begin, tty_modeblink);
732 #ifdef TERM_LINUX
733 	    strcat(end, "\033[25m");
734 #else
735 	    need_end = 1;
736 #endif
737 	} else {
738 	    strcat(begin, tty_modestandon);
739 	    strcpy(end, tty_modestandoff);
740 	}
741     }
742 
743     if (tok & ATTR_UNDERLINE) {
744 	if (tty_modeuline[0]) {
745 	    strcat(begin, tty_modeuline);
746 #ifdef TERM_LINUX
747 	    strcat(end, "\033[24m");
748 #else
749 	    need_end = 1;
750 #endif
751 	} else {
752 	    strcat(begin, tty_modestandon);
753 	    strcpy(end, tty_modestandoff);
754 	}
755     }
756 
757     if (tok & ATTR_INVERSE) {
758 	if (tty_modeinv[0]) {
759 	    strcat(begin, tty_modeinv);
760 #ifdef TERM_LINUX
761 	    strcat(end, "\033[27m");
762 #else
763 	    need_end = 1;
764 #endif
765 	} else {
766 	    strcat(begin, tty_modestandon);
767 	    strcpy(end, tty_modestandoff);
768 	}
769     }
770 
771 #ifndef TERM_LINUX
772     if (need_end)
773 	strcpy(end, tty_modenorm);
774 #endif
775 }
776 
777 /*
778  * parse attribute description in line.
779  * Return attribute if successful, -1 otherwise.
780  */
__P1(char *,line)781 int parse_attributes __P1 (char *,line)
782 {
783     char *p;
784     int tok = 0, fg, bg, t = -1;
785 
786     if (!(p = strtok(line, " ")))
787       return NOATTRCODE;
788 
789     fg = bg = NO_COLOR;
790 
791     while (t && p) {
792 	if (!strcasecmp(p, "bold"))
793 	  t = ATTR_BOLD;
794 	else if (!strcasecmp(p, "blink"))
795 	  t = ATTR_BLINK;
796 	else if (!strcasecmp(p, "underline"))
797 	  t = ATTR_UNDERLINE;
798 	else if (!strcasecmp(p, "inverse") || !strcasecmp(p, "reverse"))
799 	  t = ATTR_INVERSE;
800 	else
801 	  t = 0;
802 
803 	if (t) {
804 	    tok |= t;
805 	    p = strtok(NULL, " ");
806 	}
807     }
808 
809     if (!p)
810 	return ATTRCODE(tok, fg, bg);
811 
812     for (t = 0; t <= COLORS && strcmp(p, colornames[t]); t++)
813 	;
814     if (t <= COLORS) {
815 	fg = t;
816 	p = strtok(NULL, " ");
817     }
818 
819     if (!p)
820       return ATTRCODE(tok, fg, bg);
821 
822     if (strcasecmp(p, "on"))
823       return -1;      /* invalid attribute */
824 
825     if (!(p = strtok(NULL, " ")))
826       return -1;
827 
828     for (t = 0; t <= COLORS && strcmp(p, colornames[t]); t++)
829       ;
830     if (t <= COLORS)
831       bg = t;
832     else
833       return -1;
834 
835     return ATTRCODE(tok, fg, bg);
836 }
837 
838 
839 /*
840  * return a static pointer to name of given attribute code
841  */
__P1(int,attrcode)842 char *attr_name __P1 (int,attrcode)
843 {
844     static char name[BUFSIZE];
845     int fg = FOREGROUND(attrcode), bg = BACKGROUND(attrcode), tok = ATTR(attrcode);
846 
847     name[0] = 0;
848     if (tok > (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE) || fg > COLORS || bg > COLORS)
849       return name;   /* error! */
850 
851     if (tok & ATTR_BOLD)
852       strcat(name, "bold ");
853     if (tok & ATTR_BLINK)
854       strcat(name, "blink ");
855     if (tok & ATTR_UNDERLINE)
856       strcat(name, "underline ");
857     if (tok & ATTR_INVERSE)
858       strcat(name, "inverse ");
859 
860     if (fg < COLORS || (fg == bg && fg == COLORS && !tok))
861       strcat(name, colornames[fg]);
862 
863     if (bg < COLORS) {
864 	strcat(name, " on ");
865 	strcat(name, colornames[bg]);
866     }
867 
868     if (!*name)
869 	strcpy(name, "none");
870 
871     return name;
872 }
873 
874 /*
875  * show defined marks
876  */
__P0(void)877 void show_marks __P0 (void)
878 {
879     marknode *p;
880     PRINTF("#%s marker%s defined%c\n", markers ? "the following" : "no",
881 	       (markers && !markers->next) ? " is" : "s are",
882 	       markers ? ':' : '.');
883     for (p = markers; p; p = p->next)
884 	tty_printf("#mark %s%s=%s\n", p->mbeg ? "^" : "",
885 		   p->pattern, attr_name(p->attrcode));
886 }
887 
888 
889 /*
890  * parse arguments to the #mark command
891  */
__P1(char *,str)892 void parse_mark __P1 (char *,str)
893 {
894     char *p;
895     marknode **np, *n;
896     char mbeg = 0;
897 
898     if (*str == '=') {
899 	PRINTF("#marker must be non-null.\n");
900 	return;
901     }
902     p = first_regular(str, '=');
903     if (!*p) {
904 	if (*str ==  '^')
905 	    mbeg = 1, str++;
906         unescape(str);
907         np = lookup_marker(str, mbeg);
908         if ((n = *np)) {
909 	    ptr pbuf = (ptr)0;
910 	    char *name;
911 	    pbuf = ptrmescape(pbuf, n->pattern, strlen(n->pattern), 0);
912 	    if (MEM_ERROR) { ptrdel(pbuf); return; }
913             name = attr_name(n->attrcode);
914             sprintf(inserted_next, "#mark %s%.*s=%.*s", n->mbeg ? "^" : "",
915 		    BUFSIZE-(int)strlen(name)-9, pbuf ? ptrdata(pbuf) : "",
916 		    BUFSIZE-9, name);
917 	    ptrdel(pbuf);
918         } else {
919             PRINTF("#unknown marker, cannot show: \"%s\"\n", str);
920         }
921 
922     } else {
923         int attrcode, wild = 0;
924 	char pattern[BUFSIZE], *p2;
925 
926         *(p++) = '\0';
927 	p = skipspace(p);
928 	if (*str ==  '^')
929 	    mbeg = 1, str++;
930 	my_strncpy(pattern, str, BUFSIZE-1);
931         unescape(pattern);
932 	p2 = pattern;
933 	while (*p2) {
934 	    if (ISMARKWILDCARD(*p2)) {
935 		wild = 1;
936 		if (ISMARKWILDCARD(*(p2 + 1))) {
937 		    error=SYNTAX_ERROR;
938 		    PRINTF("#error: two wildcards (& or $) may not be next to eachother\n");
939 		    return;
940 		}
941 	    }
942 	    p2++;
943 	}
944 
945         np = lookup_marker(pattern, mbeg);
946         attrcode = parse_attributes(p);
947         if (attrcode == -1) {
948             PRINTF("#invalid attribute syntax.\n");
949 	    error=SYNTAX_ERROR;
950             if (opt_info) show_attr_syntax();
951         } else if (!*p)
952 	    if ((n = *np)) {
953 		if (opt_info) {
954 		    PRINTF("#deleting mark: %s%s=%s\n", n->mbeg ? "^" : "",
955 			   n->pattern, attr_name(n->attrcode));
956 		}
957 		delete_marknode(np);
958 	    } else {
959 		PRINTF("#unknown marker, cannot delete: \"%s%s\"\n",
960 		       mbeg ? "^" : "", pattern);
961 	    }
962         else {
963             if (*np) {
964                 (*np)->attrcode = attrcode;
965                 if (opt_info) {
966                     PRINTF("#changed");
967                 }
968             } else {
969                 add_marknode(pattern, attrcode, mbeg, wild);
970                 if (opt_info) {
971                     PRINTF("#new");
972                 }
973             }
974             if (opt_info)
975 		tty_printf(" mark: %s%s=%s\n", mbeg ? "^" : "",
976 			   pattern, attr_name(attrcode));
977         }
978     }
979 }
980 
981 /*
982  * turn ASCII description of a sequence
983  * into raw escape sequence
984  * return pointer to end of ASCII description
985  */
__P3(char *,buf,char *,seq,int *,seqlen)986 static char *unescape_seq __P3 (char *,buf, char *,seq, int *,seqlen)
987 {
988     char c, *start = buf;
989     enum { NORM, ESCSINGLE, ESCAPE, CARET, DONE } state = NORM;
990 
991     for (; (c = *seq); seq++) {
992 	switch (state) {
993 	  case NORM:
994 	    if (c == '^')
995 		state = CARET;
996 	    else if (c == ESC)
997 		state = ESCSINGLE;
998 	    else if (c == '=')
999 		state = DONE;
1000 	    else
1001 		*(buf++) = c;
1002 	    break;
1003 	  case CARET:
1004 	    /*
1005 	     * handle ^@ ^A  ... ^_ as expected:
1006 	     * ^@ == 0x00, ^A == 0x01, ... , ^_ == 0x1f
1007 	     */
1008 	    if (c > 0x40 && c < 0x60)
1009 		*(buf++) = c & 0x1f;
1010 	    /* special case: ^? == 0x7f */
1011 	    else if (c == '?')
1012 		*(buf++) = 0x7f;
1013 	    state = NORM;
1014 	    break;
1015 	  case ESCSINGLE:
1016 	  case ESCAPE:
1017 	    /*
1018 	     * GH: \012 ==> octal number
1019 	     */
1020 	    if (state == ESCSINGLE &&
1021 		ISODIGIT(seq[0]) && ISODIGIT(seq[1]) && ISODIGIT(seq[2])) {
1022 		*(buf++) =(((seq[0] - '0') << 6) |
1023 			   ((seq[1] - '0') << 3) |
1024 			   (seq[2] - '0'));
1025 		seq += 2;
1026 	    } else {
1027 		*(buf++) = c;
1028 		if (c == ESC)
1029 		    state = ESCAPE;
1030 		else
1031 		    state = NORM;
1032 	    }
1033 	    break;
1034 	  default:
1035 	    break;
1036 	}
1037 	if (state == DONE)
1038 	    break;
1039     }
1040     *buf = '\0';
1041     *seqlen = buf - start;
1042     return seq;
1043 }
1044 
1045 /*
1046  * read a single character from tty, with timeout in milliseconds.
1047  * timeout == 0 means wait indefinitely (no timeout).
1048  * return char or -1 if timeout was reached.
1049  */
__P1(int,timeout)1050 static int get_one_char __P1 (int,timeout)
1051 {
1052     struct timeval timeoutbuf;
1053     fd_set fds;
1054     int n;
1055     char c;
1056 
1057  again:
1058     FD_ZERO(&fds);
1059     FD_SET(tty_read_fd, &fds);
1060     timeoutbuf.tv_sec = 0;
1061     timeoutbuf.tv_usec = timeout * uSEC_PER_mSEC;
1062     n = select(tty_read_fd + 1, &fds, NULL, NULL,
1063                timeout ? &timeoutbuf : NULL);
1064     if (n == -1 && errno == EINTR)
1065 	return -1;
1066     if (n == -1) {
1067 	errmsg("select");
1068 	return -1;
1069     }
1070     do {
1071         n = tty_read(&c, 1);
1072     } while (n < 0 && errno == EINTR);
1073     if (n == 1)
1074         return (unsigned char)c;
1075     if (n < 0) {
1076         if (errno != EAGAIN)
1077 	    errmsg("read from tty");
1078         return -1;
1079     }
1080     /* n == 0 */
1081     if (timeout == 0)
1082 	goto again;
1083     return -1;
1084 }
1085 
1086 /*
1087  * print an escape sequence in human-readably form.
1088  */
__P2(char *,seq,int,len)1089 void print_seq __P2 (char *,seq, int,len)
1090 {
1091     while (len--) {
1092 	unsigned char ch = *(seq++);
1093         if (ch == '\033') {
1094             tty_puts("esc ");
1095             continue;
1096         }
1097         if (ch < ' ') {
1098             tty_putc('^');
1099             ch |= '@';
1100         }
1101         if (ch == ' ')
1102 	    tty_puts("space ");
1103         else if (ch == 0x7f)
1104 	    tty_puts("del ");
1105 	else if (ch & 0x80)
1106 	    tty_printf("\\%03o ", ch);
1107 	else
1108 	    tty_printf("%c ", ch);
1109     }
1110 }
1111 
1112 /*
1113  * return a static pointer to escape sequence made printable, for use in
1114  * definition-files
1115  */
__P2(char *,seq,int,len)1116 char *seq_name __P2 (char *,seq, int,len)
1117 {
1118     static char buf[CAPLEN*4];
1119     char *p = buf;
1120     /*
1121      * rules: control chars are written as ^X, where
1122      * X is char | 64
1123      *
1124      * GH: codes > 0x80  ==>  octal \012
1125      *
1126      * special case: 0x7f is written ^?
1127      */
1128     while (len--) {
1129         unsigned char c = *seq++;
1130         if (c == '^' || (c && strchr(SPECIAL_CHARS, c)))
1131 	    *(p++) = ESC;
1132 
1133 	if (c < ' ') {
1134             *(p++) = '^';
1135             *(p++) = c | '@';
1136 	} else if (c == 0x7f) {
1137 	    *(p++) = '^';
1138 	    *(p++) = '?';
1139         } else if (c & 0x80) {
1140 	    /* GH: save chars with high bit set in octal */
1141 	    sprintf(p, "\\%03o", (int)c);
1142 	    p += strlen(p);
1143 	} else
1144 	    *(p++) = c;
1145     }
1146     *p = '\0';
1147     return buf;
1148 }
1149 
1150 /*
1151  * read a single escape sequence from the keyboard
1152  * prompting user for it; return static pointer
1153  */
__P2(char *,name,int *,len)1154 char *read_seq __P2 (char *,name, int *,len)
1155 {
1156     static char seq[CAPLEN];
1157     int i = 1, tmp;
1158 
1159     PRINTF("#please press the key \"%s\" : ", name);
1160     tty_flush();
1161 
1162     if ((tmp = get_one_char(0)) >= 0)
1163 	seq[0] = tmp;
1164     else {
1165 	tty_puts("#unable to get key. Giving up.\n");
1166 	return NULL;
1167     }
1168 
1169     while (i < CAPLEN - 1 &&
1170 	   (tmp = get_one_char(KBD_TIMEOUT)) >= 0)
1171 	seq[i++] = tmp;
1172     *len = i;
1173     print_seq(seq, i);
1174 
1175     tty_putc('\n');
1176     if (seq[0] >= ' ' && seq[0] <= '~') {
1177 	PRINTF("#that is not a redefinable key.\n");
1178 	return NULL;
1179     }
1180     return seq;
1181 }
1182 
1183 /*
1184  * show full definition of one binding,
1185  * with custom message
1186  */
__P2(char *,msg,keynode *,p)1187 static void show_single_bind __P2 (char *,msg, keynode *,p)
1188 {
1189     if (p->funct == key_run_command) {
1190 	PRINTF("#%s %s %s=%s\n", msg, p->name,
1191 	       seq_name(p->sequence, p->seqlen), p->call_data);
1192     } else {
1193 	PRINTF("#%s %s %s=%s%s%s\n", msg, p->name,
1194 	       seq_name(p->sequence, p->seqlen),
1195 	       internal_functions[lookup_edit_function(p->funct)].name,
1196 	       p->call_data ? " " : "",
1197 	       p->call_data ? p->call_data : "");
1198     }
1199 }
1200 
1201 /*
1202  * list keyboard bindings
1203  */
__P1(char,edit)1204 void show_binds __P1 (char,edit)
1205 {
1206     keynode *p;
1207     int count = 0;
1208     for (p = keydefs; p; p = p->next) {
1209 	if (edit != (p->funct == key_run_command)) {
1210 	    if (!count) {
1211 		if (edit) {
1212 		    PRINTF("#line-editing keys:\n");
1213 		} else {
1214 		    PRINTF("#user-defined keys:\n");
1215 		}
1216 	    }
1217 	    show_single_bind("bind", p);
1218 	    ++count;
1219 	}
1220     }
1221     if (!count) {
1222         PRINTF("#no key bindings defined right now.\n");
1223     }
1224 }
1225 
1226 
1227 /*
1228  * interactively create a new keybinding
1229  */
__P2(char *,name,char *,command)1230 static void define_new_key __P2 (char *,name, char *,command)
1231 {
1232     char *seq, *arg;
1233     keynode *p;
1234     int seqlen, function;
1235 
1236     seq = read_seq(name, &seqlen);
1237     if (!seq) return;
1238 
1239     for (p = keydefs; p; p = p->next)
1240 	/* GH: don't allow binding of supersets of another bind */
1241 	if (!memcmp(p->sequence, seq, MIN2(p->seqlen, seqlen))) {
1242 	    show_single_bind("key already bound as:", p);
1243 	    return;
1244 	}
1245 
1246     function = lookup_edit_name(command, &arg);
1247     if (function)
1248 	add_keynode(name, seq, seqlen,
1249 		    internal_functions[function].funct, arg);
1250     else
1251 	add_keynode(name, seq, seqlen, key_run_command, command);
1252 
1253     if (opt_info) {
1254 	PRINTF("#new key binding: %s %s=%s\n",
1255 	       name, seq_name(seq, seqlen), command);
1256     }
1257 }
1258 
1259 /*
1260  * parse the #bind command non-interactively.
1261  */
__P1(char *,arg)1262 static void parse_bind_noninteractive __P1 (char *,arg)
1263 {
1264     char rawseq[CAPLEN], *p, *seq, *params;
1265     int function, seqlen;
1266     keynode **kp;
1267 
1268     p = strchr(arg, ' ');
1269     if (!p) {
1270         PRINTF("#syntax error: \"#bind %s\"\n", arg);
1271         return;
1272     }
1273     *(p++) = '\0';
1274     seq = p = skipspace(p);
1275 
1276     p = unescape_seq(rawseq, p, &seqlen);
1277     if (!p[0] || !p[1]) {
1278         PRINTF("#syntax error: \"#bind %s %s\"\n", arg, seq);
1279         return;
1280     }
1281     *p++ = '\0';
1282 
1283     kp = lookup_key(arg);
1284     if (kp && *kp)
1285 	delete_keynode(kp);
1286 
1287     if ((function = lookup_edit_name(p, &params)))
1288 	add_keynode(arg, rawseq, seqlen,
1289 		    internal_functions[function].funct, params);
1290     else
1291 	add_keynode(arg, rawseq, seqlen, key_run_command, p);
1292 
1293     if (opt_info) {
1294 	PRINTF("#%s: %s %s=%s\n",
1295                (kp && *kp) ? "redefined key" : "new key binding",
1296                arg, seq, p);
1297     }
1298 }
1299 
1300 /*
1301  * parse the argument of the #bind command (interactive)
1302  */
__P1(char *,arg)1303 void parse_bind __P1 (char *,arg)
1304 {
1305     char *p, *q, *command, *params;
1306     char *name = arg;
1307     keynode **npp, *np;
1308     int function;
1309 
1310     p = first_valid(arg, '=');
1311     q = first_valid(arg, ' ');
1312     q = skipspace(q);
1313 
1314     if (*p && *q && p > q) {
1315         parse_bind_noninteractive(arg);
1316 	return;
1317     }
1318 
1319     if (*p) {
1320         *(p++) = '\0';
1321         np = *(npp = lookup_key(name));
1322         if (*p) {
1323             command = p;
1324             if (np) {
1325                 if (np->funct == key_run_command)
1326 		    free(np->call_data);
1327 		if ((function = lookup_edit_name(command, &params))) {
1328 		    np->call_data = my_strdup(params);
1329 		    np->funct = internal_functions[function].funct;
1330 		} else {
1331                     np->call_data = my_strdup(command);
1332 		    np->funct = key_run_command;
1333 		}
1334                 if (opt_info) {
1335                     PRINTF("#redefined key: %s %s=%s\n", name,
1336 			       seq_name(np->sequence, np->seqlen),
1337 			       command);
1338                 }
1339             } else
1340 		define_new_key(name, command);
1341         } else {
1342             if (np) {
1343                 if (opt_info)
1344 		    show_single_bind("deleting key binding:", np);
1345 		delete_keynode(npp);
1346             } else {
1347                 PRINTF("#no such key: \"%s\"\n", name);
1348             }
1349         }
1350     } else {
1351         np = *(npp = lookup_key(name));
1352         if (np) {
1353 	    char *seqname;
1354 	    int seqlen;
1355 	    seqname = seq_name(np->sequence, np->seqlen);
1356 	    seqlen = strlen(seqname);
1357 
1358 	    if (np->funct == key_run_command)
1359 		sprintf(inserted_next, "#bind %.*s %s=%.*s",
1360 			BUFSIZE-seqlen-9, name, seqname,
1361 			BUFSIZE-seqlen-(int)strlen(name)-9,
1362 			np->call_data);
1363 	    else {
1364 		p = internal_functions[lookup_edit_function(np->funct)].name;
1365 		sprintf(inserted_next, "#bind %.*s %s=%s%s%.*s",
1366 			BUFSIZE-seqlen-10, name, seqname, p,
1367 			np->call_data ? " " : "",
1368 			BUFSIZE-seqlen-(int)strlen(name)-(int)strlen(p)-10,
1369 			np->call_data ? np->call_data : "");
1370 	    }
1371 	} else {
1372             PRINTF("#no such key: \"%s\"\n", name);
1373         }
1374     }
1375 }
1376 
__P1(char *,arg)1377 void parse_rebind __P1 (char *,arg)
1378 {
1379     char rawseq[CAPLEN], *seq, **old;
1380     keynode **kp, *p;
1381     int seqlen;
1382 
1383     arg = skipspace(arg);
1384     if (!*arg) {
1385 	PRINTF("#rebind: missing key.\n");
1386 	return;
1387     }
1388 
1389     seq = first_valid(arg, ' ');
1390     if (*seq) {
1391 	*seq++ = '\0';
1392 	seq = skipspace(seq);
1393     }
1394 
1395     kp = lookup_key(arg);
1396     if (!kp || !*kp) {
1397 	PRINTF("#no such key: \"%s\"\n", arg);
1398 	return;
1399     }
1400 
1401     if (!*seq) {
1402 	seq = read_seq(arg, &seqlen);
1403 	if (!seq)
1404 	    return;
1405     } else {
1406 	(void)unescape_seq(rawseq, seq, &seqlen);
1407 	seq = rawseq;
1408     }
1409 
1410     for (p = keydefs; p; p = p->next) {
1411 	if (p == *kp)
1412 	    continue;
1413 	if (!memcmp(p->sequence, seq, MIN2(seqlen, p->seqlen))) {
1414 	    show_single_bind("key already bound as:", p);
1415 	    return;
1416 	}
1417     }
1418 
1419     old = &((*kp)->sequence);
1420     if (*old)
1421 	free(*old);
1422     *old = (char *)malloc((*kp)->seqlen = seqlen);
1423     memmove(*old, seq, seqlen);
1424 
1425     if (opt_info)
1426 	show_single_bind("redefined key:", *kp);
1427 }
1428 
1429 /*
1430  * evaluate an expression, or unescape a text.
1431  * set value of start and end line if <(expression...) or !(expression...)
1432  * if needed, use/malloc "pbuf" as buffer (on error, also free pbuf)
1433  * return resulting char *
1434  */
__P7(char *,arg,ptr *,pbuf,char *,kind,char *,name,int,also_num,long *,start,long *,end)1435 char *redirect __P7 (char *,arg, ptr *,pbuf, char *,kind, char *,name, int,also_num, long *,start, long *,end)
1436 {
1437     char *tmp = skipspace(arg), k;
1438     int type, i;
1439 
1440     if (!pbuf) {
1441 	print_error(error=INTERNAL_ERROR);
1442 	return NULL;
1443     }
1444 
1445     k = *tmp;
1446     if (k == '!' || k == '<')
1447 	arg = ++tmp;
1448     else
1449 	k = 0;
1450 
1451     *start = *end = 0;
1452 
1453     if (*tmp=='(') {
1454 
1455 	arg = tmp + 1;
1456 	type = evalp(pbuf, &arg);
1457 	if (!REAL_ERROR && type!=TYPE_TXT && !also_num)
1458 	    error=NO_STRING_ERROR;
1459 	if (REAL_ERROR) {
1460 	    PRINTF("#%s: ", name);
1461 	    print_error(error);
1462 	    ptrdel(*pbuf);
1463 	    return NULL;
1464 	}
1465 	for (i=0; i<2; i++) if (*arg == CMDSEP) {
1466 	    long buf;
1467 
1468 	    arg++;
1469 	    if (!i && *arg == CMDSEP) {
1470 		*start = 1;
1471 		continue;
1472 	    }
1473 	    else if (i && *arg == ')') {
1474 		*end = LONG_MAX;
1475 		continue;
1476 	    }
1477 
1478 	    type = evall(&buf, &arg);
1479 	    if (!REAL_ERROR && type != TYPE_NUM)
1480 		error=NO_NUM_VALUE_ERROR;
1481 	    if (REAL_ERROR) {
1482 		PRINTF("#%s: ", name);
1483 		print_error(error);
1484 		ptrdel(*pbuf);
1485 		return NULL;
1486 	    }
1487 	    if (i)
1488 		*end = buf;
1489 	    else
1490 		*start = buf;
1491 	}
1492 	if (*arg != ')') {
1493 	    PRINTF("#%s: ", name);
1494 	    print_error(error=MISSING_PAREN_ERROR);
1495 	    ptrdel(*pbuf);
1496 	    return NULL;
1497 	}
1498 	if (!*pbuf) {
1499 	    /* make space to add a final \n */
1500 	    *pbuf = ptrsetlen(*pbuf, 1);
1501 	    ptrzero(*pbuf);
1502 	    if (REAL_ERROR) {
1503 		print_error(error);
1504 		ptrdel(*pbuf);
1505 		return NULL;
1506 	    }
1507 	}
1508 	arg = ptrdata(*pbuf);
1509 	if (!*start && *end)
1510 	    *start = 1;
1511     } else
1512 	unescape(arg);
1513 
1514     *kind = k;
1515     return arg;
1516 }
1517 
__P0(void)1518 void show_vars __P0 (void)
1519 {
1520     varnode *v;
1521     int i, type;
1522     ptr p = (ptr)0;
1523 
1524     PRINTF("#the following variables are defined:\n");
1525 
1526     for (type = 0; !REAL_ERROR && type < 2; type++) {
1527 	reverse_sortedlist((sortednode **)&sortednamed_vars[type]);
1528 	v = sortednamed_vars[type];
1529 	while (v) {
1530 	    if (type == 0) {
1531 		tty_printf("#(@%s = %ld)\n", v->name, v->num);
1532 	    } else {
1533 		p = ptrescape(p, v->str, 0);
1534 		if (REAL_ERROR) {
1535 		    print_error(error);
1536 		    break;
1537 		}
1538 		tty_printf("#($%s = \"%s\")\n", v->name,
1539 		       p ? ptrdata(p) : "");
1540 	    }
1541 	    v = v->snext;
1542 	}
1543 	reverse_sortedlist((sortednode **)&sortednamed_vars[type]);
1544     }
1545     for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) {
1546 	if (*VAR[i].num)
1547 	    tty_printf("#(@%d = %ld)\n", i, *VAR[i].num);
1548     }
1549     for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) {
1550 	if (*VAR[i].str && ptrlen(*VAR[i].str)) {
1551 	    p = ptrescape(p, *VAR[i].str, 0);
1552 	    if (p && ptrlen(p))
1553 		tty_printf("#($%d = \"%s\")\n", i, ptrdata(p));
1554 	}
1555     }
1556     ptrdel(p);
1557 }
1558 
__P2(delaynode *,p,int,in_or_at)1559 void show_delaynode __P2 (delaynode *,p, int,in_or_at)
1560 {
1561     long d;
1562     struct tm *s;
1563     char buf[BUFSIZE];
1564 
1565     update_now();
1566     d = diff_vtime(&p->when, &now);
1567     s = localtime((time_t *)&p->when.tv_sec);
1568     /* s now points to a calendar struct */
1569     if (in_or_at) {
1570 
1571 	if (in_or_at == 2) {
1572 	    /* write time in buf */
1573 	    (void)strftime(buf, BUFSIZE - 1, "%H%M%S", s);
1574 	    sprintf(inserted_next, "#at %.*s (%s) %.*s",
1575 		    BUFSIZE - 15, p->name, buf,
1576 		    BUFSIZE - 15 - (int)strlen(p->name), p->command);
1577 	}
1578 	else
1579 	    sprintf(inserted_next, "#in %.*s (%ld) %.*s",
1580 		    BUFSIZE - LONGLEN - 9, p->name, d,
1581 		    BUFSIZE - LONGLEN - 9 - (int)strlen(p->name), p->command);
1582     } else {
1583 	(void)strftime(buf, BUFSIZE - 1, "%H:%M:%S", s);
1584 	PRINTF("#at (%s) #in (%ld) \"%s\" %s\n", buf, d, p->name, p->command);
1585     }
1586 }
1587 
__P0(void)1588 void show_delays __P0 (void)
1589 {
1590     delaynode *p;
1591     int n = (delays ? delays->next ? 2 : 1 : 0) +
1592 	(dead_delays ? dead_delays->next ? 2 : 1 : 0);
1593 
1594     PRINTF("#%s delay label%s defined%c\n", n ? "the following" : "no",
1595 	       n == 1 ? " is" : "s are", n ? ':' : '.');
1596     for (p = delays; p; p = p->next)
1597 	show_delaynode(p, 0);
1598     for (p = dead_delays; p; p = p->next)
1599 	show_delaynode(p, 0);
1600 }
1601 
__P3(delaynode **,p,char *,command,long,millisec)1602 void change_delaynode __P3 (delaynode **,p, char *,command, long,millisec)
1603 {
1604     delaynode *m=*p;
1605 
1606     *p = m->next;
1607     m->when.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC;
1608     m->when.tv_sec  =  millisec / mSEC_PER_SEC;
1609     update_now();
1610     add_vtime(&m->when, &now);
1611     if (*command) {
1612 	if (strlen(command) > strlen(m->command)) {
1613 	    free((void*)m->command);
1614 	    m->command = my_strdup(command);
1615 	}
1616 	else
1617 	    strcpy(m->command, command);
1618     }
1619     if (millisec < 0)
1620 	add_node((defnode*)m, (defnode**)&dead_delays, rev_time_sort);
1621     else
1622 	add_node((defnode*)m, (defnode**)&delays, time_sort);
1623     if (opt_info) {
1624 	PRINTF("#changed ");
1625 	show_delaynode(m, 0);
1626     }
1627 }
1628 
__P3(char *,name,char *,command,long,millisec)1629 void new_delaynode __P3 (char *,name, char *,command, long,millisec)
1630 {
1631     vtime t;
1632     delaynode *node;
1633 
1634     t.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC;
1635     t.tv_sec  =  millisec / mSEC_PER_SEC;
1636     update_now();
1637     add_vtime(&t, &now);
1638     node = add_delaynode(name, command, &t, millisec < 0);
1639     if (opt_info && node) {
1640 	PRINTF("#new ");
1641 	show_delaynode(node, 0);
1642     }
1643 }
1644 
__P1(int,count)1645 void show_history __P1 (int,count)
1646 {
1647     int i = curline;
1648 
1649     if (!count) count = lines - 1;
1650     if (count >= MAX_HIST) count = MAX_HIST - 1;
1651     i -= count;
1652     if (i < 0) i += MAX_HIST;
1653 
1654     while (count) {
1655 	if (hist[i]) {
1656 	    PRINTF("#%2d: %s\n", count, hist[i]);
1657 	}
1658 	count--;
1659 	if (++i == MAX_HIST) i = 0;
1660     }
1661 }
1662 
__P1(int,count)1663 void exe_history __P1 (int,count)
1664 {
1665     int i = curline;
1666     char buf[BUFSIZE];
1667 
1668     if (count >= MAX_HIST)
1669 	count = MAX_HIST - 1;
1670     i -= count;
1671     if (i < 0)
1672 	i += MAX_HIST;
1673     if (hist[i]) {
1674 	strcpy(buf, hist[i]);
1675 	parse_user_input(buf, 0);
1676     }
1677 }
1678 
1679