aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--configure.ac8
-rw-r--r--doc/powwow.doc.in7
-rw-r--r--src/Makefile.am4
-rw-r--r--src/cmd.c20
-rw-r--r--src/cmd2.c439
-rw-r--r--src/cmd2.h4
-rw-r--r--src/defines.h19
-rw-r--r--src/edit.c3
-rw-r--r--src/list.c76
-rw-r--r--src/list.h6
-rw-r--r--src/main.c11
-rw-r--r--src/main.h1
-rw-r--r--src/prove.c463
-rw-r--r--src/prove.h12
-rw-r--r--src/tty.c8
-rw-r--r--src/utils.c157
-rw-r--r--src/utils.h5
18 files changed, 1107 insertions, 138 deletions
diff --git a/README b/README
index 830df06..1f5e080 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
- powwow 1.2.22 README
+ powwow 1.2.23 README
Powwow is a client program intended mainly for playing MUD.
It implements (a subset of) the telnet protocol.
diff --git a/configure.ac b/configure.ac
index 0e03934..34a7666 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
# Process this file with autoconf to produce a configure script.
-AC_INIT(powwow, 1.2.22)
+AC_INIT(powwow, 1.2.23)
AM_INIT_AUTOMAKE
AC_CANONICAL_HOST
@@ -25,6 +25,12 @@ AC_ARG_ENABLE(regex,
[enable_regex="yes"]
)
+AC_ARG_ENABLE(prove,
+ AC_HELP_STRING([--enable-prove],
+ [Enable prove option [[default=no]]]),
+ [AC_DEFINE(ENABLE_PROVE)]
+)
+
AC_ARG_ENABLE(sort,
AC_HELP_STRING([--enable-sort],
[Sort aliases and actions [[default=no]]]),
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/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
diff --git a/src/cmd.c b/src/cmd.c
index f11dd53..9ef0539 100644
--- a/src/cmd.c
+++ b/src/cmd.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);
}
-
-
diff --git a/src/cmd2.c b/src/cmd2.c
index dbd264a..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",
@@ -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);
}
}
-
diff --git a/src/cmd2.h b/src/cmd2.h
index 96cb067..7e9e1fe 100644
--- a/src/cmd2.h
+++ b/src/cmd2.h
@@ -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_ */
-
diff --git a/src/edit.c b/src/edit.c
index bcfab21..d42d8bb 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -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();
}
-
diff --git a/src/list.c b/src/list.c
index 2652bda..3e7471b 100644
--- a/src/list.c
+++ b/src/list.c
@@ -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);
}
diff --git a/src/list.h b/src/list.h
index c336049..c57167e 100644
--- a/src/list.h
+++ b/src/list.h
@@ -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_ */
-
diff --git a/src/main.c b/src/main.c
index 3c7fe9b..2f2a320 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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();
diff --git a/src/main.h b/src/main.h
index e76f7cb..3daafc3 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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_ */
+
diff --git a/src/tty.c b/src/tty.c
index 104c780..a1e8590 100644
--- a/src/tty.c
+++ b/src/tty.c
@@ -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_ */
-