pscat(1)
This commit is contained in:
parent
1ba5208e58
commit
9a95b64618
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ bin/id
|
|||||||
bin/lowercase
|
bin/lowercase
|
||||||
bin/nonzero
|
bin/nonzero
|
||||||
bin/nutshell
|
bin/nutshell
|
||||||
|
bin/pscat
|
||||||
bin/rldecode
|
bin/rldecode
|
||||||
bin/rlencode
|
bin/rlencode
|
||||||
bin/roll
|
bin/roll
|
||||||
|
7
Makefile
7
Makefile
@ -25,6 +25,7 @@ cleanprograms:
|
|||||||
$(RM) bin/mm
|
$(RM) bin/mm
|
||||||
$(RM) bin/multiply
|
$(RM) bin/multiply
|
||||||
$(RM) bin/nonzero
|
$(RM) bin/nonzero
|
||||||
|
$(RM) bin/pscat
|
||||||
$(RM) bin/simexec
|
$(RM) bin/simexec
|
||||||
$(RM) bin/sleep
|
$(RM) bin/sleep
|
||||||
$(RM) bin/streq
|
$(RM) bin/streq
|
||||||
@ -90,6 +91,12 @@ nutshell.o: libio usefulmacros src/nutshell.c src/nutshell.h src/nutshell_builti
|
|||||||
nutshell: libio nutshell.o
|
nutshell: libio nutshell.o
|
||||||
$(CC) $(CFLAGS) -o bin/nutshell build/libio.o build/nutshell.o
|
$(CC) $(CFLAGS) -o bin/nutshell build/libio.o build/nutshell.o
|
||||||
|
|
||||||
|
pscat.o: libio src/pscat.c
|
||||||
|
$(CC) $(CFLAGS) -c -o build/pscat.o src/pscat.c
|
||||||
|
|
||||||
|
pscat: libio pscat.o
|
||||||
|
$(CC) $(CFLAGS) -o bin/pscat build/libio.o build/pscat.o
|
||||||
|
|
||||||
roll.o: lib/libio.h src/roll.c sysexits
|
roll.o: lib/libio.h src/roll.c sysexits
|
||||||
$(CC) $(CFLAGS) -c -o build/roll.o src/roll.c
|
$(CC) $(CFLAGS) -c -o build/roll.o src/roll.c
|
||||||
|
|
||||||
|
33
man/pscat.1
Normal file
33
man/pscat.1
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.TH PSCAT 1
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
|
||||||
|
pscat \- concatenate the output of processes
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
|
||||||
|
pscat
|
||||||
|
"["
|
||||||
|
.RB [ utility
|
||||||
|
.RB [ argument... ]]
|
||||||
|
"]" ...
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Pscat executes multiple commands, one after the other.
|
||||||
|
This allows multiple processes to be piped as one to another program.
|
||||||
|
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
|
||||||
|
Pscat will print an error message and exit with the appropriate status from sysexits(3) if executed improperly.
|
||||||
|
Pscat will exit with the sum of the child processes' exit statuses if run correctly.
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
|
||||||
|
Pscat's exit status isn't useful when run correctly; there's no way to tell which process failed if one did.
|
||||||
|
This issue of ergonomics isn't obviously mendable as processes' standard outputs and standard errors are meant to both be conveyed.
|
||||||
|
If either could be ignored the individual exit statuses could simply be printed.
|
||||||
|
|
||||||
|
.SH COPYRIGHT
|
||||||
|
|
||||||
|
Public domain.
|
88
src/pscat.c
Normal file
88
src/pscat.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include <ascii.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "libio.h"
|
||||||
|
|
||||||
|
static char *program_name = "pscat";
|
||||||
|
|
||||||
|
/* Originally designed to use parentheses but changed to brackets to escape the
|
||||||
|
* hassle of escaping them from the Bourne shell. */
|
||||||
|
#define L_PAREN ASCII_LEFT_SQUARE_BRACKET
|
||||||
|
#define R_PAREN ASCII_RIGHT_SQUARE_BRACKET
|
||||||
|
|
||||||
|
/* Test string containing { c, '\0' } without iteration.
|
||||||
|
* Theoretically saves a little bit of time compared to strcmp(3). */
|
||||||
|
int
|
||||||
|
scmpflat(char *s, int c){
|
||||||
|
return
|
||||||
|
s[0] != '\0'
|
||||||
|
&& s[1] == '\0'
|
||||||
|
&& s[0] == c
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verifies arguments to pscat are sensible. */
|
||||||
|
int
|
||||||
|
check_arg(char **argv){
|
||||||
|
enum {
|
||||||
|
UNINITIALIZED = 0,
|
||||||
|
INLPAREN = 1,
|
||||||
|
NORMAL = 2
|
||||||
|
} s;
|
||||||
|
int terms;
|
||||||
|
|
||||||
|
for(s = UNINITIALIZED, terms = 0; *argv != NULL; ++argv)
|
||||||
|
switch(s){
|
||||||
|
case UNINITIALIZED: case NORMAL:
|
||||||
|
if(scmpflat(*argv, L_PAREN))
|
||||||
|
s = INLPAREN;
|
||||||
|
else
|
||||||
|
return 0; /* syntax error */
|
||||||
|
break;
|
||||||
|
case INLPAREN:
|
||||||
|
if(scmpflat(*argv, R_PAREN))
|
||||||
|
{ ++s; ++terms; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]){
|
||||||
|
char *argv0;
|
||||||
|
char **psstart;
|
||||||
|
int child;
|
||||||
|
int i;
|
||||||
|
int retval;
|
||||||
|
int terms;
|
||||||
|
|
||||||
|
argv0 = argv[0] == NULL ? program_name : argv[0];
|
||||||
|
retval = 0;
|
||||||
|
|
||||||
|
if((terms = check_arg(++argv)) == 0){
|
||||||
|
write(2, "Usage: ", 7);
|
||||||
|
fdprint(2, argv0);
|
||||||
|
write(2, " \"[\" [utility [argument...]] \"]\" ...\n", 37);
|
||||||
|
return EX_USAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loop starts with *argv -> the next L_PAREN */
|
||||||
|
for(i = 0; i < terms; ++i){
|
||||||
|
psstart = ++argv;
|
||||||
|
while(!scmpflat(*++argv, R_PAREN));
|
||||||
|
/* *argv -> the corresponding R_PAREN. turn it into NULL to
|
||||||
|
* terminate the argument list to send to execvp(3) */
|
||||||
|
*argv = NULL;
|
||||||
|
if(fork() == 0){
|
||||||
|
execvp(psstart[0], psstart);
|
||||||
|
}else
|
||||||
|
wait(&child);
|
||||||
|
/* interpret status information from wait(2) */
|
||||||
|
if(WIFEXITED(child))
|
||||||
|
retval += WEXITSTATUS(child);
|
||||||
|
++argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user