diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/beam.c | 2 | ||||
| -rw-r--r-- | src/cmd.c | 20 | ||||
| -rw-r--r-- | src/cmd2.c | 439 | ||||
| -rw-r--r-- | src/cmd2.h | 4 | ||||
| -rw-r--r-- | src/defines.h | 23 | ||||
| -rw-r--r-- | src/edit.c | 3 | ||||
| -rw-r--r-- | src/eval.c | 6 | ||||
| -rw-r--r-- | src/list.c | 76 | ||||
| -rw-r--r-- | src/list.h | 6 | ||||
| -rw-r--r-- | src/main.c | 16 | ||||
| -rw-r--r-- | src/main.h | 1 | ||||
| -rw-r--r-- | src/prove.c | 463 | ||||
| -rw-r--r-- | src/prove.h | 12 | ||||
| -rw-r--r-- | src/tty.c | 8 | ||||
| -rw-r--r-- | src/utils.c | 157 | ||||
| -rw-r--r-- | src/utils.h | 5 | 
17 files changed, 1100 insertions, 145 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index caab15c..8b79eb7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,12 +5,12 @@ AM_CPPFLAGS=-D_XOPEN_SOURCE=700 -DPOWWOW_DIR=\"$(pkgdatadir)\" \  bin_PROGRAMS = powwow powwow-muc powwow-movieplay  powwow_SOURCES = beam.c cmd.c log.c edit.c cmd2.c eval.c \  		 utils.c main.c tcp.c list.c map.c tty.c \ -		 ptr.c +		 ptr.c prove.c  powwow_LDFLAGS = @dl_ldflags@  powwowdir = $(pkgincludedir)  powwow_HEADERS = beam.h cmd.h log.h edit.h cmd2.h eval.h \  		 utils.h main.h tcp.h list.h map.h tty.h \ -		 ptr.h defines.h feature/regex.h +		 ptr.h defines.h feature/regex.h prove.h  powwow_muc_SOURCES = powwow-muc.c  powwow_movieplay_SOURCES = powwow-movieplay.c @@ -291,7 +291,7 @@ cleanup_file: if (unlink(sp->file) < 0)   */  void message_edit(char *text, int msglen, char view, char builtin)  { -    char tmpname[BUFSIZE], command_str[BUFSIZE], buf[BUFSIZE]; +    char tmpname[BUFSIZE], command_str[BUFSIZE];      char *errdesc = "#warning: protocol error (message_edit, no %s)\n";      int tmpfd, i, childpid;      unsigned int key; @@ -62,7 +62,7 @@ static void F(help), F(shell), F(action), F(add),    F(rawsend), F(rawprint), F(rebind), F(rebindall), F(rebindALL),    F(record), F(request), F(reset), F(retrace),    F(save), F(send), F(setvar), F(snoop), F(spawn), F(stop), -  F(time), F(var), F(ver), F(while), F(write), +  F(substitute), F(time), F(var), F(ver), F(while), F(write),    F(eval), F(zap), F(module), F(group), F(speedwalk), F(groupdelim);  #ifdef BUG_TELNET @@ -229,6 +229,8 @@ cmdstruct default_commands[] =        "[speedwalk sequence]\texecute a speedwalk sequence explicitly"),      C("stop",       cmd_stop,        "\t\t\t\tremove all delayed commands from active list"), +    C("substitute",       cmd_substitute, +      "[string[=[text]]]\tdelete/list/define substitutions"),      C("time",       cmd_time,        "\t\t\t\tprint current time and date"),      C("var",        cmd_var, @@ -1281,6 +1283,12 @@ static void cmd_reset(char *arg)  	if (!all)  	    return;      } +    if (all || !strcmp(arg, "substitute")) { +        while (substitutions) +	    delete_substnode(&substitutions); +	if (!all) +	    return; +    }      if (all || !strcmp(arg, "var")) {          int n;  	varnode **first; @@ -1335,6 +1343,14 @@ static void cmd_stop(char *arg)      }  } +static void cmd_substitute(char *arg) +{ +    if (!*arg) +	show_substitutions(); +    else +	parse_substitute(arg); +} +  static void cmd_time(char *arg)  {      struct tm *s; @@ -2709,5 +2725,3 @@ static void cmd_put(char *arg)  	put_history(arg);      ptrdel(pbuf);  } - - @@ -41,6 +41,41 @@ int select();  #include "tty.h"  #include "eval.h" +typedef struct parsed_pattern_definition { +    int match_from_beginning; + +    int has_pattern; +    char pattern[BUFSIZE]; + +    int pattern_has_wild; + +    const char *pattern_start_p; +    int pattern_offset; +    int pattern_length; + +    int has_value; +    char value[BUFSIZE]; + +    const char *value_start_p; +    int value_offset; +    int value_length; + +    const char *error_string; +    int error_code; +} parsed_pattern_definition; + +typedef struct parse_pattern_action_result { +    int error_code; +    char message[BUFSIZE]; +    char display_value[BUFSIZE]; +    int action_complete; +    void (*post_call_f)(); +} parse_pattern_action_result; + +typedef void parse_pattern_action(parse_pattern_action_result *result, char *pattern, int match_from_beginning); +typedef void parse_pattern_with_value_action(parse_pattern_action_result *result, char *pattern, int match_from_beginning, int pattern_has_wild, char *value); + +  /* anyone knows if ANSI 6429 talks about more than 8 colors? */  static char *colornames[] = {      "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", @@ -879,104 +914,359 @@ void show_marks(void)  	       (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)); +	tty_printf("#mark %s%s=%s\n", p->b.mbeg ? "^" : "", +		   p->b.pattern, attr_name(p->attrcode));  } - -/* - * parse arguments to the #mark command - */ -void parse_mark(char *str) +static void parse_pattern_definition(char *str, parsed_pattern_definition *res)  {      char *p; -    marknode **np, *n; -    char mbeg = 0; + +    res->match_from_beginning = 0; + +    res->pattern_has_wild = 0; +    res->has_pattern = 0; +    res->pattern[0] = 0; +    res->pattern_start_p = NULL; +    res->pattern_offset = 0; +    res->pattern_length = 0; + +    res->has_value = 0; +    res->value[0] = 0; +    res->value_start_p = NULL; +    res->value_offset = 0; +    res->value_length = 0; + +    res->error_code = 0; +    res->error_string = NULL;      if (*str == '=') { -	PRINTF("#marker must be non-null.\n"); -	return; +        res->error_string = "must be non-empty"; +        return; +    } + +    res->has_pattern = 1; +    if (*str ==  '^') { +        res->match_from_beginning = 1; +        res->pattern_offset++;      } + +    str += res->pattern_offset; +    res->pattern_start_p = str; +      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); +        /* no = so just a pattern no value */ +        res->pattern_length = strlen(str); +    } else { +        res->pattern_length = p - str; +    } + +    my_strncpy(res->pattern, str, res->pattern_length); +    unescape(res->pattern); + +    if (1) { +        char *p2; +        p2 = res->pattern; +        while (*p2) { +            if (ISMARKWILDCARD(*p2)) { +                res->pattern_has_wild = 1; +                if (ISMARKWILDCARD(*(p2 + 1))) { +                    res->error_code = SYNTAX_ERROR; +                    res->error_string = "two wildcards (& or $) may not be next to eachother"; +                    //PRINTF("#error: two wildcards (& or $) may not be next to eachother\n"); +                    return; +                } +            } +            p2++;          } +    } -    } else { -        int attrcode, wild = 0; -	char pattern[BUFSIZE], *p2; +    // no value to parse +    if (!*p) { +        return; +    } -        *(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++; -	} +    // skip the equals +    p++; -        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; +    // this was only used in the #mark syntax, is it needed? +    int clear_leading_value_whitespace = 0; +    if (clear_leading_value_whitespace) { +        p = skipspace(p); +    } + +    res->has_value = 1; +    res->value_start_p = p; +    res->value_offset = p - str; +    res->value_length = strlen(p); + +    my_strncpy(res->value, p, BUFSIZE-1); +} + +void _dump_parsed(parsed_pattern_definition *res) +{ +    PRINTF("res->match_from_beginning = %d\n", res->match_from_beginning); + +    PRINTF("res->pattern_has_wild = %d\n", res->pattern_has_wild); +    PRINTF("res->has_pattern = %d\n", res->has_pattern); +    PRINTF("res->pattern = %s\n", res->pattern); +    PRINTF("res->pattern_start_p = %d\n", res->pattern_start_p); +    PRINTF("res->pattern_offset = %d\n", res->pattern_offset); +    PRINTF("res->pattern_length = %d\n", res->pattern_length); + +    PRINTF("res->has_value = %d\n", res->has_value); +    PRINTF("res->value = %s\n", res->value); +    PRINTF("res->value_start_p = %d\n", res->value_start_p); +    PRINTF("res->value_offset = %d\n", res->value_offset); +    PRINTF("res->value_length = %d\n", res->value_length); + +    PRINTF("res->error_code = %d\n", res->error_code); +    PRINTF("res->error_string = %d\n", res->error_string); +} + +void parse_pattern_definition_and_process_action(char *str, +    char *command, char *command_noun, int detail_magic_number, +    parse_pattern_action *do_delete, +    parse_pattern_with_value_action *do_update_or_insert, +    parse_pattern_action *do_show) +{ +    ptr pbuf = (ptr)0; +    char *detail_display; + +    parse_pattern_action_result action_result; +    parsed_pattern_definition parsed; + +    parse_pattern_definition(str, &parsed); +    //_dump_parsed(&parsed); + +    if (parsed.error_string) { +        error = parsed.error_code; +        PRINTF("%s %s\n", command, parsed.error_string); +        return; +    } + +    action_result.action_complete = 0; +    action_result.post_call_f = NULL; +    strcpy(action_result.display_value, ""); +    strcpy(action_result.message, ""); + +    if (parsed.has_value) { +        if (! parsed.value[0]) { +            (*do_delete)(&action_result, parsed.pattern, parsed.match_from_beginning); + +            if (action_result.action_complete) {                  if (opt_info) { -                    PRINTF("#changed"); +                    PRINTF("#deleting %s: %s%s=%s\n", +                        command_noun, +                        parsed.match_from_beginning ? "^" : "", +                        parsed.pattern, +                        action_result.display_value);                  }              } else { -                add_marknode(pattern, attrcode, mbeg, wild); +                PRINTF("#unknown %s, cannot delete: \"%s%s\"\n", +                    command_noun, +                    parsed.match_from_beginning ? "^" : "", +                    parsed.pattern); +            } +        } else { +            (*do_update_or_insert)(&action_result, parsed.pattern, parsed.match_from_beginning, parsed.pattern_has_wild, parsed.value); + +            if (action_result.action_complete) {                  if (opt_info) { -                    PRINTF("#new"); +                    if (action_result.action_complete == 1) { +                        PRINTF("#changed"); +                    } else if (action_result.action_complete == 2) { +                        PRINTF("#new"); +                    } + +                    tty_printf(" %s: %s%s=%s\n", +                        command_noun, +                        parsed.match_from_beginning ? "^" : "", +                        parsed.pattern, +                        action_result.display_value);                  } +            } else { +                PRINTF("%s\n", action_result.message); +                if (action_result.post_call_f != NULL) { +                    (*action_result.post_call_f)(); +                } +                return;              } -            if (opt_info) -		tty_printf(" mark: %s%s=%s\n", mbeg ? "^" : "", -			   pattern, attr_name(attrcode));          } +    } else { +        (*do_show)(&action_result, parsed.pattern, parsed.match_from_beginning); +        if (action_result.action_complete) { +            detail_display = action_result.display_value; + +            pbuf = ptrmescape(pbuf, parsed.pattern, strlen(parsed.pattern), 0); + +            if (MEM_ERROR) { ptrdel(pbuf); return; } + +            sprintf(inserted_next, "%s %s%.*s=%.*s", +                command, +                parsed.match_from_beginning ? "^" : "", +                BUFSIZE - (int)strlen(detail_display) - detail_magic_number, +                pbuf ? ptrdata(pbuf) : "", +                BUFSIZE - detail_magic_number, detail_display); + +            ptrdel(pbuf); +        } else { +            PRINTF("#unknown %s, cannot show: \"%s\"\n", +                command_noun, +                parsed.pattern); +        } +    } +} + +marknode **parse_mark__action_with_marknode(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    marknode **np, *n = NULL; +    np = lookup_marker(pattern, match_from_beginning); +    result->action_complete = 0; + +    if (n = *np) { +        strcpy(result->display_value, attr_name(n->attrcode)); +    } + +    return np; +} + +void parse_mark__action_delete(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    marknode *n, **np = parse_mark__action_with_marknode(result, pattern, match_from_beginning); + +    if (n = *np) { +        result->action_complete = 1; +        delete_marknode(np); +    } +} + +void parse_mark__action_update_or_insert(parse_pattern_action_result *result, char *pattern, int match_from_beginning, int pattern_has_wild, char *value) +{ +    marknode *n, **np = parse_mark__action_with_marknode(result, pattern, match_from_beginning); +    int attrcode = parse_attributes(value); + +    if (attrcode == -1) { +        strcpy(result->message, "#invalid attribute syntax."); +        error = SYNTAX_ERROR; +        result->post_call_f = &show_attr_syntax; +        return; +    } + +    if (n = *np) { +        result->action_complete = 1; +        n->attrcode = attrcode; +    } else { +        result->action_complete = 2; +        add_marknode(pattern, attrcode, match_from_beginning, pattern_has_wild); +    } + +    strcpy(result->display_value, attr_name(attrcode)); +} + +void parse_mark__action_show(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    marknode *n, **np = parse_mark__action_with_marknode(result, pattern, match_from_beginning); + +    if (n = *np) { +        result->action_complete = 1;      }  }  /* + * parse arguments to the #mark command + */ +void parse_mark(char *str) +{ +    parse_pattern_definition_and_process_action(str, +        "#mark", "mark", 9, +        &parse_mark__action_delete, +        &parse_mark__action_update_or_insert, +        &parse_mark__action_show); +} + +/* + * show defined substitutions + */ +void show_substitutions(void) +{ +    substnode *p; +    PRINTF("#%s substitution%s defined%c\n", substitutions ? "the following" : "no", +	       (substitutions && !substitutions->next) ? " is" : "s are", +	       substitutions ? ':' : '.'); +    for (p = substitutions; p; p = p->next) +	tty_printf("#substitute %s%s=%s\n", p->b.mbeg ? "^" : "", +		   p->b.pattern, p->replacement); +} + +substnode **parse_substitute__action_with_substnode(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    substnode **np, *n = NULL; +    np = lookup_subst(pattern, match_from_beginning); +    result->action_complete = 0; + +    if (n = *np) { +        strcpy(result->display_value, n->replacement); +    } + +    return np; +} + +void parse_substitute__action_delete(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    substnode *n, **np = parse_substitute__action_with_substnode(result, pattern, match_from_beginning); + +    if (n = *np) { +        result->action_complete = 1; +        delete_substnode(np); +    } +} + +void parse_substitute__action_update_or_insert(parse_pattern_action_result *result, char *pattern, int match_from_beginning, int pattern_has_wild, char *value) +{ +    char *replacement = value; +    substnode *n, **np = parse_substitute__action_with_substnode(result, pattern, match_from_beginning); + +    if (n = *np) { +        if (!(replacement = my_strdup(replacement))) { +            strcpy(result->message, "Failed to strdup"); +            return; +        } +        if (n->replacement) { free(n->replacement); } +        n->replacement = replacement; +        result->action_complete = 1; +    } else { +        result->action_complete = 2; +        add_substnode(pattern, replacement, match_from_beginning, pattern_has_wild); +    } + +    strcpy(result->display_value, replacement); +} + +void parse_substitute__action_show(parse_pattern_action_result *result, char *pattern, int match_from_beginning) +{ +    substnode *n, **np = parse_substitute__action_with_substnode(result, pattern, match_from_beginning); + +    if (n = *np) { +        result->action_complete = 1; +    } +} + +/* + * parse arguments to the #substitute command + */ +void parse_substitute(char *str) +{ +    parse_pattern_definition_and_process_action(str, +        "#substitute", "substitution", 14, +        &parse_substitute__action_delete, +        &parse_substitute__action_update_or_insert, +        &parse_substitute__action_show); +} + +/*   * turn ASCII description of a sequence   * into raw escape sequence   * return pointer to end of ASCII description @@ -1673,4 +1963,3 @@ void exe_history(int count)  	parse_user_input(buf, 0);      }  } - @@ -14,6 +14,9 @@ char *attr_name(int attrcode);  void show_marks(void);  void parse_mark(char *str); +void show_substitutions(void); +void parse_substitute(char *str); +  char *seq_name(char *seq, int len);  void show_binds(char edit);  void parse_bind(char *arg); @@ -29,4 +32,3 @@ void new_delaynode(char *name, char *command, long millisec);  void show_history(int count);  void exe_history(int count); - diff --git a/src/defines.h b/src/defines.h index 194b933..52d50fe 100644 --- a/src/defines.h +++ b/src/defines.h @@ -75,8 +75,8 @@  #define PARAMLEN	99	/* initial length of text strings */  #define MAX_MAPLEN	1000	/* maximum length of automapped path */  #define MIN_WORDLEN	3	/* the minimum length for history words */ -#define MAX_WORDS	512	/* number of words kept for TAB-completion */ -#define MAX_HIST	128	/* number of history lines kept */ +#define MAX_WORDS	4096	/* number of words kept for TAB-completion */ +#define MAX_HIST	2048	/* number of history lines kept */  #define LOG_MAX_HASH	7  #define MAX_HASH	(1<<LOG_MAX_HASH) /* max hash value, must be a power of 2 */  #define NUMPARAM	10	/* number of local unnamed params allowed @@ -215,15 +215,25 @@ typedef struct aliasnode {      int active;  } aliasnode; +typedef struct basenode { +    char *pattern; +    char *start, *end; // temporary data: start/end of current line match +    char mbeg; // match from beginning of string +    char wild; +} basenode; +  typedef struct marknode {      struct marknode *next; -    char *pattern; +    basenode b;      int attrcode; -    char *start, *end; -    char mbeg; -    char wild;  } marknode; +typedef struct substnode { +    struct substnode *next; +    basenode b; +    char *replacement; +} substnode; +  typedef struct triggernode {      struct triggernode *next;      char *command, *label; @@ -303,4 +313,3 @@ typedef struct editsess {  } editsess;  #endif /* _DEFINES_H_ */ - @@ -133,7 +133,7 @@ void draw_prompt(void)          char *esc, *pstr;  	int e = error;  	error = 0; -	marked_prompt = ptraddmarks(marked_prompt, prompt->str); +	marked_prompt = ptraddsubst_and_marks(marked_prompt, prompt->str);  	if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return; }          /* if prompt ends in unterminated escape code, do not print @@ -958,4 +958,3 @@ void edit_bootstrap(void)  {      default_completions();  } - @@ -789,8 +789,9 @@ static int exe_op(operator *op)  	    /* src == empty string. just return it */  	    check_delete(&o2);  	    o1.txt = src; -	    if (!REAL_ERROR) +	    if (!REAL_ERROR) {  		p=&o1; ret=1; +            }  	    break;  	} @@ -915,8 +916,9 @@ static int exe_op(operator *op)  	}  	check_delete(&o2);  	o1.txt = dst; -	if (!REAL_ERROR) +	if (!REAL_ERROR) {  	    p=&o1; ret=1; +        }  	break;        case (int)colon_question:        case (int)point_question: @@ -23,6 +23,7 @@  #include "feature/regex.h"  #include "utils.h"  #include "cmd2.h" +#include "list.h"  #include "tty.h"  #include "eval.h" @@ -206,12 +207,13 @@ void add_marknode(char *pattern, int attrcode, char mbeg, char wild)  	errmsg("malloc");  	return;      } -    new->pattern = my_strdup(pattern); +    new->next = NULL; +    new->b.pattern = my_strdup(pattern); +    new->b.start = new->b.end = NULL; +    new->b.mbeg = mbeg; +    new->b.wild = wild;      new->attrcode = attrcode; -    new->start = new->end = NULL; -    new->mbeg = mbeg; -    new->wild = wild; -    if (!new->pattern) { +    if (!new->b.pattern) {  	errmsg("malloc");  	free(new);  	return; @@ -226,6 +228,39 @@ void add_marknode(char *pattern, int attrcode, char mbeg, char wild)  #endif  } + +/* + * add a node to the substitution list + */ +void add_substnode(char *pattern, char *replacement, char mbeg, char wild) +{ +    substnode **p, *new = (substnode*)malloc(sizeof(substnode)); +    int i; +    if (!new) { +        errmsg("malloc"); +        return; +    } +    new->next = NULL; +    new->b.pattern = my_strdup(pattern); +    new->b.start = new->b.end = NULL; +    new->b.mbeg = mbeg; +    new->b.wild = wild; +    new->replacement = my_strdup(replacement); +    if (!new->b.pattern || (replacement && !new->replacement)) { +        errmsg("malloc"); +        delete_substnode(&new); +        return; +    } +#ifdef DO_SORT +    add_node((defnode*)new, (defnode**)&substitutions, ascii_sort); +#else +    for (p=&substitutions, i=1; *p && (a_nice==0 || i<a_nice); p = &(*p)->next, i++) +        ; +    new->next = *p; +    *p = new; +#endif +} +  /*   * add a node to the action list   */ @@ -487,13 +522,25 @@ actionnode **lookup_prompt(char *label)  }  /* - * look up an marker node by pattern: + * look up a marker node by pattern:   * return pointer to pointer to node or a pointer to NULL if nothing found   */  marknode **lookup_marker(char *pattern, char mbeg)  {      marknode **p = &markers; -    while (*p && (mbeg != (*p)->mbeg || strcmp(pattern, (*p)->pattern))) +    while (*p && (mbeg != (*p)->b.mbeg || strcmp(pattern, (*p)->b.pattern))) +	p = &(*p)->next; +    return p; +} + +/* + * look up a substitution node by pattern: + * return pointer to pointer to node or a pointer to NULL if nothing found + */ +substnode **lookup_subst(char *pattern, char mbeg) +{ +    substnode **p = &substitutions; +    while (*p && (mbeg != (*p)->b.mbeg || strcmp(pattern, (*p)->b.pattern)))  	p = &(*p)->next;      return p;  } @@ -505,7 +552,6 @@ marknode **lookup_marker(char *pattern, char mbeg)  keynode **lookup_key(char *name)  {      keynode **p = &keydefs; -      while (*p && strcmp(name, (*p)->name))  	p = &(*p)->next;      return p; @@ -596,7 +642,19 @@ void delete_promptnode(promptnode **base)  void delete_marknode(marknode **base)  {      marknode *p = *base; -    if (p->pattern) free(p->pattern); +    if (p->b.pattern) free(p->b.pattern); +    *base = p->next; +    free((void*)p); +} + +/* + * delete a substnode, given a pointer to its precessor's pointer + */ +void delete_substnode(substnode **base) +{ +    substnode *p = *base; +    if (p->b.pattern) free(p->b.pattern); +    if (p->replacement) free(p->replacement);      *base = p->next;      free((void*)p);  } @@ -13,7 +13,7 @@ int rev_ascii_sort(defnode *node1, defnode *node2);  int time_sort(defnode *node1, defnode *node2);  int rev_time_sort(defnode *node1, defnode *node2); -int hash(char *name); +int hash(char *name, int optlen);  void add_node(defnode *newnode, defnode **base, function_sort sort);  void reverse_sortedlist(sortednode **base); @@ -23,6 +23,7 @@ void add_actionnode(char *pattern, char *command, char *label, int active, int t  void add_promptnode(char *pattern, char *command, char *label, int active, int type, void *qregexp);  void add_marknode(char *pattern, int attrcode, char mbeg, char wild);  void add_keynode(char *name, char *sequence, int seqlen, function_str funct, char *call_data); +void add_substnode(char *pattern, char *replacement, char mbeg, char wild);  delaynode *add_delaynode(char *name, char *command, vtime *when, int is_dead);  varnode *add_varnode(char *name, int type); @@ -31,6 +32,7 @@ actionnode **lookup_action(char *label);  actionnode **lookup_prompt(char *label);  actionnode **lookup_action_pattern(char *pattern);  marknode   **lookup_marker(char *pattern, char mbeg); +substnode  **lookup_subst(char *pattern, char mbeg);  keynode    **lookup_key(char *name);  delaynode  **lookup_delay(char *name, int is_dead);  varnode    **lookup_varnode(char *name, int type); @@ -41,7 +43,7 @@ void delete_promptnode(promptnode **base);  void delete_marknode(marknode **base);  void delete_keynode(keynode **base);  void delete_delaynode(delaynode **base); +void delete_substnode(substnode **base);  void delete_varnode(varnode **base, int type);  #endif /* _LIST_H_ */ - @@ -13,7 +13,7 @@   *   * Initially inspired to the Tintin client by Peter Unold,   * Powwow contains no Tintin code. - * The original program Cancan, written by Mattias Engdegård (Yorick) + * The original program Cancan, written by Mattias Engdeg�rd (Yorick)   * (f91-men@nada.kth.se) 1992-94,   * was greatly improved upon by Vivriel, Thuzzle and Ilie and then   * transformed from Cancan into Powwow by Cosmos who worked @@ -89,6 +89,7 @@ extern int select();  #include "tty.h"  #include "eval.h"  #include "log.h" +#include "prove.h"  /*     local function declarations       */  #ifdef MOTDFILE @@ -158,7 +159,8 @@ aliasnode *sortedaliases;          /* head of (ASCII) sorted alias list */  actionnode *actions;               /* head of action list */  promptnode *prompts;               /* head of prompt list */  marknode *markers;                 /* head of mark list */ -int a_nice = 0;                    /* default priority of new actions/marks */ +substnode *substitutions;          /* head of substitution list */ +int a_nice = 0;                    /* default priority of new actions/marks/substitutions */  keynode *keydefs;                  /* head of key binding list */  delaynode *delays;                 /* head of delayed commands list */  delaynode *dead_delays;            /* head of dead-delayed commands list */ @@ -344,6 +346,11 @@ under certain conditions; type \"#help copyright\" for details.\n"  		   );      } +    if (argc == 2 && strcmp(argv[1], "--prove") == 0) { +        prove(); +        exit(0); +    } +      if (argc == 1 || argc == 3) {  	tty_add_initial_binds();  	tty_add_walk_binds(); @@ -1394,11 +1401,8 @@ static void get_user_input(void)  	    edbuf[edlen] = '\0';  #ifdef BUG_ANSI  	    if (edattrbg) -		tty_printf("%s\n", edattrend); -	    else +		tty_puts(edattrend);  #endif -		tty_putc('\n'); -  	    tcp_write(tcp_fd, edbuf);  	    edlen = 0;  	    typed[nchars = 0] = 0; @@ -63,6 +63,7 @@ extern aliasnode *sortedaliases;  extern actionnode *actions;  extern promptnode *prompts;  extern marknode *markers; +extern substnode *substitutions;  extern int a_nice;  extern keynode *keydefs;  extern delaynode *delays; diff --git a/src/prove.c b/src/prove.c new file mode 100644 index 0000000..497ebda --- /dev/null +++ b/src/prove.c @@ -0,0 +1,463 @@ +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +#include "defines.h" +#include "main.h" +#include "utils.h" +#include "eval.h" +#include "list.h" +#include "edit.h" + +#ifdef ENABLE_PROVE + +/* + * Run powwow --prove to get TAP output + * + * To use with prove, create a script powwow.t that contains powwow --prove + * + * Then run: + * + *   prove powwow.t + * + * Or without a wrapper .t: + * + *   prove --exec '' ./src/powwow :: --prove + */ + +int _is_proving = 0; +int num_tests = 0; + +#define PROVE_TTY_OUTPUT_LENGTH 40000 +char prove_tty_output[PROVE_TTY_OUTPUT_LENGTH]; + +#define TAP_STRING_LENGTH 400 +char tap_string[TAP_STRING_LENGTH]; + +void prove_save_tty_output(const char *s) +{ +    strncat(prove_tty_output, s, PROVE_TTY_OUTPUT_LENGTH - strlen(prove_tty_output) - 1); +} + +int is_proving() +{ +    return _is_proving; +} + +static void t_clear_tty_output() +{ +    strcpy(prove_tty_output, ""); +} + +static const char *t_get_tty_output() +{ +    return prove_tty_output; +} + +static void tap_putchar(const char c) +{ +    putchar(c); +} + +static void tap_printf(const char *format, ...) +{ +    va_list va_args; +    va_start(va_args, format); +    vprintf(format, va_args); +    va_end(va_args); +} + +static void tap_ok(const char *description) +{ +    tap_printf("ok %d - %s\n", ++num_tests, description); +} + +static void tap_not_ok(const char *description) +{ +    tap_printf("not ok %d - %s\n", ++num_tests, description); +} + +static void done_testing() { +    tap_printf("1..%d\n", num_tests); +} + +char *tap_stringf(const char *format, ...) +{ +    va_list va_args; +    va_start(va_args, format); +    vsnprintf(tap_string, TAP_STRING_LENGTH, format, va_args); +    va_end(va_args); +    return tap_string; +} + +static void todo(const char *description) +{ +    tap_not_ok(tap_stringf("# TODO %s", description)); +} + +static void note(const char *description) +{ +    int offset = 0; +    char last_c = '\n'; +    while (description[offset] != 0) { +        if (last_c == '\n') { +            tap_printf("# "); +        } + +        last_c = description[offset++]; +        tap_putchar(last_c); +    } +    tap_putchar('\n'); +} + +static void note_tty_output() +{ +    note(tap_stringf("error = %d", error)); +    note("--- begin tty output ---"); +    note(t_get_tty_output()); +    note("---  end tty output ---"); +} + +static void t_reset_errors() +{ +    error = 0; +} + +static void t_simulate_user_input(const char *original_line) +{ +    char line[BUFSIZE]; + +    // send in a copy since some function will modify input +    strcpy(line, original_line); + +    note(tap_stringf("Simulating input: %s", line)); + +    clear_line(0); +    t_reset_errors(); +    edlen = strlen(line); +    strcpy(edbuf, line); +    parse_user_input(line, 1); +} + +static void t_simulate_output(const char *original_line) +{ +    char line[BUFSIZE]; + +    // send in a copy since some function will modify input +    strcpy(line, original_line); + +    note(tap_stringf("Simulating output: %s", line)); + +    t_reset_errors(); +    smart_print(line, 1); +} + + +static void C_ok(int arg, const char *description) +{ +    if (arg) { +        tap_ok(description); +    } else { +        tap_not_ok(description); +    } +} + +static void C_is(const char *input, const char *match, const char *description) +{ +    if (strcmp(input, match) == 0) { +        tap_ok(description); +    } else { +        tap_not_ok(description); +        note(tap_stringf( +                "C_is FAILED\n" +                "      got: '%s'\n" +                " expected: '%s'\n", +            input, match)); +    } +} + +static void C_contains(const char *input, const char *match, const char *description) +{ +    if (strstr(input, match)) { +        tap_ok(description); +    } else { +        tap_not_ok(description); +        note(tap_stringf( +                "C_contains FAILED\n" +                "      got: '%s'\n" +                " expected: '%s'\n", +            input, match)); +    } +} + +static void C_output_from_user_input_contains(char *user_input, const char *output_contains, const char *description) +{ +    t_clear_tty_output(); +    t_simulate_user_input(user_input); +    C_contains(t_get_tty_output(), output_contains, description); +    note_tty_output(); +} + +static void C_output_from_user_input_is(char *user_input, const char *output_is, const char *description) +{ +    t_clear_tty_output(); +    t_simulate_user_input(user_input); +    C_is(t_get_tty_output(), output_is, description); +    note_tty_output(); +} + +static void C_simulated_output_contains(char *original_output, const char *output_contains, const char *description) +{ +    t_clear_tty_output(); +    t_simulate_output(original_output); +    C_contains(t_get_tty_output(), output_contains, description); +    note_tty_output(); +} + +static void C_simulated_output_is(char *original_output, const char *output_is, const char *description) +{ +    t_clear_tty_output(); +    t_simulate_output(original_output); +    C_is(t_get_tty_output(), output_is, description); +    note_tty_output(); +} + +static void C_edit_from_user_input_is(char *user_input, const char *output_is, const char *description) +{ +    t_clear_tty_output(); +    strcpy(inserted_next, ""); +    t_simulate_user_input(user_input); +    C_is(inserted_next, output_is, description); +    note_tty_output(); +} + +static void t_start_new_test() +{ +    /* this should init the globals again so everything is clean */ +    t_reset_errors(); + +    t_simulate_user_input("#reset all"); + +    t_clear_tty_output(); +} + +static void do_test(void (*func)(), const char *name) +{ +    note(tap_stringf("-=-=-=-=- Setting up for test %s -=-=-=-=-\n", name)); +    t_start_new_test(); +    (*func)(); +    note(tap_stringf("-=-=-=-=- End test %s -=-=-=-=-\n", name)); +} + +static void TEST_tap() +{ +    C_ok(1, "Should ok"); +    return; +    C_ok(0, "Should not ok"); +} + +static void TEST_mark_syntax_error() +{ +    C_output_from_user_input_contains( +        "#mark foo=bar", +        "#invalid attribute syntax", +        "#mark - syntax error"); + +    C_output_from_user_input_is( +        "#mark =bar", +        "#mark must be non-empty\n", +        "#mark - empty error"); + +    C_output_from_user_input_is( +        "#mark foo&$foo=bar", +        "#mark two wildcards (& or $) may not be next to eachother\n", +        "#mark - two consecutive wildcards"); +} + +static void TEST_mark_reset_step1() +{ +    C_output_from_user_input_is( +        "#mark foo=bold", +        "#new mark: foo=bold \n", +        "#mark - new mark is created"); + +    C_simulated_output_is( +        "The word foo should be bold", +        "The word \x1b[1mfoo\x1b[0m should be bold", +        "#mark - foo should be bold"); + +    C_output_from_user_input_is( +        "#mark", + +        "#the following marker is defined:\n" +        "#mark foo=bold \n", + +        "#mark - can list marks"); +} + +static void TEST_mark_reset_step2() +{ +    C_output_from_user_input_is( +        "#mark", +        "#no markers are defined.\n", +        "#mark - there should be no marks defined after a reset"); + +    C_simulated_output_is( +        "The word foo should not be bold", +        "The word foo should not be bold", +        "#mark - foo should be not be bold because of test reset"); +} + + +static void TEST_mark() +{ +    C_output_from_user_input_is( +        "#mark match rest &=underline", +        "#new mark: match rest &=underline \n", +        "#mark - new mark is created with pattern"); + +    C_output_from_user_input_is( +        "#mark ^bar=underline", +        "#new mark: ^bar=underline \n", +        "#mark - new mark is created with anchor"); + +    C_simulated_output_is( +        "bar - The word bar should be underlined at the beginning", +        "\x1b[4mbar\x1b[0m - The word bar should be underlined at the beginning", +        "#mark - ^bar should be underlined"); + + +    todo("The below test should fail when mbeg is fixed!"); + +    // need to see if this is intended, mbeg suggests it should only match +    // at beginning of the line +    C_simulated_output_is( +        "The word bar should be not be underlined if not at the beginning (mbeg - THIS IS BROKEN?)", + +        //"The word bar should be not be underlined if not at the beginning", +        "The word \x1b[4mbar\x1b[0m should be not be underlined if not at the beginning (mbeg - THIS IS BROKEN?)", + +        "#mark - other bar should NOT be underlined"); + +    todo("The above test should fail when mbeg is fixed!"); + + +    C_output_from_user_input_is( +        "#mark foo=bold", +        "#new mark: foo=bold \n", +        "#mark - new mark is created"); + +    C_edit_from_user_input_is( +        "#mark foo", +        "#mark foo=bold ", +        "#mark - mark can be edited"); + +    C_simulated_output_is( +        "The word foo should be bold", +        "The word \x1b[1mfoo\x1b[0m should be bold", +        "#mark - foo should be bold"); + +    C_simulated_output_is( +        "After match rest all should be underlined and foo should not be bold", +        "After \x1b[4mmatch rest all should be underlined and foo should not be bold\x1b[0m", +        "#mark - match rest should be underlined but foo is not bold, matches seem to be greedy"); + +    C_simulated_output_is( +        "The word foo and foo should be bold", +        "The word \x1b[1mfoo\x1b[0m and \x1b[1mfoo\x1b[0m should be bold", +        "#mark - multiple foo should be bold"); + + +    C_output_from_user_input_is( +        "#mark foo=", +        "#deleting mark: foo=bold \n", +        "#mark - mark is deleted"); + +    C_output_from_user_input_is( +        "#mark ^bar=", +        "#deleting mark: ^bar=underline \n", +        "#mark - mark is deleted"); + +    C_output_from_user_input_is( +        "#mark match rest &=", +        "#deleting mark: match rest &=underline \n", +        "#mark - mark is deleted"); + +    C_simulated_output_is( +        "The word foo should not be bold", +        "The word foo should not be bold", +        "#mark - foo should not be bold"); + +    C_output_from_user_input_is( +        "#mark", +        "#no markers are defined.\n", +        "#mark - all marks deleted"); +} + +static void TEST_substitute_syntax_error() +{ +    C_output_from_user_input_is( +        "#substitute = ", +        "#substitute must be non-empty\n", +        "#substitute - syntax error"); +} + +static void TEST_substitute() +{ +    C_output_from_user_input_contains( +        "#substitute foo=bar", +        "#new substitution: foo=bar\n", +        "#substitute - new substitute is created"); + +    C_edit_from_user_input_is( +        "#substitute foo", +        "#substitute foo=bar", +        "#substitute - substitute can be edited"); + +    C_simulated_output_is( +        "The word foo should be bar", +        "The word bar should be bar", +        "#substitute - foo should be bar"); + +    C_output_from_user_input_is( +        "#substitute foo=", +        "#deleting substitution: foo=bar\n", +        "#substitute - substitute is deleted"); + +    C_simulated_output_is( +        "The word foo should not be bar", +        "The word foo should not be bar", +        "#substitute - foo should not be bar"); +} + +void prove() +{ +    _is_proving = 1; + +    int looping = 2; +    while (looping--) { +        do_test(TEST_tap, "TAP output"); + +        do_test(TEST_mark_syntax_error, "#mark syntax"); +        do_test(TEST_mark_reset_step1, "#mark reset step 1"); +        do_test(TEST_mark_reset_step2, "#mark reset step 2"); +        do_test(TEST_mark, "#mark usage"); + +        do_test(TEST_substitute_syntax_error, "#substitute syntax error"); +        do_test(TEST_substitute, "#substitute usage"); +    } + +    done_testing(); + +    _is_proving = 0; +} + +#else +void prove() +{ +    printf("Prove is disabled\n"); +} + +#endif + + diff --git a/src/prove.h b/src/prove.h new file mode 100644 index 0000000..36e3c41 --- /dev/null +++ b/src/prove.h @@ -0,0 +1,12 @@ +#ifndef _PROVE_H_ +#define _PROVE_H_ + +void prove(void); + +#ifdef ENABLE_PROVE +void prove_save_tty_output(const char *s); +int is_proving(); +#endif + +#endif /* _PROVE_H_ */ + @@ -34,6 +34,7 @@  #include "list.h"  #include "tty.h"  #include "tcp.h" +#include "prove.h"  #ifndef USE_SGTTY  #  ifdef APOLLO @@ -815,6 +816,13 @@ void input_insert_follow_chars(char *str, int n)  void tty_puts(const char *s)  { +#ifdef ENABLE_PROVE +    if (is_proving()) { +        prove_save_tty_output(s); +        return; +    } +#endif +      while (*s)          tty_putc(*s++);  } diff --git a/src/utils.c b/src/utils.c index f6582ef..0c3be5d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -334,24 +334,24 @@ void escape_specials(char *dst, char *src)   * if 1, start and end contain the match bounds   * if 0, start and end are undefined on return   */ -static int match_mark(marknode *mp, char *src) +static int match_mark_or_subst(basenode *bp, char *src)  { -    char *pat = mp->pattern; +    char *pat = bp->pattern;      char *npat=0, *npat2=0, *nsrc=0, *prm=0, *endprm=0, *tmp, c;      static char mpat[BUFSIZE]; -    int mbeg = mp->mbeg, mword = 0, p; +    int mbeg = bp->mbeg, mword = 0, p;      /* shortcut for #marks without wildcards */ -    if (!mp->wild) { +    if (!bp->wild) {  	if ((nsrc = strstr(src, pat))) { -	    mp->start = nsrc; -	    mp->end = nsrc + strlen(pat); +	    bp->start = nsrc; +	    bp->end = nsrc + strlen(pat);  	    return 1;  	}  	return 0;      } -    mp->start = NULL; +    bp->start = NULL;      if (ISMARKWILDCARD(*pat))  	mbeg = - mbeg - 1;  /* pattern starts with '&' or '$' */ @@ -363,8 +363,8 @@ static int match_mark(marknode *mp, char *src)  	    prm = src;  	    if (c == '$')  		mword = 1; -	    else if (!mp->start) -		mp->start = src; +	    else if (!bp->start) +		bp->start = src;  	    ++pat;  	} @@ -389,17 +389,17 @@ static int match_mark(marknode *mp, char *src)  		mbeg = 0;  /* reset mbeg to stop further start match */  	    }  	    endprm = nsrc; -	    if (!mp->start) { +	    if (!bp->start) {  		if (prm) -		    mp->start = src; +		    bp->start = src;  		else -		    mp->start = nsrc; +		    bp->start = nsrc;  	    } -	    mp->end = nsrc + strlen(mpat); +	    bp->end = nsrc + strlen(mpat);  	} else if (prm)           /* end of pattern space */ -	    mp->end = endprm = prm + strlen(prm); +	    bp->end = endprm = prm + strlen(prm);  	else -	    mp->end = src; +	    bp->end = src;  	/* post-processing of param */ @@ -417,11 +417,11 @@ static int match_mark(marknode *mp, char *src)  			else  			    p = -1;  		    } -		    mp->start = prm += p + 1; +		    bp->start = prm += p + 1;  		} else if (!*pat) {  		    /* '$' at end of pattern, take first word */  		    if ((tmp = memchrs(prm, strlen(prm), DELIM, DELIM_LEN))) -			mp->end = endprm = tmp; +			bp->end = endprm = tmp;  		} else {  			/* match only if param is single-worded */  		    if (memchrs(prm, endprm - prm, DELIM, DELIM_LEN)) @@ -441,7 +441,7 @@ static int match_mark(marknode *mp, char *src)  /*   * add marks to line. write in dst.   */ -ptr ptrmaddmarks(ptr dst, char *line, int len) +static ptr ptrmaddmarks(ptr dst, char *line, int len)  {      marknode *mp, *mfirst;      char begin[CAPLEN], end[CAPLEN], *lineend = line + len; @@ -453,19 +453,19 @@ ptr ptrmaddmarks(ptr dst, char *line, int len)  	return dst;      for (mp = markers; mp; mp = mp->next) -	mp->start = NULL; +	mp->b.start = NULL;      do {  	mfirst = NULL;  	for (mp = markers; mp; mp = mp->next) { -	    if (mp->start && mp->start >= line) +	    if (mp->b.start && mp->b.start >= line)  		matched = 1;  	    else { -		if (!(matched = (!mp->mbeg || start) && match_mark(mp, line))) -		    mp->start = lineend; +		if (!(matched = (!mp->b.mbeg || start) && match_mark_or_subst(&mp->b, line))) +		    mp->b.start = lineend;  	    } -	    if (matched && mp->start < lineend && -		(!mfirst || mp->start < mfirst->start)) +	    if (matched && mp->b.start < lineend && +		(!mfirst || mp->b.start < mfirst->b.start))  		mfirst = mp;  	} @@ -473,7 +473,7 @@ ptr ptrmaddmarks(ptr dst, char *line, int len)  	    start = 0;  	    attr_string(mfirst->attrcode, begin, end); -	    dst = ptrmcat(dst, line, matchlen = mfirst->start - line); +	    dst = ptrmcat(dst, line, matchlen = mfirst->b.start - line);  	    if (MEM_ERROR) break;  	    line += matchlen;  	    len -= matchlen; @@ -481,7 +481,7 @@ ptr ptrmaddmarks(ptr dst, char *line, int len)  	    dst = ptrmcat(dst, begin, strlen(begin));  	    if (MEM_ERROR) break; -	    dst = ptrmcat(dst, line, matchlen = mfirst->end - mfirst->start); +	    dst = ptrmcat(dst, line, matchlen = mfirst->b.end - mfirst->b.start);  	    if (MEM_ERROR) break;  	    line += matchlen;  	    len -= matchlen; @@ -497,10 +497,93 @@ ptr ptrmaddmarks(ptr dst, char *line, int len)      return dst;  } -ptr ptraddmarks(ptr dst, ptr line) +/* + * add substitutions to line. write in dst. + */ +static ptr ptrmaddsubst(ptr dst, char *line, int len) +{ +    substnode *sp, *sfirst; +    char *lineend = line + len; +    int start = 1, matchlen, matched = 0; + +    ptrzero(dst); + +    if (!line || len <= 0) +	return dst; + +    for (sp = substitutions; sp; sp = sp->next) +	sp->b.start = NULL; + +    do { +	sfirst = NULL; +	for (sp = substitutions; sp; sp = sp->next) { +	    if (sp->b.start && sp->b.start >= line) +		matched = 1; +	    else { +		if (!(matched = (!sp->b.mbeg || start) && match_mark_or_subst(&sp->b, line))) +		    sp->b.start = lineend; +	    } +	    if (matched && sp->b.start < lineend && +		(!sfirst || sp->b.start < sfirst->b.start)) +		sfirst = sp; +	} + +	if (sfirst) { +	    start = 0; + +	    dst = ptrmcat(dst, line, matchlen = sfirst->b.start - line); +	    if (MEM_ERROR) break; +	    line += matchlen; +	    len -= matchlen; + +	    dst = ptrmcat(dst, sfirst->replacement, strlen(sfirst->replacement)); +	    if (MEM_ERROR) break; +            matchlen = sfirst->b.end - sfirst->b.start; +            line += matchlen; +	    len -= matchlen; +	} +    } while (sfirst); + +    if (!MEM_ERROR) +	dst = ptrmcat(dst, line, len); + +    return dst; +} + + +/* + * add substitutions and markers to line. write in dst. + */ +ptr ptrmaddsubst_and_marks(ptr dst, char *line, int len) +{ +    static ptr ptrbuf = NULL; +    if (!substitutions) { +        return ptrmaddmarks(dst, line, len); +    } else if (!markers) { +        return ptrmaddsubst(dst, line, len); +    } +    if (!ptrbuf) { +        ptrbuf = ptrnew(PARAMLEN); +        if (!ptrbuf) { +            goto fail; +        } +    } +    ptrbuf = ptrmaddsubst(ptrbuf, line, len); +    if (ptrbuf) { +        dst = ptrmaddmarks(dst, ptrdata(ptrbuf), ptrlen(ptrbuf)); +    } +fail: +    if (MEM_ERROR) { +        print_error(error); +        ptrzero(dst); +    } +    return dst; +} + +ptr ptraddsubst_and_marks(ptr dst, ptr line)  {      if (line) -	return ptrmaddmarks(dst, ptrdata(line), ptrlen(line)); +	return ptrmaddsubst_and_marks(dst, ptrdata(line), ptrlen(line));      ptrzero(dst);      return dst;  } @@ -594,7 +677,7 @@ static void wrap_print(char *s)  }  /* - * add marks to line and print. + * add substitutions and marks to line and print.   * if newline, also print a final \n   */  void smart_print(char *line, char newline) @@ -607,7 +690,7 @@ void smart_print(char *line, char newline)  	    ptrbuf = ptrnew(PARAMLEN);  	    if (MEM_ERROR) break;  	} -	ptrbuf = ptrmaddmarks(ptrbuf, line, strlen(line)); +	ptrbuf = ptrmaddsubst_and_marks(ptrbuf, line, strlen(line));  	if (MEM_ERROR || !ptrbuf) break;  	buf = ptrdata(ptrbuf); @@ -943,6 +1026,8 @@ int read_settings(void)  	delete_marknode(&markers);      while (keydefs)  	delete_keynode(&keydefs); +    while (substitutions) +	delete_substnode(&substitutions);      for (n = 0; n < MAX_HASH; n++) {  	while (named_vars[0][n])  	    delete_varnode(&named_vars[0][n], 0); @@ -1059,6 +1144,7 @@ int save_settings(void)      actionnode *acp;      promptnode *ptp;      marknode *mp; +    substnode *sp;      keynode *kp;      varnode *vp;      ptr pp = (ptr)0; @@ -1150,11 +1236,18 @@ int save_settings(void)      }      for (mp = markers; mp && failed > 0; mp = mp->next) { -	pp = ptrmescape(pp, mp->pattern, strlen(mp->pattern), 0); +	pp = ptrmescape(pp, mp->b.pattern, strlen(mp->b.pattern), 0);  	if (MEM_ERROR) { failed = -1; break; } -	failed = fprintf(f, "#mark %s%s=%s\n", mp->mbeg ? "^" : "", +	failed = fprintf(f, "#mark %s%s=%s\n", mp->b.mbeg ? "^" : "",  			     ptrdata(pp), attr_name(mp->attrcode));      } + +    for (sp = substitutions; sp && failed > 0; sp = sp->next) { +	pp = ptrmescape(pp, sp->b.pattern, strlen(sp->b.pattern), 0); +	if (MEM_ERROR) { failed = -1; break; } +	failed = fprintf(f, "#substitute %s%s=%s\n", sp->b.mbeg ? "^" : "", +			     ptrdata(pp), sp->replacement); +    }      /* save value of global variables */      for (flag = 0, i=0; i<NUMVAR && failed > 0; i++) { diff --git a/src/utils.h b/src/utils.h index f5b8704..833b0ba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -13,8 +13,8 @@ int  memunescape(char *p, int lenp);  ptr  ptrescape(ptr dst, ptr src, int append);  ptr  ptrmescape(ptr dst, char *src, int srclen, int append); -ptr  ptraddmarks(ptr dst, ptr line); -ptr  ptrmaddmarks(ptr dst, char *line, int len); +ptr  ptraddsubst_and_marks(ptr dst, ptr line); +ptr  ptrmaddsubst_and_marks(ptr dst, char *line, int len);  void put_marks(char *dst, char *line);  void smart_print(char *line, char newline); @@ -45,4 +45,3 @@ void movie_write(char *str, int newline);  void update_now(void);  #endif /* _UTILS_H_ */ - | 
