aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-04-26 23:04:32 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-04-26 23:04:32 -0700
commit411630a95f6c856c4a627d67c6afe4be2ca71ab6 (patch)
tree6d1b840463998d3e55e0e245f50a42afe3a95c0c
parent66ba7fdd8752bc46c9cf14605226f044d259ab05 (diff)
downloadpw-411630a95f6c856c4a627d67c6afe4be2ca71ab6.tar.gz
pw-411630a95f6c856c4a627d67c6afe4be2ca71ab6.tar.bz2
pw-411630a95f6c856c4a627d67c6afe4be2ca71ab6.zip
Add a vi-like colon mode with :w, :a and :! commands.
-rw-r--r--pw.145
-rw-r--r--pw.c161
2 files changed, 192 insertions, 14 deletions
diff --git a/pw.1 b/pw.1
index db849fa..e33e2e4 100644
--- a/pw.1
+++ b/pw.1
@@ -156,6 +156,45 @@ status string disappears. If the status is already
.B EOF
then suspend mode cannot be entered.
+.IP \fB:\fP
+Enter colon command mode. In colon command mode, the status line clears
+and a colon prompt appears there. An extended command can be entered.
+Pressing
+.I Enter
+in colon mode dispatches the command. Simple editing is available:
+backspace,
+.B Ctrl-W
+word erase and
+.B Ctrl-U
+line erase. If backspace is used in an empty colon line, colon mode
+terminates without executing a command.
+.B Ctrl-C
+and
+.B Esc
+also terminate colon mode without executing a command. Colon commands
+are documented in the COLON COMMANDS section below.
+
+.SH COLON COMMANDS
+
+First, some general remarks. the current contents of the FIFO buffer may have
+changed relative to what is displayed on the screen. The space between the
+command and the argument may be omitted. The command is replaced with
+a result string, which persists over the poll interval period. The longer
+the poll interval
+.RB ( -i " option),
+the longer the result message persists.
+
+.IP "\fB:w\fP \fIfilename\fP"
+Write the current contents of the FIFO buffer into the specified file.
+
+.IP "\fB:a\fP \fIfilename\fP"
+Append the current contents of the FIFO buffer into the specified file.
+
+.IP "\fB:!\fP \fIfilename\fP"
+Pipe the current contents of the FIFO buffers into the specified command.
+.PP
+Any other command results in a brief error message.
+
.SH OPTIONS
.IP "\fB-i\fP \fIreal\fP"
@@ -241,6 +280,12 @@ screen.
There is no support for unwrapping long lines, which would be useful for
copy and paste.
+Ctrl-Z suspend is missing.
+
+During the
+.B :!
+command's execution, the TTY settings are not restored.
+
.SH AUTHOR
Kaz Kylheku <kaz@kylheku.com>
diff --git a/pw.c b/pw.c
index 8d79183..5ef006d 100644
--- a/pw.c
+++ b/pw.c
@@ -114,29 +114,93 @@ static void drawline(const char *line, int hpos, int columns)
}
}
-static void drawstatus(unsigned stat)
+static void clear_cur_line()
+{
+ printf("\r\033[J");
+}
+
+static void drawstatus(unsigned stat, char *cmd)
{
- if ((stat & (stat_eof | stat_susp))) {
+ if (cmd) {
+ printf(":%s", cmd);
+ } else if ((stat & (stat_eof | stat_susp))) {
if ((stat & stat_eof))
printf("EOF ");
if ((stat & stat_susp))
printf("SUSPENDED ");
- fflush(stdout);
+ } else {
+ clear_cur_line();
}
+ fflush(stdout);
}
static void redraw(char **circbuf, int nlines, int hpos,
- int columns, unsigned stat)
+ int columns, unsigned stat, char *cmd)
{
printf("\r\033[%dA\033[J", nlines);
for (int i = 0; i < nlines; i++)
drawline(circbuf[i], hpos, columns);
- drawstatus(stat);
+ drawstatus(stat, cmd);
}
-static void clear_cur_line()
+static void execute(char *cmd, char **circbuf, int nlines)
{
- printf("\r\033[J");
+ char *arg = cmd + 1 + strspn(cmd + 1, " \t");
+
+ clear_cur_line();
+
+ switch (cmd[0]) {
+ case 'w': case 'a':
+ {
+ FILE *f = fopen(arg, cmd[0] == 'w' ? "w" : "a");
+ int ok = 1;
+
+ if (!f) {
+ sprintf(cmd, "unable to open file");
+ break;
+ }
+
+ for (int i = 0; ok && i < nlines; i++)
+ if (fprintf(f, "%s\n", circbuf[i]) < 0) {
+ sprintf(cmd, "write error!");
+ ok = 0;
+ break;
+ }
+
+ fclose(f);
+ if (ok)
+ sprintf(cmd, "saved!");
+ }
+ break;
+ case '!':
+ {
+ FILE *p = popen(arg, "w");
+ int ok = 1;
+
+ if (!p) {
+ sprintf(cmd, "unable to open command");
+ fflush(stdout);
+ break;
+ }
+
+ for (int i = 0; i < nlines && ok; i++)
+ if (fprintf(p, "%s\n", circbuf[i]) < 0) {
+ sprintf(cmd, "write error!");
+ ok = 0;
+ break;
+ }
+
+ pclose(p);
+ if (ok)
+ sprintf(cmd, "piped!");
+ }
+ break;
+ default:
+ sprintf(cmd, "bad command");
+ break;
+ }
+
+ fflush(stdout);
}
int main(int argc, char **argv)
@@ -153,9 +217,10 @@ int main(int argc, char **argv)
struct termios tty_saved, tty_new;
struct winsize ws = { 0 };
int columns = 80;
- enum kbd_state { kbd_cmd, kbd_esc, kbd_bkt, kbd_exit };
+ enum kbd_state { kbd_cmd, kbd_esc, kbd_bkt, kbd_exit, kbd_colon, kbd_result };
int auto_quit = 1;
int exit_status = EXIT_FAILURE;
+ char cmdbuf[100], *colcmd = 0;
if (fd < 0)
panic("unable to obtain input file descriptor");
@@ -241,7 +306,7 @@ int main(int argc, char **argv)
now = (((unsigned) tv.tv_sec)%1000000)*1000 + tv.tv_usec/1000;
if (lasttime == ~0U || now - lasttime > (unsigned) long_interval) {
if ((stat & stat_dirty) && nlines == maxlines)
- redraw(circbuf, nlines, hpos, columns, stat);
+ redraw(circbuf, nlines, hpos, columns, stat, colcmd);
lasttime = now;
stat &= ~stat_dirty;
}
@@ -249,10 +314,15 @@ int main(int argc, char **argv)
if (poll(pe, ((stat & stat_eof)) ? 1 : 2, poll_interval) <= 0) {
if ((stat & stat_dirty) && nlines == maxlines) {
- redraw(circbuf, nlines, hpos, columns, stat);
+ redraw(circbuf, nlines, hpos, columns, stat, colcmd);
stat &= ~stat_dirty;
}
- kbd_state = kbd_cmd;
+ if (kbd_state == kbd_esc || kbd_state == kbd_result) {
+ kbd_state = kbd_cmd;
+ colcmd = 0;
+ clear_cur_line();
+ drawstatus(stat, colcmd);
+ }
} else {
if ((stat & stat_eof) == 0 && pe[1].revents) {
if ((line = getln(stdin))) {
@@ -269,14 +339,14 @@ int main(int argc, char **argv)
circbuf[nlines++] = line;
clear_cur_line();
drawline(line, hpos, columns);
- drawstatus(stat);
+ drawstatus(stat, colcmd);
}
} else {
if (auto_quit)
kbd_state = kbd_exit;
else
stat |= stat_eof;
- redraw(circbuf, nlines, hpos, columns, stat);
+ redraw(circbuf, nlines, hpos, columns, stat, colcmd);
stat |= stat_eof;
stat &= ~stat_dirty;
if (!ferror(stdin))
@@ -326,6 +396,11 @@ int main(int argc, char **argv)
case 27:
kbd_state = kbd_esc;
break;
+ case ':':
+ kbd_state = kbd_colon;
+ cmdbuf[0] = 0;
+ colcmd = cmdbuf;
+ break;
}
break;
case kbd_esc:
@@ -347,13 +422,71 @@ int main(int argc, char **argv)
goto fakecmd;
}
break;
+ case kbd_colon:
+ switch (ch) {
+ case 27: case 13: case 3:
+ kbd_state = kbd_cmd;
+ stat |= stat_dirty;
+ if (ch == 13 && cmdbuf[0]) {
+ execute(cmdbuf, circbuf, nlines);
+ stat &= ~stat_dirty;
+ kbd_state = kbd_result;
+ break;
+ }
+ colcmd = 0;
+ break;
+ case 8: case 127:
+ {
+ size_t len = strlen(cmdbuf);
+ if (len == 0) {
+ kbd_state = kbd_cmd;
+ colcmd = 0;
+ stat |= stat_dirty;
+ } else {
+ cmdbuf[--len] = 0;
+ }
+ }
+ break;
+ case 21:
+ cmdbuf[0] = 0;
+ break;
+ case 23:
+ {
+ size_t len = strlen(cmdbuf);
+ while (len > 0 && isspace((unsigned char) cmdbuf[len - 1]))
+ len--;
+ while (len > 0 && !isspace((unsigned char) cmdbuf[len - 1]))
+ len--;
+ cmdbuf[len] = 0;
+ }
+ break;
+ default:
+ if (isprint(ch))
+ {
+ size_t len = strlen(cmdbuf);
+ if (len < sizeof cmdbuf - 1 && (int) len < columns - 1) {
+ cmdbuf[len++] = ch;
+ cmdbuf[len] = 0;
+ }
+ }
+ break;
+ }
+ break;
+ case kbd_result:
+ kbd_state = kbd_cmd;
+ stat |= stat_dirty;
+ colcmd = 0;
+ break;
case kbd_exit:
break;
}
if ((stat & stat_dirty)) {
- redraw(circbuf, nlines, hpos, columns, stat);
+ redraw(circbuf, nlines, hpos, columns, stat, colcmd);
stat &= ~stat_dirty;
+ } else if (kbd_state == kbd_colon || kbd_state == kbd_result) {
+ clear_cur_line();
+ drawstatus(stat, colcmd);
}
}
}