diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-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 | 19 | ||||
-rw-r--r-- | src/edit.c | 3 | ||||
-rw-r--r-- | src/list.c | 76 | ||||
-rw-r--r-- | src/list.h | 6 | ||||
-rw-r--r-- | src/main.c | 11 | ||||
-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 |
15 files changed, 1092 insertions, 136 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 @@ -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 1e273e1..52d50fe 100644 --- a/src/defines.h +++ b/src/defines.h @@ -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(); } - @@ -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(); @@ -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_ */ - |