diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2023-06-08 22:59:40 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2023-06-08 22:59:40 -0700 |
commit | d2ae79caf381389d5bda82b324ec82f980e4a6a8 (patch) | |
tree | 9610f3ee93bb1ccf50bb874da4083efd86124592 /pw.c | |
parent | 53cb074b0db3fb20b1496ef608bb6b7fadad59ad (diff) | |
download | pw-d2ae79caf381389d5bda82b324ec82f980e4a6a8.tar.gz pw-d2ae79caf381389d5bda82b324ec82f980e4a6a8.tar.bz2 pw-d2ae79caf381389d5bda82b324ec82f980e4a6a8.zip |
Feature: implement pass-through mode.
In pass-through mode, pw can be used in the middle of a pipeline,
or redirected to a file or device. Then instead of discarding
data, it copies from standard input to standard output.
pw's implementation assumes that standard output is the terminal,
and uses some functions that operate implicitly on standard output.
To avoid changing all that code, dup2 calls are used to rearrange
the file descriptors. The tty descriptor opened from /dev/tty is
installed as standard output, and the original standard output
(the device, file or pipe to which data is to be passed through)
is tied to a dedicated stream held in the local variable out.
Whenever out is not null, bytes are to be sent to it rather than
discarded.
In pass-through mode, though, standard output is not the
terminal.
Diffstat (limited to 'pw.c')
-rw-r--r-- | pw.c | 37 |
1 files changed, 31 insertions, 6 deletions
@@ -1156,14 +1156,19 @@ static char **resizebuf(char **buf, size_t nlfrom, size_t nlto) return buf; } -int isbkgnd(FILE *tty) +int isbkgnd(int ttyfd) { - int fd = fileno(tty); pid_t grp = getpgrp(); - pid_t fgrp = tcgetpgrp(fd); + pid_t fgrp = tcgetpgrp(ttyfd); return (grp != fgrp); } +int ismytty(int ttyfd) +{ + pid_t fgrp = tcgetpgrp(ttyfd); + return fgrp != -1; +} + void clipsplits(pwstate *pw) { int columns = pw->columns; @@ -1188,6 +1193,7 @@ int main(int argc, char **argv) size_t maxlen = 2047; int opt; int ifd = fileno(stdin); + int ofd = fileno(stdout); int ttyfd = tty ? fileno(tty) : -1; struct termios tty_saved, tty_new; struct winsize ws = { 0 }; @@ -1198,6 +1204,7 @@ int main(int argc, char **argv) int auto_quit = 1; int quit_count = 1, quit_cntdwn = quit_count; int exit_status = EXIT_FAILURE; + FILE *out = NULL; #ifdef SIGWINCH static struct sigaction sa; #endif @@ -1220,6 +1227,12 @@ int main(int argc, char **argv) } } + if (!isatty(ofd) || !ismytty(ttyfd)) { + int dup_ofd = dup(ofd); + dup2(ttyfd, ofd); + out = fdopen(dup_ofd, "w"); + } + while ((opt = getopt(argc, argv, "n:i:l:dEBg:q:m:p:e:f:")) != -1) { switch (opt) { case 'n': @@ -1392,7 +1405,7 @@ int main(int argc, char **argv) if (fcntl(ifd, F_SETFL, O_NONBLOCK) < 0) panic("unable to set stdin nonblocking"); - if (!isbkgnd(stdout)) + if (!isbkgnd(ttyfd)) ttyset(ttyfd, &tty_new); else pw.stat = stat_bkgnd; @@ -1415,9 +1428,14 @@ int main(int argc, char **argv) if ((pw.stat & stat_eof) == 0) { int ch; - while ((ch = getc(stdin)) != EOF && ch != '\n' && dslen(line) < maxlen) + while ((ch = getchar()) != EOF && ch != '\n' && dslen(line) < maxlen) { line = addchesc(line, pw.tstop, ch); + if (out) + putc(ch, out); + } if (ch == EOF) { + if (out) + fflush(out); if (feof(stdin) || (errno != EAGAIN && errno != EWOULDBLOCK)) { nfds = 1; if (!ferror(stdin)) @@ -1430,9 +1448,13 @@ int main(int argc, char **argv) pw.stat |= stat_eof; clrline(pw.stat); drawstatus(&pw); + if (out) + fflush(out); } clearerr(stdin); } else if (ch == '\n') { + if (out) + putc(ch, out); nfds = 1; line = dsensure(line); if ((pw.stat & stat_grep)) { @@ -1552,7 +1574,7 @@ int main(int argc, char **argv) work = workbout; if ((pw.stat & stat_bkgnd)) { - if (!isbkgnd(stdout)) { + if (!isbkgnd(ttyfd)) { pw.stat &= ~stat_bkgnd; ttyset(ttyfd, &tty_new); for (int i = 0; i < pw.nlines; i++) @@ -2055,6 +2077,9 @@ int main(int argc, char **argv) ttyset(ttyfd, &tty_saved); } + if (out) + fclose(out); + #if CONFIG_DEBUG_LEAKS freebuf(pw.circbuf, pw.maxlines); free(pw.circbuf); |