aboutsummaryrefslogtreecommitdiffstats
path: root/cmd2.c
diff options
context:
space:
mode:
authorSteve Slaven <bpk@hoopajoo.net>2005-03-12 00:27:55 (GMT)
committerSteve Slaven <bpk@hoopajoo.net>2005-03-12 00:27:55 (GMT)
commit77b250bfb63a28a8fe8a8da67de7354bce6e61ff (patch)
treee28f72c18305016c19c608f5fce4432529e7673d /cmd2.c
parent5dfb1906b299bf7c8a1ee3ba5cd1c9ea40648d89 (diff)
downloadpowwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.zip
powwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.tar.gz
powwow-77b250bfb63a28a8fe8a8da67de7354bce6e61ff.tar.bz2
Initial revisionv1.2.7
Diffstat (limited to 'cmd2.c')
-rw-r--r--cmd2.c1665
1 files changed, 1665 insertions, 0 deletions
diff --git a/cmd2.c b/cmd2.c
new file mode 100644
index 0000000..b43f68b
--- /dev/null
+++ b/cmd2.c
@@ -0,0 +1,1665 @@
+/*
+ * 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 <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>
+
+#ifdef USE_REGEXP
+# include "malloc.h"
+# include <regex.h>
+#endif
+
+int strcasecmp();
+int select();
+
+#include "defines.h"
+#include "main.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 __P0 (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\n",
+ p->active ? "" : "(disabled) ",
+ buf,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 __P1 (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 __P1 (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 = strchr( left, '@' );
+ if( group ) {
+ *group = 0;
+ group++;
+ }
+
+ if (check_alias(left))
+ return;
+ p = *(np = lookup_alias(left));
+ if (!*str) {
+ /* delete alias */
+ if (p) {
+ if (echo_int) {
+ 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 && *group != '\0' ) {
+ np = lookup_alias(left);
+ if( (*np)->group != NULL )
+ free((*np)->group);
+
+ (*np)->group = my_strdup(group);
+ }
+
+ if (echo_int) {
+ 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);
+ sprintf(inserted_next, "#alias %s%.*s@%s=%.*s",
+ BUFSIZE-9, buf,
+ (*np)->group == NULL ? "*" : (*np)->group,
+ BUFSIZE-(int)strlen(buf)-9, (*np)->subst);
+ } else {
+ PRINTF("#unknown alias, cannot show: `%s'\n", left);
+ }
+ }
+}
+
+/*
+ * delete an action node
+ */
+static void delete_action __P1 (actionnode **,nodep)
+{
+ if (echo_int) {
+ PRINTF("#deleting action: >%c%s %s\n", (*nodep)->active ?
+ '+' : '-', (*nodep)->label, (*nodep)->pattern);
+ }
+ delete_actionnode(nodep);
+}
+
+/*
+ * delete a prompt node
+ */
+static void delete_prompt __P1 (actionnode **,nodep)
+{
+ if (echo_int) {
+ PRINTF("#deleting prompt: >%c%s %s\n", (*nodep)->active ?
+ '+' : '-', (*nodep)->label, (*nodep)->pattern);
+ }
+ delete_promptnode(nodep);
+}
+
+/*
+ * create new action
+ */
+static void add_new_action __P6 (char *,label, char *,pattern, char *,command, int,active, int,type, void *,q)
+{
+ add_actionnode(pattern, command, label, active, type, q);
+ if (echo_int) {
+ PRINTF("#new action: %c%c%s %s=%s\n",
+ action_chars[type],
+ active ? '+' : '-', label,
+ pattern, command);
+ }
+}
+
+/*
+ * create new prompt
+ */
+static void add_new_prompt __P6 (char *,label, char *,pattern, char *,command, int,active, int,type, void *,q)
+{
+ add_promptnode(pattern, command, label, active, type, q);
+ if (echo_int) {
+ 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 __P4 (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 __P6 (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 (echo_int) {
+ 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 __P0 (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\n",
+ action_chars[p->type],
+ p->active ? '+' : '-', p->label,
+ p->group == NULL ? "*" : p->group,
+ p->pattern,
+ p->command);
+}
+
+/*
+ * show defined prompts
+ */
+void show_prompts __P0 (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 __P2 (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 = strchr( str, '@' );
+ if( group ) {
+ *group = 0;
+ group++;
+ }
+
+ 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 && *group != '\0' ) {
+ /* 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 );
+ (*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 (echo_int) {
+ 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", pattern);
+ }
+ }
+ }
+}
+
+#undef ONPROMPT
+
+/*
+ * display attribute syntax
+ */
+void show_attr_syntax __P0 (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 __P3 (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 __P1 (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 __P1 (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 __P0 (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 __P1 (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 (echo_int) show_attr_syntax();
+ } else if (!*p)
+ if ((n = *np)) {
+ if (echo_int) {
+ 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 (echo_int) {
+ PRINTF("#changed");
+ }
+ } else {
+ add_marknode(pattern, attrcode, mbeg, wild);
+ if (echo_int) {
+ PRINTF("#new");
+ }
+ }
+ if (echo_int)
+ 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 __P3 (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 __P1 (int,timeout)
+{
+ extern int errno;
+ int err;
+ fd_set fds;
+ char c;
+ struct timeval timeoutbuf;
+
+ FD_ZERO(&fds);
+ FD_SET(tty_read_fd, &fds);
+ timeoutbuf.tv_sec = 0;
+ timeoutbuf.tv_usec = timeout * uSEC_PER_mSEC;
+ err = select(tty_read_fd+1, &fds, NULL, NULL,
+ timeout ? &timeoutbuf : NULL);
+ if (err == 0 || (err == -1 && errno == EINTR))
+ return -1;
+ if (err == -1) {
+ errmsg("select");
+ return -1;
+ }
+ while ((err = tty_read(tty_read_fd, &c, 1)) < 0 && errno == EINTR)
+ ;
+ if (err != 1) {
+ errmsg("read from tty");
+ return -1;
+ }
+ return (int)c;
+}
+
+/*
+ * print an escape sequence in human-readably form.
+ */
+void print_seq __P2 (char *,seq, int,len)
+{
+ char ch;
+
+ while (len--) {
+ 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 __P2 (char *,seq, int,len)
+{
+ static char buf[CAPLEN*4];
+ char *p = buf;
+ char c;
+ /*
+ * rules: control chars are written as ^X, where
+ * X is char | 64
+ *
+ * GH: codes > 0x80 ==> octal \012
+ *
+ * special case: 0x7f is written ^?
+ */
+ while (len--) {
+ 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 __P2 (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 __P2 (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 __P1 (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 __P2 (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 (echo_int) {
+ 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 __P1 (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, &params)))
+ add_keynode(arg, rawseq, seqlen,
+ internal_functions[function].funct, params);
+ else
+ add_keynode(arg, rawseq, seqlen, key_run_command, p);
+
+ if (echo_int) {
+ 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 __P1 (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, &params))) {
+ 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 (echo_int) {
+ 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 (echo_int)
+ 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 __P1 (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 (seqlen == p->seqlen && !memcmp(p->sequence, seq, 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 (echo_int)
+ 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 __P7 (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 __P0 (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 __P2 (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 __P0 (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 __P3 (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 (echo_int) {
+ PRINTF("#changed ");
+ show_delaynode(m, 0);
+ }
+}
+
+void new_delaynode __P3 (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 (echo_int && node) {
+ PRINTF("#new ");
+ show_delaynode(node, 0);
+ }
+}
+
+void show_history __P1 (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 __P1 (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);
+ }
+}
+