diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-03-09 15:48:33 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-03-09 15:50:44 +0200 |
commit | 0200dd186ea1f536301638560722019722f21f78 (patch) | |
tree | 0dffe9dde70ef8d4d618c48d28568e65a94a05c0 | |
parent | 16874f2338544dad6cc573726760406bae8f1029 (diff) | |
download | ion-0200dd186ea1f536301638560722019722f21f78.tar.gz ion-0200dd186ea1f536301638560722019722f21f78.tar.bz2 |
Improve docs, provide a testcase.
* .gitignore: New file.
* Makefile: Update.
* ion.1: Rewrite.
* ion.c (getarg0): Bugfix.
* test/.gitignore: New file.
* test/Makefile: New file.
* test/a.c: New file.
* test/b.c: New file.
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | ion.1 | 83 | ||||
-rw-r--r-- | ion.c | 24 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile | 14 | ||||
-rw-r--r-- | test/a.c | 98 | ||||
-rw-r--r-- | test/b.c | 25 |
8 files changed, 213 insertions, 42 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0976d46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.emacs* +*.o +*~ +core +*.tar.* +ion @@ -1,2 +1,6 @@ ion: ion.c cc -ggdb -Wall -oion -DCANONICAL_NAME=\"ion\" ion.c +check: + make -C test check +clean: + rm -f ion *.o @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Ion. If not, see <http://www.gnu.org/licenses/>. -.TH ION 1 "March 4, 2013" "ION" +.TH ION 1 "March 9, 2013" "ION" .SH NAME ion \- Input/Output Normalizer .SH SYNOPSIS @@ -21,34 +21,64 @@ ion \- Input/Output Normalizer .I PROGRAM [\fIARGS\fR...] .SH DESCRIPTION -Some interactive shell programs incorrectly bufferize their output, -which can cause problems when another program is trying to communicate -with them via pipes. For example, GNU -.BR ed (1) -fails to flush its +.B Ion +runs the supplied program with its three standard streams connected to +a +.BR tty . +Normally this means that the program's +.BR stdin , +.BR stdout , +and .B stderr -after issuing an error explanation message. Another example is -.BR xfs_db (8) -from xfsprogs v. 3.1.7, which does not flush -.B stdout -after outputting its prompt. The list can be continued. +will be line-buffered. .PP -.B Ion -attempts to fix it by invoking the program supplied as its argument -and attaching a -.B PTY -to its three basic streams. It then acts as a proxy between the -program and the process that invoked it, transparently sending back -and forth data through these streams. +Admittedly, there's not much sense in that when started from the +command line. However, there is a scenario when the use of +.B ian +becomes necessary. It is when an interactive +.B PROGRAM +is invoked from another program, which communicates with it via pipes. +Unless the invoked +.I PROGRAM +takes special precautions, its standard streams would become fully +buffered, which would make any interactive use meaningless. In this +scenario, the calling program is supposed to run +.B ion +with its command line being the +.I PROGRAM +name and its arguments. When invoked so, the calling program would be +able to communicate with the +.I PROGRAM +just as a human being would using the console. .PP -Unless invoked as +One example of such usage is +.BR xfs_db (8), +which, when called from an interactive program such as +.BR xfs_irecover (8) +from Jan Engelhardt's +.B hxtools +fails to communicate because it uses full buffering by default. +.PP +Often it is not possible or desirable to alter the calling program so, +that it would run +.B ion +instead of the intended +.IR PROGRAM . +Taking into account this, .B ion -this program tries to spawn the program it is invoked as with the -arguments from its command line. This feature is particularly useful -if your program invokes another not by its absolute pathname, but -rather using the shell -.B PATH -instead. To use this, make sure you link +implements a special calling convention. Namely, unless invoked as +.B ion +it tries to spawn the program it is invoked as with the arguments from +its command line. The +.I PROGRAM +(unless started with an absolute pathname) is then searched in the +system +.BR PATH , +excluding the directory where the invoked copy of +.B ion +itself is located. +.PP +To use this feature, make sure you link .B ion to a program name in a directory preceding the one where the actual program is located in @@ -82,8 +112,7 @@ from your program will first invoke which will invoke .B xfs_db from -.B /usr/bin -and will let your program communicate with it. +.BR /usr/bin . .SH AUTHORS Sergey Poznyakoff .SH "BUG REPORTS" @@ -114,7 +114,8 @@ getarg0(char **argv) size_t bufsize = 0; int found; - if (argv[0][0] == '/') { + pn = strrchr(argv[0], '/'); + if (pn) { if (stat(argv[0], &st)) { perror("stat"); return 1; @@ -122,7 +123,6 @@ getarg0(char **argv) dev = st.st_dev; ino = st.st_ino; - pn = strrchr(argv[0], '/'); *pn++ = 0; } else { pn = argv[0]; @@ -287,7 +287,7 @@ main(int argc, char **argv) for (; i < sysconf(_SC_OPEN_MAX); i++) close(i); execvp(argv[0], argv); - perror("execvp"); + perror(argv[0]); _exit(EX_EXEC); } @@ -295,8 +295,8 @@ main(int argc, char **argv) bufinit(obuf); for (stop = 0; !stop;) { FD_ZERO(&rdset); - FD_SET(in, &rdset); - FD_SET(master, &rdset); + if (in != -1) FD_SET(in, &rdset); + if (master != -1) FD_SET(master, &rdset); if (select(master + 1, &rdset, NULL, NULL, NULL) < 0) { if (errno == EINTR) { @@ -308,28 +308,22 @@ main(int argc, char **argv) exit(EX_ERR); } - if (in >= 0 && FD_ISSET(in, &rdset)) { - bufread(ibuf, in); - } + if (in >= 0 && FD_ISSET(in, &rdset)) bufread(ibuf, in); - if (master >= 0 && FD_ISSET(master, &rdset)) { + if (master >= 0 && FD_ISSET(master, &rdset)) bufread(obuf, master); - } if (out >= 1 && !bufisempty(obuf)) { tr(&obuf); bufwrite(obuf, out); } - if (master > 0 && !bufisempty(ibuf)) { - bufwrite(ibuf, master); - } + if (master > 0 && !bufisempty(ibuf)) bufwrite(ibuf, master); if (bufisempty(ibuf)) bufinit(ibuf); if (bufisempty(obuf)) { bufinit(obuf); - if (master < 0) - stop = 1; + if (master < 0) stop = 1; } } diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..f47cb20 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +*.out diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..18a8143 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,14 @@ +check: ../ion a.out b.out + @if a.out ../ion b.out; then \ + echo "Success"; \ + else \ + echo >&2 "FAILURE"; \ + false; \ + fi + +.c.out: + cc -o$@ $< + + + + diff --git a/test/a.c b/test/a.c new file mode 100644 index 0000000..b1f715b --- /dev/null +++ b/test/a.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> + +char prompt[] = "rev> "; +int promptlen = sizeof(prompt) - 1; +char *input[] = { "adam", "word", "revert", NULL }; +char *output[] = { "mada", "drow", "trever", NULL }; + +int +main(int argc, char **argv) +{ + int ip[2], op[2]; + pid_t pid; + int i; + FILE *ifile, *ofile; + char buf[128]; + + assert(argc > 1); + argc--; + argv++; + assert(pipe(ip) == 0); + assert(pipe(op) == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* ip[0] - child stdin + ip[1] - master write + op[0] - master read + op[1] - child stdout + */ + close(ip[1]); + close(op[0]); + if (ip[0] != 0) { + close(0); + dup(ip[0]); + close(ip[0]); + } + if (op[1] != 1) { + close(1); + dup(op[1]); + close(op[1]); + } + execvp(argv[0], argv); + perror(argv[0]); + _exit(127); + } + /* master */ + close(ip[0]); + close(op[1]); + ifile = fdopen(op[0], "r"); + if (!ifile) { + perror("op[0]"); + abort(); + } + setlinebuf(ifile); + ofile = fdopen(ip[1], "w"); + if (!ofile) { + perror("ip[1]"); + abort(); + } + setlinebuf(ofile); + for (i = 0; input[i]; i++) { + alarm(5); + if (fread(buf, promptlen, 1, ifile) != 1) { + fprintf(stderr, "%d: read error\n", __LINE__); + abort(); + } + alarm(0); + + if (strncmp(buf, prompt, promptlen)) { + fprintf(stderr, "%d: expected prompt, but got \"%*.*s\"\n", + __LINE__, promptlen, promptlen, buf); + abort(); + } + + fprintf(ofile, "%s\n", input[i]); + + alarm(5); + if (!fgets(buf, sizeof(buf), ifile)) { + fprintf(stderr, "%d: read error\n", __LINE__); + abort(); + } + alarm(0); + buf[strlen(buf)-1] = 0; + if (strcmp(buf, output[i])) { + fprintf(stderr, "%d: expected \"%s\", but got \"%s\"\n", + __LINE__, output[i], buf); + abort(); + } + } + return 0; +} + diff --git a/test/b.c b/test/b.c new file mode 100644 index 0000000..115d521 --- /dev/null +++ b/test/b.c @@ -0,0 +1,25 @@ +#include <stdio.h> + +int +main() +{ + int i, j; + char buf[128]; + static char prompt[] = "rev> "; + + while (1) { + fwrite(prompt, strlen(prompt), 1, stdout); + if (!fgets(buf, sizeof(buf), stdin)) + break; + j = strlen(buf) - 1; + if (buf[j] == '\n') + buf[j--] = 0; + for (i = 0; i < j; i++, j--) { + int c = buf[j]; + buf[j] = buf[i]; + buf[i] = c; + } + fprintf(stdout, "%s\n", buf); + } + return 0; +} |