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, ¶ms)))
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, ¶ms))) {
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