aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-03-09 15:48:33 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2013-03-09 15:50:44 +0200
commit0200dd186ea1f536301638560722019722f21f78 (patch)
tree0dffe9dde70ef8d4d618c48d28568e65a94a05c0
parent16874f2338544dad6cc573726760406bae8f1029 (diff)
downloadion-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--.gitignore6
-rw-r--r--Makefile4
-rw-r--r--ion.183
-rw-r--r--ion.c24
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile14
-rw-r--r--test/a.c98
-rw-r--r--test/b.c25
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
diff --git a/Makefile b/Makefile
index 788d790..1c7d20e 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/ion.1 b/ion.1
index 926776e..a21f545 100644
--- a/ion.1
+++ b/ion.1
@@ -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"
diff --git a/ion.c b/ion.c
index 45225f7..93f7428 100644
--- a/ion.c
+++ b/ion.c
@@ -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;
+}

Return to:

Send suggestions and report system problems to the System administrator.