aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pw.1122
-rw-r--r--pw.c115
2 files changed, 175 insertions, 62 deletions
diff --git a/pw.1 b/pw.1
index 8c95cca..1f25b7e 100644
--- a/pw.1
+++ b/pw.1
@@ -184,49 +184,107 @@ and
also terminate colon mode without executing a command. Colon commands
are documented in the COLON COMMANDS section below.
-.IP "\fB/\fP\fIpattern\fP, \fB?\fP\fIpattern\fP"
-Activate trigger mode if a non-empty
+.IP "[\fIpos\fP]\fB/\fP\fIpattern\fP, [\fIpos\fP]\fB?\fP\fIpattern\fP"
+Set a trigger if a non-empty
.I pattern
-is specified, or else cancel trigger mode if an empty pattern is
-specified. The
+is specified, or else cancel a trigger if an empty pattern is specified.
+The
.B /
-command specifies
-.I "head trigger"
+command specifies a
+.I "head-referenced trigger"
mode, whereas
.B ?
specifies
-.I "tail trigger"
+.I "tail-referenced trigger"
mode.
-Under head trigger mode,
-the display refreshes only when the most recent line ("head line")
-in the FIFO matches the specified
-.IR pattern .
-The head line is the one which will appear at
-the bottom of the display, since newer lines appear at the bottom
-and scroll out toward the top.
-In head trigger mode, the status string
-.BI "TRIG (/" pattern ")"
-appears, showing the pattern preceded by a slash.
-Under tail trigger mode,
-the display refreshes only when the least recent line ("tail line") in the FIFO
-matches the specified
-.IR pattern .
-That line is the one which will appear at
-the top of the display. In tail trigger mode, the status string
-.BI "TRIG (?" pattern ")"
-appears, showing the pattern preceded by a question mark.
-The
-.I pattern
-is interpreted as either an extended regular expression (ERE)
-or (BRE) depending on the current setting.
+
+Under trigger mode, the display is suspended, similarly to suspend mode.
+It is only refreshed when data arrives into the FIFO such that the
+state of the FIFO then matches certain regular expression patterns.
+
+A head-referenced trigger is relative to most recently received data
+which appears at the bottom of the display. A tail-referenced
+trigger is on least-recently received data, relative to the top
+of the display. If one or more triggers are set, then trigger
+mode is in effect. If all triggers are removed, trigger mode
+is canceled.
+
+Tail-referenced and head-referenced triggers are mutually exclusive.
+If a tail-referenced trigger command is successfully executed,
+then all head-referenced triggers are removed. Vice versa, a
+tail-referenced trigger command cancels all head-referenced triggers.
+
+If the numeric prefix
+.I pos
+is omitted, or specified as zero, then it defaults to 1.
+The prefix specifies the relative position of the pattern.
+A head-referenced trigger's position is relative to the most
+recent data in the FIFO, and therefore is effectively a
+reverse line number relative to the bottom of the display.
+For example
+.B /foo
+or, equivalently,
+.B 1/foo
+triggers when the bottom line of the display contains a match for
+the expression
+.BR foo .
+Whereas
+.B 3/bar
+triggers when the third line from the bottom matches
+.BR bar .
+To cancel the
+.B 3/bar
+trigger without canceling the
+.B 1/foo
+trigger, the command
+.B 3/
+can be used:
+.B 3/
+with an empty pattern. Inversely, the tail-referenced pattern
+positions are from the top of the display. Thus
+.B ?foo
+or
+.B 1?foo
+trigger when the top line contains a match for
+.BR foo .
+
+If the value of
+.I pos
+specifies a line beyond the display range, or a value greater than 100, the
+command is ignored.
+
Trigger patterns saved in a history which may be navigated for
recall using the up and down arrow keys or
.B Ctrl-P
and
.BR Ctrl-N .
-While the trigger pattern is being edited, the current trigger remains
-in effect. Editing a trigger pattern may be canceled with
-.BR ESC .
+While the trigger pattern is being edited, the current set of
+triggers remains in effect. Editing a trigger pattern may be canceled with
+.BR ESC ,
+in which case it is not entered into the history and and the
+trigger command isn't executed. The history stores only the patterns, not the
+.I pos
+prefix or the
+.B ?
+or
+.B /
+command. Erroneous patterns, and the patterns of ignored commands
+are still entered into the history; empty patterns are not.
+
+The
+.I pattern
+of a newly issued command interpreted as either an extended regular
+expression (ERE) or (BRE) depending on the current setting.
+This is true even if it is recalled from a history entry which had been created
+under a different mode.
+
+In trigger mode, the status string
+.B TRIG/
+or
+.B TRIG?
+is shown followed by the list of triggers in parentheses.
+Patterns which have a position other than 1 are preceded by
+the position shown in square brackets.
.IP "[\fIcount\fP]\fB+\fP"
Increases the display size by
diff --git a/pw.c b/pw.c
index 9960e5a..7a22523 100644
--- a/pw.c
+++ b/pw.c
@@ -23,6 +23,8 @@
#define ESC 27
#define DEL 127
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
#ifdef __GNUC__
#define printf_attr(fmtpos, vargpos) __attribute__ ((format (printf, \
fmtpos, vargpos)))
@@ -64,8 +66,10 @@ static int regex_flags = 0;
static char **snapshot;
static int snaplines;
-static char *trigpat;
-static regex_t trigex;
+
+#define maxtrig 100
+static char *trigpat[maxtrig];
+static regex_t trigex[maxtrig];
static grep grepstack[maxgrep];
static int ngrep;
@@ -296,10 +300,21 @@ static void drawstatus(int columns, unsigned stat, char *cmd)
}
}
- if ((stat & stat_htmode))
- ptr += snprintf(ptr, end - ptr, "TRIG (/%s) ", trigpat);
- else if ((stat & stat_ttmode))
- ptr += snprintf(ptr, end - ptr, "TRIG (?%s) ", trigpat);
+ if ((stat & (stat_htmode | stat_ttmode))) {
+ ptr += snprintf(ptr, end - ptr, "TRIG%c (",
+ (stat & stat_htmode) ? '/' : '?');
+ for (int i = 0, first = 1; i < maxtrig; i++) {
+ if (trigpat[i]) {
+ if (!first)
+ ptr += snprintf(ptr, end - ptr, ", ");
+ if (i > 0)
+ ptr += snprintf(ptr, end - ptr, "[%d]", i + 1);
+ ptr += snprintf(ptr, end - ptr, "%s", trigpat[i]);
+ first = 0;
+ }
+ }
+ ptr += snprintf(ptr, end - ptr, ") ");
+ }
if ((stat & stat_susp))
ptr += snprintf(ptr, end - ptr, "SUSPENDED ");
@@ -716,17 +731,32 @@ int main(int argc, char **argv)
}
}
if (line) {
- if ((stat & stat_htmode))
- if (regexec(&trigex, line, 0, NULL, 0) == 0)
- stat |= stat_trgrd;
if (nlines == maxlines) {
dsdrop(circbuf[0]);
memmove(circbuf, circbuf + 1, (nlines - 1) * sizeof *circbuf);
circbuf[nlines - 1] = line;
stat |= stat_dirty;
- if ((stat & stat_ttmode))
- if (regexec(&trigex, circbuf[0], 0, NULL, 0) == 0)
+ if ((stat & stat_ttmode)) {
+ int lim = min(maxtrig, nlines);
+ int trig = 1;
+ for (int i = 0; i < lim; i++)
+ if (trigpat[i] && regexec(&trigex[i], circbuf[i], 0, NULL, 0) != 0) {
+ trig = 0;
+ break;
+ }
+ if (trig)
stat |= stat_trgrd;
+ } else if ((stat & stat_htmode)) {
+ int trig = 1;
+ for (int j = nlines - 1, i = 0; j >= 0 && i < maxtrig; j--, i++) {
+ if (trigpat[i] && regexec(&trigex[i], circbuf[j], 0, NULL, 0) != 0) {
+ trig = 0;
+ break;
+ }
+ }
+ if (trig)
+ stat |= stat_trgrd;
+ }
} else {
circbuf[nlines++] = line;
if ((stat & stat_susp) == 0) {
@@ -944,6 +974,8 @@ int main(int argc, char **argv)
break;
case CR: case ctrl('c'):
if (ch == CR) {
+ unsigned trig = (cmdcount == UINT_MAX || cmdcount == 0
+ ? 0 : cmdcount - 1);
if (cmdbuf[1]) {
unsigned *pnhist = (kbd_state == kbd_colon
? &ncmdhist : &npathist);
@@ -960,38 +992,61 @@ int main(int argc, char **argv)
}
}
- if (kbd_state == kbd_trig) {
- if (trigpat) {
- regfree(&trigex);
- dsdrop(trigpat);
- trigpat = 0;
- }
- stat &= ~(stat_htmode | stat_ttmode);
- }
-
if (kbd_state == kbd_colon && cmdbuf[1]) {
execute(cmdbuf, &stat);
if (cmdbuf[0] != 0) {
kbd_state = kbd_result;
break;
}
- } else if (cmdbuf[1]) {
+ } else if (kbd_state == kbd_trig && (int) trig < maxlines && trig < maxtrig) {
int err;
- trigpat = dsdup(cmdbuf + 1);
- if ((err = regcomp(&trigex, trigpat,
- regex_flags | REG_NOSUB)))
- {
- regerror(err, &trigex, cmdbuf, sizeof cmdbuf);
+ char *pat = dsdup(cmdbuf + 1);
+ regex_t regex;
+
+ if (*pat && (err = regcomp(&regex, pat, regex_flags | REG_NOSUB))) {
+ regerror(err, &regex, cmdbuf, sizeof cmdbuf);
if (columns < (int) sizeof cmdbuf - 1)
cmdbuf[columns] = 0;
kbd_state = kbd_result;
- dsdrop(trigpat);
- trigpat = 0;
- break;
+ } else {
+ if ((cmdbuf[0] == '/' && (stat & stat_ttmode)) ||
+ (cmdbuf[0] == '?' && (stat & stat_htmode)))
+ {
+ for (int i = 0; i < maxtrig; i++) {
+ if (trigpat[i]) {
+ regfree(&trigex[i]);
+ dsdrop(trigpat[i]);
+ trigpat[i] = 0;
+ }
+ }
+ }
+
+ if (trigpat[trig]) {
+ regfree(&trigex[trig]);
+ dsdrop(trigpat[trig]);
+ trigpat[trig] = 0;
+ }
+
+ if (*pat) {
+ regfree(&regex);
+ regcomp(&trigex[trig], pat, regex_flags | REG_NOSUB);
+ trigpat[trig] = dsdup(pat);
+ }
+ }
+ dsdrop(pat);
+ }
+
+ if (kbd_state == kbd_trig) {
+ stat &= ~(stat_htmode | stat_ttmode);
+ for (int i = 0; i < maxtrig; i++) {
+ if (trigpat[i]) {
+ stat |= (cmdbuf[0] == '/' ? stat_htmode : stat_ttmode);
+ break;
+ }
}
- stat |= (cmdbuf[0] == '/' ? stat_htmode : stat_ttmode);
}
}
+
kbd_state = kbd_cmd;
curcmd = 0;
cmdcount = UINT_MAX;