1
0
This commit is contained in:
dtb 2022-07-19 16:52:28 -04:00
parent 1ba5208e58
commit 9a95b64618
4 changed files with 129 additions and 0 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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
View 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
View 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;
}