1
0

independ pscat(1)

This commit is contained in:
dtb
2022-09-13 00:52:52 -04:00
parent ec717feb5f
commit 97c96d1063
4 changed files with 60 additions and 17 deletions

28
pscat/Makefile Normal file
View File

@@ -0,0 +1,28 @@
all: pscat
clean:
rm -rf ../dist/pscat ../dist/pscat.tar ../dist/pscat.tar.gz pscat
dist: ../dist/pscat.tar.gz
sane: pscat.c ../include/sysexits.h
$(CC) -DUSE_ASCII_H -DDONT_USE_SYSTEM_SYSEXITS -o pscat pscat.c
pscat: pscat.c
$(CC) -o pscat pscat.c
../dist/pscat: pscat
mkdir -p ../dist/pscat.tmp/bin/ ../dist/pscat.tmp/share/man/man1/
cp pscat ../dist/pscat.tmp/bin/pscat
cp pscat.1 ../dist/pscat.tmp/share/man/man1/pscat.1
mv ../dist/pscat.tmp ../dist/pscat
../dist/pscat.tar: ../dist/pscat
cd ../dist/pscat && pax -w -x ustar . >../pscat.tar.tmp
mv ../dist/pscat.tar.tmp ../dist/pscat.tar
../dist/pscat.tar.gz: ../dist/pscat.tar
gzip -c <../dist/pscat.tar >../dist/pscat.tar.gz.tmp
mv ../dist/pscat.tar.gz.tmp ../dist/pscat.tar.gz
.PHONY: all clean sane

37
pscat/pscat.1 Normal file
View File

@@ -0,0 +1,37 @@
.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.
.PP
Pscat's function is redundant to the sh(1) construct
.RB { utility ; utility ;}
- this is a feature, not a bug.
.SH COPYRIGHT
Public domain.

111
pscat/pscat.c Normal file
View File

@@ -0,0 +1,111 @@
#ifndef DONT_USE_SYSTEM_SYSEXITS
# include <sysexits.h>
#else
# include "../include/sysexits.h"
#endif /* ifndef DONT_USE_SYSTEM_SYSEXITS */
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.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. */
#ifndef USE_ASCII_H
# define L_PAREN '['
# define R_PAREN ']'
#else
# include "../include/ascii.h"
# define L_PAREN ASCII_LEFT_SQUARE_BRACKET
# define R_PAREN ASCII_RIGHT_SQUARE_BRACKET
#endif /* ifndef USE_ASCII_H */
/* 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,
NORMAL = 1,
INLPAREN = 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;
default: /* >= INLPAREN */
if(scmpflat(*argv, R_PAREN)){
--s;
terms += s == NORMAL;
}else if(scmpflat(*argv, L_PAREN)) /* ineligant */
++s;
break;
}
return terms;
}
int main(int argc, char *argv[]){
char **psstart;
int child;
int i;
int p;
int retval;
int terms;
retval = 0;
if((terms = check_arg(++argv)) == 0){
fprintf(stderr,
"Usage: %s \"[\" [utility [argument...]] \"]\" ...\n",
argv[0] == NULL ? program_name : argv[0]
);
return EX_USAGE;
}
/* loop starts with *argv -> the next L_PAREN */
for(i = 0; i < terms; ++i){
psstart = ++argv;
p = 1;
while(p > 0){
++argv;
/* branching here potentially saves a comparison.
* this seems like the most optimal way to do this,
* maybe it isn't, i don't care too much */
if(scmpflat(*argv, L_PAREN))
++p;
else if(scmpflat(*argv, R_PAREN))
--p;
}
/* *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;
}