From 4a4c430d3affc992737d11c6b403c62ceb97007c Mon Sep 17 00:00:00 2001 From: Steve Slaven Date: Mon, 23 Jan 2023 22:28:34 -0800 Subject: factor out parsing #substitute and #mark diff --git a/doc/powwow.doc.in b/doc/powwow.doc.in index 7236f87..649310b 100644 --- a/doc/powwow.doc.in +++ b/doc/powwow.doc.in @@ -580,6 +580,13 @@ SPECIAL COMMANDS: ALL OTHERS at the beginning of a line) #mark \^=on blue (mark a literal ^ ) ----------------------------------------------------------- + Substitute output + #substitute [pattern[=[replace]]] + + This command substitutes the pattern with test from replace. + + Uses the same pattern rules as #mark + ----------------------------------------------------------- #module [module name] This loads a shared library module by name, if supported by your system. The diff --git a/src/cmd.c b/src/cmd.c index 847fbdc..9ef0539 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -227,10 +227,10 @@ cmdstruct default_commands[] = "connect-id command\ttalk with a shell command"), C("speedwalk", cmd_speedwalk, "[speedwalk sequence]\texecute a speedwalk sequence explicitly"), - C("substitute", cmd_substitute, - "[string[=[text]]]\tdelete/list/define substitutions"), 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, diff --git a/src/cmd2.c b/src/cmd2.c index 1503f88..b21ede3 100644 --- a/src/cmd2.c +++ b/src/cmd2.c @@ -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", @@ -883,99 +918,274 @@ void show_marks(void) 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-empty.\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->b.pattern, strlen(n->b.pattern), 0); - if (MEM_ERROR) { ptrdel(pbuf); return; } - name = attr_name(n->attrcode); - sprintf(inserted_next, "#mark %s%.*s=%.*s", n->b.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->b.mbeg ? "^" : "", - n->b.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 @@ -991,97 +1201,72 @@ void show_substitutions(void) 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; -/* - * parse arguments to the #mark command - */ -void parse_substitute(char *str) + 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) { - char *p; - substnode **np, *n; - char mbeg = 0; + substnode *n, **np = parse_substitute__action_with_substnode(result, pattern, match_from_beginning); - if (*str == '=') { - PRINTF("#substitute must be non-empty.\n"); - return; + if (n = *np) { + result->action_complete = 1; + delete_substnode(np); } - p = first_regular(str, '='); - if (!*p) { - if (*str == '^') - mbeg = 1, str++; - unescape(str); - np = lookup_subst(str, mbeg); - if ((n = *np)) { - char *replacement = n->replacement; - ptr pbuf = ptrmescape(pbuf, n->b.pattern, strlen(n->b.pattern), 0); - if (MEM_ERROR) { ptrdel(pbuf); return; } - sprintf(inserted_next, "#substitute %s%.*s=%.*s", n->b.mbeg ? "^" : "", - BUFSIZE-(int)strlen(replacement)-14, pbuf ? ptrdata(pbuf) : "", - BUFSIZE-14, replacement); - ptrdel(pbuf); - } else { - PRINTF("#unknown substitution, cannot show: \"%s\"\n", str); - } +} +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 { - char pattern[BUFSIZE], *p2, *replacement; - int wild = 0; + result->action_complete = 2; + add_substnode(pattern, replacement, match_from_beginning, pattern_has_wild); + } - *(p++) = '\0'; - 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++; - } + strcpy(result->display_value, replacement); +} - np = lookup_subst(pattern, mbeg); - replacement = p; - if (!*p) - if ((n = *np)) { - if (opt_info) { - PRINTF("#deleting substitution: %s%s=%s\n", n->b.mbeg ? "^" : "", - n->b.pattern, n->replacement); - } - delete_substnode(np); - } else { - PRINTF("#unknown substitution, cannot delete: \"%s%s\"\n", - mbeg ? "^" : "", pattern); - } - else { - if ((n = *np)) { - if (!(replacement = my_strdup(replacement))) { - return; - } - if (n->replacement) { free(n->replacement); } - n->replacement = replacement; - if (opt_info) { - PRINTF("#changed"); - } - } else { - add_substnode(pattern, replacement, mbeg, wild); - if (opt_info) { - PRINTF("#new"); - } - } - if (opt_info) - tty_printf(" substitution: %s%s=%s\n", mbeg ? "^" : "", - pattern, 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 diff --git a/src/defines.h b/src/defines.h index 87687ef..52d50fe 100644 --- a/src/defines.h +++ b/src/defines.h @@ -218,7 +218,7 @@ typedef struct aliasnode { typedef struct basenode { char *pattern; char *start, *end; // temporary data: start/end of current line match - char mbeg; + char mbeg; // match from beginning of string char wild; } basenode; diff --git a/src/prove.c b/src/prove.c index 33757f3..497ebda 100644 --- a/src/prove.c +++ b/src/prove.c @@ -265,12 +265,12 @@ static void TEST_mark_syntax_error() C_output_from_user_input_is( "#mark =bar", - "#marker must be non-empty.\n", + "#mark must be non-empty\n", "#mark - empty error"); C_output_from_user_input_is( "#mark foo&$foo=bar", - "#error: two wildcards (& or $) may not be next to eachother\n", + "#mark two wildcards (& or $) may not be next to eachother\n", "#mark - two consecutive wildcards"); } @@ -396,9 +396,9 @@ static void TEST_mark() static void TEST_substitute_syntax_error() { - C_output_from_user_input_contains( + C_output_from_user_input_is( "#substitute = ", - "#substitute must be non-empty.", + "#substitute must be non-empty\n", "#substitute - syntax error"); } @@ -434,15 +434,18 @@ void prove() { _is_proving = 1; - do_test(TEST_tap, "TAP output"); + 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_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"); + do_test(TEST_substitute_syntax_error, "#substitute syntax error"); + do_test(TEST_substitute, "#substitute usage"); + } done_testing(); -- cgit v0.10.2