#include /* errno */ #include /* fprintf(3), getc(3), putc(3), stderr, stdin, stdout, EOF, * NULL */ #include /* size_t */ #include /* strerror(3) */ #if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE # include #endif #include /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ #include /* dup(2), execvp(3), fork(2), getopt(3), isatty(3), * pipe(2), STDIN_FILENO */ static char *program_name = "peek"; int main(int argc, char *argv[]){ int c; int eof; size_t i; char include_eof; FILE *outputs[] = { NULL /* stdout */, NULL /* stderr */, NULL /* -p */ }; int p[2] = {0, 0}; struct termios t; if(argc < 1) goto usage; eof = EOF; include_eof = 0; while((c = getopt(argc, argv, "1enopt")) != -1) switch(c){ case '1': eof = '\n'; break; case 'n': include_eof = 1; break; case 'o': outputs[0] = stdout; break; case 'e': outputs[1] = stderr; break; case 'p': if(pipe(p) != 0) goto die; else if((outputs[2] = fdopen(p[1], "ab")) == NULL) goto die; break; case 't': if(isatty(STDIN_FILENO) != 1){ fprintf(stderr, "%s: Must be run in a terminal" " (option -t specified)\n", argv[0]); return EX_USAGE; } default: goto usage; } /* If -p is used there must be additional arguments. Getopt(3) wouldn't * work for this because optarg would have to be one string to give to * system(3) or an equivalent and it would be a mess of parsing and * security issues. Any intended usage works with this slightly funkier * argument parsing, unintended usages work as happy coincidence. */ if((argc > optind) == (outputs[2] == 0)){ usage: fprintf(stderr, "Usage: %s (-1enot)" " (-p [program [arguments...]])\n", argv[0] == NULL ? program_name : argv[0]); return EX_USAGE; } if(outputs[2] != 0) switch(fork()){ case 0: if(close(p[1]) == 0 && dup2(p[0], STDIN_FILENO) == STDIN_FILENO) execvp(argv[optind], argv + optind); case -1: goto die; default: if(close(p[0]) != 0) goto die; } /* terminal echo */ tcgetattr(STDIN_FILENO, &t); t.c_lflag ^= ECHO; tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); do{ if((c = getc(stdin)) != eof || include_eof) for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i) if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){ if(i == 2) fclose(outputs[i]); outputs[i] = 0; } }while(c != eof); tcgetattr(STDIN_FILENO, &t); t.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); return EX_OK; die: fprintf(stderr, "%s: %s\n", argv[0], strerror(errno)); return EX_OSERR; }