diff options
Diffstat (limited to 'cmd2.c')
-rw-r--r-- | cmd2.c | 1676 |
1 files changed, 0 insertions, 1676 deletions
@@ -1,1676 +0,0 @@ -/* - * cmd2.c -- back-end for the various #commands - * - * (created: Massimiliano Ghilardi (Cosmos), Aug 14th, 1998) - * - * Copyright (C) 1998 by Massimiliano Ghilardi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - - -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <signal.h> -#include <ctype.h> -#include <time.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <unistd.h> -#include <errno.h> - -int strcasecmp(); -int select(); - -#include "defines.h" -#include "main.h" -#include "feature/regex.h" -#include "utils.h" -#include "beam.h" -#include "edit.h" -#include "list.h" -#include "map.h" -#include "tcp.h" -#include "tty.h" -#include "eval.h" - -/* anyone knows if ANSI 6429 talks about more than 8 colors? */ -static char *colornames[] = { - "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", - "BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE", "none" -}; - -/* - * show defined aliases - */ -void show_aliases(void) -{ - aliasnode *p; - char buf[BUFSIZE]; - - PRINTF("#%s alias%s defined%c\n", sortedaliases ? "the following" : "no", - (sortedaliases && !sortedaliases->snext) ? " is" : "es are", - sortedaliases ? ':' : '.'); - reverse_sortedlist((sortednode **)&sortedaliases); - for (p = sortedaliases; p; p = p->snext) { - escape_specials(buf, p->name); - tty_printf("#alias %s%s%s%s=%s\n", - p->active ? "" : "(disabled) ", - buf, group_delim, p->group == NULL ? "*" : p->group, p->subst); - } - reverse_sortedlist((sortednode **)&sortedaliases); -} - -/* - * check if an alias name contains dangerous things. - * return 1 if illegal name (and print reason). - * if valid, print a warning for unbalanced () {} and "" - */ -static int check_alias(char *name) -{ - char *p = name, c; - enum { NORM, ESCAPE } state = NORM; - int quotes=0, paren=0, braces=0, ok = 1; - - if (!*p) { - PRINTF("#illegal alias name: is empty!\n"); - error = INVALID_NAME_ERROR; - return 1; - } - if (*p == '{') { - PRINTF("#illegal beginning '{' in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - if (strchr(name, ' ')) { - PRINTF("#illegal spaces in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - - for (; ok && (c = *p); p++) switch (state) { - case NORM: - if (c == ESC) - state = ESCAPE; - else if (quotes) { - if (c == '\"') - quotes = 0; - } - else if (c == '\"') - quotes = 1; - else if (c == ')') - paren--; - else if (c == '(') - paren++; - else if (c == '}') - braces--; - else if (c == '{') - braces++; - else if (c == CMDSEP && !paren && !braces) - ok = 0; - break; - case ESCAPE: - if (c == ESC) - state = ESCAPE; - else /* if (c == ESC2 || c != ESC2) */ - state = NORM; - default: - break; - } - - if (!ok) { - PRINTF("#illegal non-escaped ';' in alias name: \"%s\"\n", name); - error = INVALID_NAME_ERROR; - return 1; - } - - if (quotes || paren || braces) { - PRINTF("#warning: unbalanced%s%s%s in alias name \"%s\" may cause problems\n", - quotes ? " \"\"" : "", paren ? " ()" : "", braces ? " {}" : "", name); - } - - return 0; -} - - -/* - * parse the #alias command - */ -void parse_alias(char *str) -{ - char *left, *right, *group; - aliasnode **np, *p; - - left = str = skipspace(str); - - str = first_valid(str, '='); - - if (*str == '=') { - *str = '\0'; - right = ++str; - unescape(left); - - /* break out group name (if present) */ - group = strstr( left, group_delim ); - if( group ) { - *group = 0; - group += strlen( group_delim ); - } - - if (check_alias(left)) - return; - p = *(np = lookup_alias(left)); - if (!*str) { - /* delete alias */ - if (p) { - if (opt_info) { - PRINTF("#deleting alias: %s=%s\n", left, p->subst); - } - delete_aliasnode(np); - } else { - PRINTF("#unknown alias, cannot delete: \"%s\"\n", left); - } - } else { - /* add/redefine alias */ - - /* direct recursion is supported (alias CAN be defined by itself) */ - if (p) { - free(p->subst); - p->subst = my_strdup(right); - } else - add_aliasnode(left, right); - - /* get alias again to add group (if needed) - * don't take the lookup penalty though if not changing groups */ - if( group != NULL ) { - np = lookup_alias(left); - if( (*np)->group != NULL ) - free((*np)->group); - - if ( *group == '\0' || strcmp(group,"*") == 0 ) - group = NULL; - - (*np)->group = my_strdup(group); - } - - if (opt_info) { - PRINTF("#%s alias in group '%s': %s=%s\n", p ? "changed" : "new", - group == NULL ? "*" : group, left, right); - } - } - } else { - /* show alias */ - - *str = '\0'; - unescape(left); - if (check_alias(left)) - return; - np = lookup_alias(left); - if (*np) { - char buf[BUFSIZE]; - escape_specials(buf, left); - snprintf(inserted_next, BUFSIZE, "#alias %s%s%s=%s", - buf, - group_delim, - (*np)->group == NULL ? "*" : (*np)->group, - (*np)->subst); - } else { - PRINTF("#unknown alias, cannot show: \"%s\"\n", left); - } - } -} - -/* - * delete an action node - */ -static void delete_action(actionnode **nodep) -{ - if (opt_info) { - PRINTF("#deleting action: >%c%s %s\n", (*nodep)->active ? - '+' : '-', (*nodep)->label, (*nodep)->pattern); - } - delete_actionnode(nodep); -} - -/* - * delete a prompt node - */ -static void delete_prompt(actionnode **nodep) -{ - if (opt_info) { - PRINTF("#deleting prompt: >%c%s %s\n", (*nodep)->active ? - '+' : '-', (*nodep)->label, (*nodep)->pattern); - } - delete_promptnode(nodep); -} - -/* - * create new action - */ -static void add_new_action(char *label, char *pattern, char *command, int active, int type, void *q) -{ - add_actionnode(pattern, command, label, active, type, q); - if (opt_info) { - PRINTF("#new action: %c%c%s %s=%s\n", - action_chars[type], - active ? '+' : '-', label, - pattern, command); - } -} - -/* - * create new prompt - */ -static void add_new_prompt(char *label, char *pattern, char *command, int active, int type, void *q) -{ - add_promptnode(pattern, command, label, active, type, q); - if (opt_info) { - PRINTF("#new prompt: %c%c%s %s=%s\n", - action_chars[type], - active ? '+' : '-', label, - pattern, command); - } -} - -/* - * add an action with numbered label - */ -static void add_anonymous_action(char *pattern, char *command, int type, void *q) -{ - static int last = 0; - char label[16]; - do { - sprintf(label, "%d", ++last); - } while (*lookup_action(label)); - add_new_action(label, pattern, command, 1, type, q); -} - -#define ONPROMPT (onprompt ? "prompt" : "action") - -/* - * change fields of an existing action node - * pattern or commands can be NULL if no change - */ -static void change_actionorprompt(actionnode *node, char *pattern, char *command, int type, void *q, int onprompt) -{ -#ifdef USE_REGEXP - if (node->type == ACTION_REGEXP && node->regexp) { - regfree((regex_t *)(node->regexp)); - free(node->regexp); - } - node->regexp = q; -#endif - if (pattern) { - free(node->pattern); - node->pattern = my_strdup(pattern); - node->type = type; - } - if (command) { - free(node->command); - node->command = my_strdup(command); - } - - if (opt_info) { - PRINTF("#changed %s %c%c%s %s=%s\n", ONPROMPT, - action_chars[node->type], - node->active ? '+' : '-', - node->label, node->pattern, node->command); - } -} - -/* - * show defined actions - */ -void show_actions(void) -{ - actionnode *p; - - PRINTF("#%s action%s defined%c\n", actions ? "the following" : "no", - (actions && !actions->next) ? " is" : "s are", actions ? ':' : '.'); - for (p = actions; p; p = p->next) - tty_printf("#action %c%c%s%s%s %s=%s\n", - action_chars[p->type], - p->active ? '+' : '-', p->label, - group_delim, - p->group == NULL ? "*" : p->group, - p->pattern, - p->command); -} - -/* - * show defined prompts - */ -void show_prompts(void) -{ - promptnode *p; - - PRINTF("#%s prompt%s defined%c\n", prompts ? "the following" : "no", - (prompts && !prompts->next) ? " is" : "s are", prompts ? ':' : '.'); - for (p = prompts; p; p = p->next) - tty_printf("#prompt %c%c%s %s=%s\n", - action_chars[p->type], - p->active ? '+' : '-', p->label, - p->pattern, p->command); -} - -/* - * parse the #action and #prompt commands - * this function is too damn complex because of the hairy syntax. it should be - * split up or rewritten as an fsm instead. - */ -void parse_action(char *str, int onprompt) -{ - char *p, label[BUFSIZE], pattern[BUFSIZE], *command, *group; - actionnode **np = NULL; - char sign, assign, hastail; - char active, type = ACTION_WEAK, kind; - void *regexp = 0; - - sign = *(p = skipspace(str)); - if (!sign) { - PRINTF("%s: no arguments given\n", ONPROMPT); - return; - } - - str = p + 1; - - switch (sign) { - case '+': - case '-': /* edit */ - case '<': /* delete */ - case '=': /* list */ - assign = sign; - break; - case '%': /* action_chars[ACTION_REGEXP] */ - type = ACTION_REGEXP; - /* falltrough */ - case '>': /* action_chars[ACTION_WEAK] */ - - /* define/edit */ - assign = '>'; - sign = *(p + 1); - if (!sign) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } else if (sign == '+' || sign == '-') - str++; - else - sign = '+'; - break; - default: - assign = 0; /* anonymous action */ - str = p; - break; - } - - /* labelled action: */ - if (assign != 0) { - p = first_regular(str, ' '); - if ((hastail = *p)) - *p = '\0'; - - /* break out group name (if present) */ - group = strstr( str, group_delim ); - if( group ) { - *group = 0; - group += strlen( group_delim ); - } - - my_strncpy(label, str, BUFSIZE-1); - if (hastail) - *p++ = ' '; /* p points to start of pattern, or to \0 */ - - if (!*label) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } - - - if (onprompt) - np = lookup_prompt(label); - else - np = lookup_action(label); - - /* '<' : remove action */ - if (assign == '<') { - if (!np || !*np) { - PRINTF("#no %s, cannot delete label: \"%s\"\n", - ONPROMPT, label); - } - else { - if (onprompt) - delete_prompt(np); - else - delete_action(np); - } - - /* '>' : define action */ - } else if (assign == '>') { -#ifndef USE_REGEXP - if (type == ACTION_REGEXP) { - PRINTF("#error: regexp not allowed\n"); - return; - } -#endif - - if (sign == '+') - active = 1; - else - active = 0; - - if (!*label) { - PRINTF("#%s: label expected\n", ONPROMPT); - return; - } - - p = skipspace(p); - if (*p == '(') { - ptr pbuf = (ptr)0; - p++; - kind = evalp(&pbuf, &p); - if (!REAL_ERROR && kind != TYPE_TXT) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", ONPROMPT); - print_error(error=NO_STRING_ERROR); - ptrdel(pbuf); - return; - } - if (pbuf) { - my_strncpy(pattern, ptrdata(pbuf), BUFSIZE-1); - ptrdel(pbuf); - } else - pattern[0] = '\0'; - if (*p) - p = skipspace(++p); - if ((hastail = *p == '=')) - p++; - } - else { - p = first_regular(command = p, '='); - if ((hastail = *p)) - *p = '\0'; - my_strncpy(pattern, command, BUFSIZE-1); - - if (hastail) - *p++ = '='; - } - - if (!*pattern) { - PRINTF("#error: pattern of #%ss must be non-empty.\n", ONPROMPT); - return; - } - -#ifdef USE_REGEXP - if (type == ACTION_REGEXP && hastail) { - int errcode; - char unesc_pat[BUFSIZE]; - - /* - * HACK WARNING: - * we unescape regexp patterns now, instead of doing - * jit+unescape at runtime, as for weak actions. - */ - strcpy(unesc_pat, pattern); - unescape(unesc_pat); - - regexp = malloc(sizeof(regex_t)); - if (!regexp) { - errmsg("malloc"); - return; - } - - if ((errcode = regcomp((regex_t *)regexp, unesc_pat, REG_EXTENDED))) { - int n; - char *tmp; - n = regerror(errcode, (regex_t *)regexp, NULL, 0); - tmp = (char *)malloc(n); - if (tmp) { - if (!regerror(errcode, (regex_t *)regexp, tmp, n)) - errmsg("regerror"); - else { - PRINTF("#regexp error: %s\n", tmp); - } - free(tmp); - } else { - error = NO_MEM_ERROR; - errmsg("malloc"); - } - regfree((regex_t *)regexp); - free(regexp); - return; - } - } -#endif - command = p; - - if (hastail) { - if (np && *np) { - change_actionorprompt(*np, pattern, command, type, regexp, onprompt); - (*np)->active = active; - } else { - if (onprompt) - add_new_prompt(label, pattern, command, active, - type, regexp); - else - add_new_action(label, pattern, command, active, - type, regexp); - } - - if( group != NULL ) { - /* I don't know why but we need to clip this because somehow - * the original string is restored to *p at some point instead - * of the null-clipped one we used waaaay at the top. */ - p = first_regular(group, ' '); - *p = '\0'; - np = lookup_action(label); - if( (*np)->group != NULL ) - free( (*np)->group ); - - if ( *group == '\0' || strcmp(group,"*") == 0 ) - group = NULL; - - (*np) -> group = my_strdup( group ); - } - } - - /* '=': list action */ - } else if (assign == '='){ - if (np && *np) { - int len = (int)strlen((*np)->label); - sprintf(inserted_next, "#%s %c%c%.*s %.*s=%.*s", ONPROMPT, - action_chars[(*np)->type], (*np)->active ? '+' : '-', - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7, (*np)->label, - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len, (*np)->pattern, - BUFSIZE - 6 /*strlen(ONPROMPT)*/ - 7 - len - (int)strlen((*np)->pattern), - (*np)->command); - } else { - PRINTF("#no %s, cannot list label: \"%s\"\n", ONPROMPT, label); - } - - /* '+', '-': turn action on/off */ - } else { - if (np && *np) { - (*np)->active = (sign == '+'); - if (opt_info) { - PRINTF("#%s %c%s %s is now o%s.\n", ONPROMPT, - action_chars[(*np)->type], - label, - (*np)->pattern, (sign == '+') ? "n" : "ff"); - } - } else { - PRINTF("#no %s, cannot turn o%s label: \"%s\"\n", ONPROMPT, - (sign == '+') ? "n" : "ff", label); - } - } - - /* anonymous action, cannot be regexp */ - } else { - - if (onprompt) { - PRINTF("#anonymous prompts not supported.\n#please use labelled prompts.\n"); - return; - } - - command = first_regular(str, '='); - - if (*command == '=') { - *command = '\0'; - - my_strncpy(pattern, str, BUFSIZE-1); - *command++ = '='; - np = lookup_action_pattern(pattern); - if (*command) - if (np && *np) - change_actionorprompt(*np, NULL, command, ACTION_WEAK, NULL, 0); - else - add_anonymous_action(pattern, command, ACTION_WEAK, NULL); - else if (np && *np) - delete_action(np); - else { - PRINTF("#no action, cannot delete pattern: \"%s\"\n", - pattern); - return; - } - } else { - np = lookup_action_pattern(str); - if (np && *np) { - sprintf(inserted_next, "#action %.*s=%.*s", - BUFSIZE - 10, (*np)->pattern, - BUFSIZE - (int)strlen((*np)->pattern) - 10, - (*np)->command); - } else { - PRINTF("#no action, cannot show pattern: \"%s\"\n", str); - } - } - } -} - -#undef ONPROMPT - -/* - * display attribute syntax - */ -void show_attr_syntax(void) -{ - int i; - PRINTF("#attribute syntax:\n\tOne or more of:\tbold, blink, underline, inverse\n\tand/or\t[foreground] [ON background]\n\tColors: "); - for (i = 0; i < COLORS; i++) - tty_printf("%s%s", colornames[i], - (i == LOWCOLORS - 1 || i == COLORS - 1) ? "\n\t\t" : ","); - tty_printf("%s\n", colornames[i]); -} - -/* - * put escape sequences to turn on/off an attribute in given buffers - */ -void attr_string(int attrcode, char *begin, char *end) -{ - int fg = FOREGROUND(attrcode), bg = BACKGROUND(attrcode), - tok = ATTR(attrcode); - char need_end = 0; - *begin = *end = '\0'; - - if (tok > (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE) - || fg > COLORS || bg > COLORS || attrcode == NOATTRCODE) - return; /* do nothing */ - - if (fg < COLORS) { - if (bg < COLORS) { - sprintf(begin, "\033[%c%d;%s%dm", - fg<LOWCOLORS ? '3' : '9', fg % LOWCOLORS, - bg<LOWCOLORS ? "4" :"10", bg % LOWCOLORS); -#ifdef TERM_LINUX - strcpy(end, "\033[39;49m"); -#endif - } else { - sprintf(begin, "\033[%c%dm", - fg<LOWCOLORS ? '3' : '9', fg % LOWCOLORS); -#ifdef TERM_LINUX - strcpy(end, "\033[39m"); -#endif - } - } else if (bg < COLORS) { - sprintf(begin, "\033[%s%dm", - bg<LOWCOLORS ? "4" : "10", bg % LOWCOLORS); -#ifdef TERM_LINUX - strcpy(end, "\033[49m"); -#endif - } - -#ifndef TERM_LINUX - if (fg < COLORS || bg < COLORS) - need_end = 1; -#endif - - if (tok & ATTR_BOLD) { - if (tty_modebold[0]) { - strcat(begin, tty_modebold); -#ifdef TERM_LINUX - strcat(end, "\033[21m"); -#else - need_end = 1; -#endif - } else { - strcat(begin, tty_modestandon); - strcpy(end, tty_modestandoff); - } - } - - if (tok & ATTR_BLINK) { - if (tty_modeblink[0]) { - strcat(begin, tty_modeblink); -#ifdef TERM_LINUX - strcat(end, "\033[25m"); -#else - need_end = 1; -#endif - } else { - strcat(begin, tty_modestandon); - strcpy(end, tty_modestandoff); - } - } - - if (tok & ATTR_UNDERLINE) { - if (tty_modeuline[0]) { - strcat(begin, tty_modeuline); -#ifdef TERM_LINUX - strcat(end, "\033[24m"); -#else - need_end = 1; -#endif - } else { - strcat(begin, tty_modestandon); - strcpy(end, tty_modestandoff); - } - } - - if (tok & ATTR_INVERSE) { - if (tty_modeinv[0]) { - strcat(begin, tty_modeinv); -#ifdef TERM_LINUX - strcat(end, "\033[27m"); -#else - need_end = 1; -#endif - } else { - strcat(begin, tty_modestandon); - strcpy(end, tty_modestandoff); - } - } - -#ifndef TERM_LINUX - if (need_end) - strcpy(end, tty_modenorm); -#endif -} - -/* - * parse attribute description in line. - * Return attribute if successful, -1 otherwise. - */ -int parse_attributes(char *line) -{ - char *p; - int tok = 0, fg, bg, t = -1; - - if (!(p = strtok(line, " "))) - return NOATTRCODE; - - fg = bg = NO_COLOR; - - while (t && p) { - if (!strcasecmp(p, "bold")) - t = ATTR_BOLD; - else if (!strcasecmp(p, "blink")) - t = ATTR_BLINK; - else if (!strcasecmp(p, "underline")) - t = ATTR_UNDERLINE; - else if (!strcasecmp(p, "inverse") || !strcasecmp(p, "reverse")) - t = ATTR_INVERSE; - else - t = 0; - - if (t) { - tok |= t; - p = strtok(NULL, " "); - } - } - - if (!p) - return ATTRCODE(tok, fg, bg); - - for (t = 0; t <= COLORS && strcmp(p, colornames[t]); t++) - ; - if (t <= COLORS) { - fg = t; - p = strtok(NULL, " "); - } - - if (!p) - return ATTRCODE(tok, fg, bg); - - if (strcasecmp(p, "on")) - return -1; /* invalid attribute */ - - if (!(p = strtok(NULL, " "))) - return -1; - - for (t = 0; t <= COLORS && strcmp(p, colornames[t]); t++) - ; - if (t <= COLORS) - bg = t; - else - return -1; - - return ATTRCODE(tok, fg, bg); -} - - -/* - * return a static pointer to name of given attribute code - */ -char *attr_name(int attrcode) -{ - static char name[BUFSIZE]; - int fg = FOREGROUND(attrcode), bg = BACKGROUND(attrcode), tok = ATTR(attrcode); - - name[0] = 0; - if (tok > (ATTR_BOLD | ATTR_BLINK | ATTR_UNDERLINE | ATTR_INVERSE) || fg > COLORS || bg > COLORS) - return name; /* error! */ - - if (tok & ATTR_BOLD) - strcat(name, "bold "); - if (tok & ATTR_BLINK) - strcat(name, "blink "); - if (tok & ATTR_UNDERLINE) - strcat(name, "underline "); - if (tok & ATTR_INVERSE) - strcat(name, "inverse "); - - if (fg < COLORS || (fg == bg && fg == COLORS && !tok)) - strcat(name, colornames[fg]); - - if (bg < COLORS) { - strcat(name, " on "); - strcat(name, colornames[bg]); - } - - if (!*name) - strcpy(name, "none"); - - return name; -} - -/* - * show defined marks - */ -void show_marks(void) -{ - marknode *p; - PRINTF("#%s marker%s defined%c\n", markers ? "the following" : "no", - (markers && !markers->next) ? " is" : "s are", - markers ? ':' : '.'); - for (p = markers; p; p = p->next) - tty_printf("#mark %s%s=%s\n", p->mbeg ? "^" : "", - p->pattern, attr_name(p->attrcode)); -} - - -/* - * parse arguments to the #mark command - */ -void parse_mark(char *str) -{ - char *p; - marknode **np, *n; - char mbeg = 0; - - if (*str == '=') { - PRINTF("#marker must be non-null.\n"); - return; - } - p = first_regular(str, '='); - if (!*p) { - if (*str == '^') - mbeg = 1, str++; - unescape(str); - np = lookup_marker(str, mbeg); - if ((n = *np)) { - ptr pbuf = (ptr)0; - char *name; - pbuf = ptrmescape(pbuf, n->pattern, strlen(n->pattern), 0); - if (MEM_ERROR) { ptrdel(pbuf); return; } - name = attr_name(n->attrcode); - sprintf(inserted_next, "#mark %s%.*s=%.*s", n->mbeg ? "^" : "", - BUFSIZE-(int)strlen(name)-9, pbuf ? ptrdata(pbuf) : "", - BUFSIZE-9, name); - ptrdel(pbuf); - } else { - PRINTF("#unknown marker, cannot show: \"%s\"\n", str); - } - - } else { - int attrcode, wild = 0; - char pattern[BUFSIZE], *p2; - - *(p++) = '\0'; - p = skipspace(p); - if (*str == '^') - mbeg = 1, str++; - my_strncpy(pattern, str, BUFSIZE-1); - unescape(pattern); - p2 = pattern; - while (*p2) { - if (ISMARKWILDCARD(*p2)) { - wild = 1; - if (ISMARKWILDCARD(*(p2 + 1))) { - error=SYNTAX_ERROR; - PRINTF("#error: two wildcards (& or $) may not be next to eachother\n"); - return; - } - } - p2++; - } - - np = lookup_marker(pattern, mbeg); - attrcode = parse_attributes(p); - if (attrcode == -1) { - PRINTF("#invalid attribute syntax.\n"); - error=SYNTAX_ERROR; - if (opt_info) show_attr_syntax(); - } else if (!*p) - if ((n = *np)) { - if (opt_info) { - PRINTF("#deleting mark: %s%s=%s\n", n->mbeg ? "^" : "", - n->pattern, attr_name(n->attrcode)); - } - delete_marknode(np); - } else { - PRINTF("#unknown marker, cannot delete: \"%s%s\"\n", - mbeg ? "^" : "", pattern); - } - else { - if (*np) { - (*np)->attrcode = attrcode; - if (opt_info) { - PRINTF("#changed"); - } - } else { - add_marknode(pattern, attrcode, mbeg, wild); - if (opt_info) { - PRINTF("#new"); - } - } - if (opt_info) - tty_printf(" mark: %s%s=%s\n", mbeg ? "^" : "", - pattern, attr_name(attrcode)); - } - } -} - -/* - * turn ASCII description of a sequence - * into raw escape sequence - * return pointer to end of ASCII description - */ -static char *unescape_seq(char *buf, char *seq, int *seqlen) -{ - char c, *start = buf; - enum { NORM, ESCSINGLE, ESCAPE, CARET, DONE } state = NORM; - - for (; (c = *seq); seq++) { - switch (state) { - case NORM: - if (c == '^') - state = CARET; - else if (c == ESC) - state = ESCSINGLE; - else if (c == '=') - state = DONE; - else - *(buf++) = c; - break; - case CARET: - /* - * handle ^@ ^A ... ^_ as expected: - * ^@ == 0x00, ^A == 0x01, ... , ^_ == 0x1f - */ - if (c > 0x40 && c < 0x60) - *(buf++) = c & 0x1f; - /* special case: ^? == 0x7f */ - else if (c == '?') - *(buf++) = 0x7f; - state = NORM; - break; - case ESCSINGLE: - case ESCAPE: - /* - * GH: \012 ==> octal number - */ - if (state == ESCSINGLE && - ISODIGIT(seq[0]) && ISODIGIT(seq[1]) && ISODIGIT(seq[2])) { - *(buf++) =(((seq[0] - '0') << 6) | - ((seq[1] - '0') << 3) | - (seq[2] - '0')); - seq += 2; - } else { - *(buf++) = c; - if (c == ESC) - state = ESCAPE; - else - state = NORM; - } - break; - default: - break; - } - if (state == DONE) - break; - } - *buf = '\0'; - *seqlen = buf - start; - return seq; -} - -/* - * read a single character from tty, with timeout in milliseconds. - * timeout == 0 means wait indefinitely (no timeout). - * return char or -1 if timeout was reached. - */ -static int get_one_char(int timeout) -{ - struct timeval timeoutbuf; - fd_set fds; - int n; - char c; - - again: - FD_ZERO(&fds); - FD_SET(tty_read_fd, &fds); - timeoutbuf.tv_sec = 0; - timeoutbuf.tv_usec = timeout * uSEC_PER_mSEC; - n = select(tty_read_fd + 1, &fds, NULL, NULL, - timeout ? &timeoutbuf : NULL); - if (n == -1 && errno == EINTR) - return -1; - if (n == -1) { - errmsg("select"); - return -1; - } - do { - n = tty_read(&c, 1); - } while (n < 0 && errno == EINTR); - if (n == 1) - return (unsigned char)c; - if (n < 0) { - if (errno != EAGAIN) - errmsg("read from tty"); - return -1; - } - /* n == 0 */ - if (timeout == 0) - goto again; - return -1; -} - -/* - * print an escape sequence in human-readably form. - */ -void print_seq(char *seq, int len) -{ - while (len--) { - unsigned char ch = *(seq++); - if (ch == '\033') { - tty_puts("esc "); - continue; - } - if (ch < ' ') { - tty_putc('^'); - ch |= '@'; - } - if (ch == ' ') - tty_puts("space "); - else if (ch == 0x7f) - tty_puts("del "); - else if (ch & 0x80) - tty_printf("\\%03o ", ch); - else - tty_printf("%c ", ch); - } -} - -/* - * return a static pointer to escape sequence made printable, for use in - * definition-files - */ -char *seq_name(char *seq, int len) -{ - static char buf[CAPLEN*4]; - char *p = buf; - /* - * rules: control chars are written as ^X, where - * X is char | 64 - * - * GH: codes > 0x80 ==> octal \012 - * - * special case: 0x7f is written ^? - */ - while (len--) { - unsigned char c = *seq++; - if (c == '^' || (c && strchr(SPECIAL_CHARS, c))) - *(p++) = ESC; - - if (c < ' ') { - *(p++) = '^'; - *(p++) = c | '@'; - } else if (c == 0x7f) { - *(p++) = '^'; - *(p++) = '?'; - } else if (c & 0x80) { - /* GH: save chars with high bit set in octal */ - sprintf(p, "\\%03o", (int)c); - p += strlen(p); - } else - *(p++) = c; - } - *p = '\0'; - return buf; -} - -/* - * read a single escape sequence from the keyboard - * prompting user for it; return static pointer - */ -char *read_seq(char *name, int *len) -{ - static char seq[CAPLEN]; - int i = 1, tmp; - - PRINTF("#please press the key \"%s\" : ", name); - tty_flush(); - - if ((tmp = get_one_char(0)) >= 0) - seq[0] = tmp; - else { - tty_puts("#unable to get key. Giving up.\n"); - return NULL; - } - - while (i < CAPLEN - 1 && - (tmp = get_one_char(KBD_TIMEOUT)) >= 0) - seq[i++] = tmp; - *len = i; - print_seq(seq, i); - - tty_putc('\n'); - if (seq[0] >= ' ' && seq[0] <= '~') { - PRINTF("#that is not a redefinable key.\n"); - return NULL; - } - return seq; -} - -/* - * show full definition of one binding, - * with custom message - */ -static void show_single_bind(char *msg, keynode *p) -{ - if (p->funct == key_run_command) { - PRINTF("#%s %s %s=%s\n", msg, p->name, - seq_name(p->sequence, p->seqlen), p->call_data); - } else { - PRINTF("#%s %s %s=%s%s%s\n", msg, p->name, - seq_name(p->sequence, p->seqlen), - internal_functions[lookup_edit_function(p->funct)].name, - p->call_data ? " " : "", - p->call_data ? p->call_data : ""); - } -} - -/* - * list keyboard bindings - */ -void show_binds(char edit) -{ - keynode *p; - int count = 0; - for (p = keydefs; p; p = p->next) { - if (edit != (p->funct == key_run_command)) { - if (!count) { - if (edit) { - PRINTF("#line-editing keys:\n"); - } else { - PRINTF("#user-defined keys:\n"); - } - } - show_single_bind("bind", p); - ++count; - } - } - if (!count) { - PRINTF("#no key bindings defined right now.\n"); - } -} - - -/* - * interactively create a new keybinding - */ -static void define_new_key(char *name, char *command) -{ - char *seq, *arg; - keynode *p; - int seqlen, function; - - seq = read_seq(name, &seqlen); - if (!seq) return; - - for (p = keydefs; p; p = p->next) - /* GH: don't allow binding of supersets of another bind */ - if (!memcmp(p->sequence, seq, MIN2(p->seqlen, seqlen))) { - show_single_bind("key already bound as:", p); - return; - } - - function = lookup_edit_name(command, &arg); - if (function) - add_keynode(name, seq, seqlen, - internal_functions[function].funct, arg); - else - add_keynode(name, seq, seqlen, key_run_command, command); - - if (opt_info) { - PRINTF("#new key binding: %s %s=%s\n", - name, seq_name(seq, seqlen), command); - } -} - -/* - * parse the #bind command non-interactively. - */ -static void parse_bind_noninteractive(char *arg) -{ - char rawseq[CAPLEN], *p, *seq, *params; - int function, seqlen; - keynode **kp; - - p = strchr(arg, ' '); - if (!p) { - PRINTF("#syntax error: \"#bind %s\"\n", arg); - return; - } - *(p++) = '\0'; - seq = p = skipspace(p); - - p = unescape_seq(rawseq, p, &seqlen); - if (!p[0] || !p[1]) { - PRINTF("#syntax error: \"#bind %s %s\"\n", arg, seq); - return; - } - *p++ = '\0'; - - kp = lookup_key(arg); - if (kp && *kp) - delete_keynode(kp); - - if ((function = lookup_edit_name(p, ¶ms))) - add_keynode(arg, rawseq, seqlen, - internal_functions[function].funct, params); - else - add_keynode(arg, rawseq, seqlen, key_run_command, p); - - if (opt_info) { - PRINTF("#%s: %s %s=%s\n", - (kp && *kp) ? "redefined key" : "new key binding", - arg, seq, p); - } -} - -/* - * parse the argument of the #bind command (interactive) - */ -void parse_bind(char *arg) -{ - char *p, *q, *command, *params; - char *name = arg; - keynode **npp, *np; - int function; - - p = first_valid(arg, '='); - q = first_valid(arg, ' '); - q = skipspace(q); - - if (*p && *q && p > q) { - parse_bind_noninteractive(arg); - return; - } - - if (*p) { - *(p++) = '\0'; - np = *(npp = lookup_key(name)); - if (*p) { - command = p; - if (np) { - if (np->funct == key_run_command) - free(np->call_data); - if ((function = lookup_edit_name(command, ¶ms))) { - np->call_data = my_strdup(params); - np->funct = internal_functions[function].funct; - } else { - np->call_data = my_strdup(command); - np->funct = key_run_command; - } - if (opt_info) { - PRINTF("#redefined key: %s %s=%s\n", name, - seq_name(np->sequence, np->seqlen), - command); - } - } else - define_new_key(name, command); - } else { - if (np) { - if (opt_info) - show_single_bind("deleting key binding:", np); - delete_keynode(npp); - } else { - PRINTF("#no such key: \"%s\"\n", name); - } - } - } else { - np = *(npp = lookup_key(name)); - if (np) { - char *seqname; - int seqlen; - seqname = seq_name(np->sequence, np->seqlen); - seqlen = strlen(seqname); - - if (np->funct == key_run_command) - sprintf(inserted_next, "#bind %.*s %s=%.*s", - BUFSIZE-seqlen-9, name, seqname, - BUFSIZE-seqlen-(int)strlen(name)-9, - np->call_data); - else { - p = internal_functions[lookup_edit_function(np->funct)].name; - sprintf(inserted_next, "#bind %.*s %s=%s%s%.*s", - BUFSIZE-seqlen-10, name, seqname, p, - np->call_data ? " " : "", - BUFSIZE-seqlen-(int)strlen(name)-(int)strlen(p)-10, - np->call_data ? np->call_data : ""); - } - } else { - PRINTF("#no such key: \"%s\"\n", name); - } - } -} - -void parse_rebind(char *arg) -{ - char rawseq[CAPLEN], *seq, **old; - keynode **kp, *p; - int seqlen; - - arg = skipspace(arg); - if (!*arg) { - PRINTF("#rebind: missing key.\n"); - return; - } - - seq = first_valid(arg, ' '); - if (*seq) { - *seq++ = '\0'; - seq = skipspace(seq); - } - - kp = lookup_key(arg); - if (!kp || !*kp) { - PRINTF("#no such key: \"%s\"\n", arg); - return; - } - - if (!*seq) { - seq = read_seq(arg, &seqlen); - if (!seq) - return; - } else { - (void)unescape_seq(rawseq, seq, &seqlen); - seq = rawseq; - } - - for (p = keydefs; p; p = p->next) { - if (p == *kp) - continue; - if (!memcmp(p->sequence, seq, MIN2(seqlen, p->seqlen))) { - show_single_bind("key already bound as:", p); - return; - } - } - - old = &((*kp)->sequence); - if (*old) - free(*old); - *old = (char *)malloc((*kp)->seqlen = seqlen); - memmove(*old, seq, seqlen); - - if (opt_info) - show_single_bind("redefined key:", *kp); -} - -/* - * evaluate an expression, or unescape a text. - * set value of start and end line if <(expression...) or !(expression...) - * if needed, use/malloc "pbuf" as buffer (on error, also free pbuf) - * return resulting char * - */ -char *redirect(char *arg, ptr *pbuf, char *kind, char *name, int also_num, long *start, long *end) -{ - char *tmp = skipspace(arg), k; - int type, i; - - if (!pbuf) { - print_error(error=INTERNAL_ERROR); - return NULL; - } - - k = *tmp; - if (k == '!' || k == '<') - arg = ++tmp; - else - k = 0; - - *start = *end = 0; - - if (*tmp=='(') { - - arg = tmp + 1; - type = evalp(pbuf, &arg); - if (!REAL_ERROR && type!=TYPE_TXT && !also_num) - error=NO_STRING_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", name); - print_error(error); - ptrdel(*pbuf); - return NULL; - } - for (i=0; i<2; i++) if (*arg == CMDSEP) { - long buf; - - arg++; - if (!i && *arg == CMDSEP) { - *start = 1; - continue; - } - else if (i && *arg == ')') { - *end = LONG_MAX; - continue; - } - - type = evall(&buf, &arg); - if (!REAL_ERROR && type != TYPE_NUM) - error=NO_NUM_VALUE_ERROR; - if (REAL_ERROR) { - PRINTF("#%s: ", name); - print_error(error); - ptrdel(*pbuf); - return NULL; - } - if (i) - *end = buf; - else - *start = buf; - } - if (*arg != ')') { - PRINTF("#%s: ", name); - print_error(error=MISSING_PAREN_ERROR); - ptrdel(*pbuf); - return NULL; - } - if (!*pbuf) { - /* make space to add a final \n */ - *pbuf = ptrsetlen(*pbuf, 1); - ptrzero(*pbuf); - if (REAL_ERROR) { - print_error(error); - ptrdel(*pbuf); - return NULL; - } - } - arg = ptrdata(*pbuf); - if (!*start && *end) - *start = 1; - } else - unescape(arg); - - *kind = k; - return arg; -} - -void show_vars(void) -{ - varnode *v; - int i, type; - ptr p = (ptr)0; - - PRINTF("#the following variables are defined:\n"); - - for (type = 0; !REAL_ERROR && type < 2; type++) { - reverse_sortedlist((sortednode **)&sortednamed_vars[type]); - v = sortednamed_vars[type]; - while (v) { - if (type == 0) { - tty_printf("#(@%s = %ld)\n", v->name, v->num); - } else { - p = ptrescape(p, v->str, 0); - if (REAL_ERROR) { - print_error(error); - break; - } - tty_printf("#($%s = \"%s\")\n", v->name, - p ? ptrdata(p) : ""); - } - v = v->snext; - } - reverse_sortedlist((sortednode **)&sortednamed_vars[type]); - } - for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) { - if (*VAR[i].num) - tty_printf("#(@%d = %ld)\n", i, *VAR[i].num); - } - for (i = -NUMVAR; !REAL_ERROR && i < NUMPARAM; i++) { - if (*VAR[i].str && ptrlen(*VAR[i].str)) { - p = ptrescape(p, *VAR[i].str, 0); - if (p && ptrlen(p)) - tty_printf("#($%d = \"%s\")\n", i, ptrdata(p)); - } - } - ptrdel(p); -} - -void show_delaynode(delaynode *p, int in_or_at) -{ - long d; - struct tm *s; - char buf[BUFSIZE]; - - update_now(); - d = diff_vtime(&p->when, &now); - s = localtime((time_t *)&p->when.tv_sec); - /* s now points to a calendar struct */ - if (in_or_at) { - - if (in_or_at == 2) { - /* write time in buf */ - (void)strftime(buf, BUFSIZE - 1, "%H%M%S", s); - sprintf(inserted_next, "#at %.*s (%s) %.*s", - BUFSIZE - 15, p->name, buf, - BUFSIZE - 15 - (int)strlen(p->name), p->command); - } - else - sprintf(inserted_next, "#in %.*s (%ld) %.*s", - BUFSIZE - LONGLEN - 9, p->name, d, - BUFSIZE - LONGLEN - 9 - (int)strlen(p->name), p->command); - } else { - (void)strftime(buf, BUFSIZE - 1, "%H:%M:%S", s); - PRINTF("#at (%s) #in (%ld) \"%s\" %s\n", buf, d, p->name, p->command); - } -} - -void show_delays(void) -{ - delaynode *p; - int n = (delays ? delays->next ? 2 : 1 : 0) + - (dead_delays ? dead_delays->next ? 2 : 1 : 0); - - PRINTF("#%s delay label%s defined%c\n", n ? "the following" : "no", - n == 1 ? " is" : "s are", n ? ':' : '.'); - for (p = delays; p; p = p->next) - show_delaynode(p, 0); - for (p = dead_delays; p; p = p->next) - show_delaynode(p, 0); -} - -void change_delaynode(delaynode **p, char *command, long millisec) -{ - delaynode *m=*p; - - *p = m->next; - m->when.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC; - m->when.tv_sec = millisec / mSEC_PER_SEC; - update_now(); - add_vtime(&m->when, &now); - if (*command) { - if (strlen(command) > strlen(m->command)) { - free((void*)m->command); - m->command = my_strdup(command); - } - else - strcpy(m->command, command); - } - if (millisec < 0) - add_node((defnode*)m, (defnode**)&dead_delays, rev_time_sort); - else - add_node((defnode*)m, (defnode**)&delays, time_sort); - if (opt_info) { - PRINTF("#changed "); - show_delaynode(m, 0); - } -} - -void new_delaynode(char *name, char *command, long millisec) -{ - vtime t; - delaynode *node; - - t.tv_usec = (millisec % mSEC_PER_SEC) * uSEC_PER_mSEC; - t.tv_sec = millisec / mSEC_PER_SEC; - update_now(); - add_vtime(&t, &now); - node = add_delaynode(name, command, &t, millisec < 0); - if (opt_info && node) { - PRINTF("#new "); - show_delaynode(node, 0); - } -} - -void show_history(int count) -{ - int i = curline; - - if (!count) count = lines - 1; - if (count >= MAX_HIST) count = MAX_HIST - 1; - i -= count; - if (i < 0) i += MAX_HIST; - - while (count) { - if (hist[i]) { - PRINTF("#%2d: %s\n", count, hist[i]); - } - count--; - if (++i == MAX_HIST) i = 0; - } -} - -void exe_history(int count) -{ - int i = curline; - char buf[BUFSIZE]; - - if (count >= MAX_HIST) - count = MAX_HIST - 1; - i -= count; - if (i < 0) - i += MAX_HIST; - if (hist[i]) { - strcpy(buf, hist[i]); - parse_user_input(buf, 0); - } -} - |