aboutsummaryrefslogtreecommitdiffstats
path: root/pw.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2023-06-08 22:59:40 -0700
committerKaz Kylheku <kaz@kylheku.com>2023-06-08 22:59:40 -0700
commitd2ae79caf381389d5bda82b324ec82f980e4a6a8 (patch)
tree9610f3ee93bb1ccf50bb874da4083efd86124592 /pw.c
parent53cb074b0db3fb20b1496ef608bb6b7fadad59ad (diff)
downloadpw-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.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/pw.c b/pw.c
index 23a7beb..b12c8de 100644
--- a/pw.c
+++ b/pw.c
@@ -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);