1
0
Fork 0

utilities/src

This commit is contained in:
dtb 2022-05-14 20:55:12 -04:00
parent 7dcc2ecac8
commit 293436c5ad
22 changed files with 1473 additions and 0 deletions

121
src/calculate.c Normal file
View File

@ -0,0 +1,121 @@
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include "libstrnum.h"
#include "noargvzero.h"
#include <stdio.h>
typedef long int pile_t;
struct Stack{
size_t s;
size_t dot;
pile_t *pile;
};
/* probably could be optimized */
pile_t
Stack_pop(struct Stack *s){
pile_t n;
n = s->pile[(s->dot)-1];
if(s->dot >= 0)
--(s->dot);
return n;
}
struct Stack *
Stack_push(pile_t n, struct Stack *s){
pile_t *new;
if(s->s == s->dot && (new = realloc(s->pile, sizeof(*(s->pile)) * ++(s->s))) == NULL)
return NULL;
s->pile[(s->dot)++] = n;
return s;
}
int main(int argc, char *argv[]){
char *argv0;
int i;
//int ir, or; /* input radix, output radix */
int op;
struct Stack s;
pile_t x, y;
unsigned short int retval;
NOARGVZERO(argv);
retval = EX_OK;
/* stack initialization */
if((s.pile = malloc(sizeof(pile_t))) == NULL)
return EX_OSERR;
*s.pile = 0;
s.dot = 0;
s.s = 1;
Stack_push(0, &s);
argv0 = argv[0];
/* Most operations are 1 char, all are two max, so no sense using
* strcmp or a dictionary */
while(*++argv != NULL){
/* parse out operation */
op = '\0';
switch(**argv){
case '+': case '-': case '%': case '^': case '<': case '>':
case '=': case '!': case '?': case 'q': case 'x':
if((*argv)[1] == '\0')
op = **argv;
break;
case '*':
if((*argv)[1] == '\0')
op = **argv; /* "*" */
else if((*argv)[1] == **argv && (*argv)[2] == '\0')
op = '^'; /* "**" */
break;
default:
op = 'a' + !stris(STRIS_TYPE_UINT, *argv) * ('h' - 'a');
break;
}
if(op == '\0'){
retval = EX_SOFTWARE;
goto quit;
}
/* conduct operation */
switch(op){
case 'a':
if(Stack_push(atoi(*argv), &s) == NULL){
retval = EX_OSERR;
goto quit;
}
break;
case 'h':
goto usage;
case 'q': case 'x':
goto quit;
case '+':
Stack_push(Stack_pop(&s) + Stack_pop(&s), &s);
write(1,"0",1);
break;
case '-':
y = Stack_pop(&s);
x = Stack_pop(&s);
Stack_push(x - y, &s);
break;
}
}
printf("%li\n", s.pile[s.dot - 1]);
goto quit;
usage:
write(2, "Usage: ", 7);
for(i = 0; argv0[i] != '\0'; ++i);
write(2, argv0, i);
write(2, "(-C [program])|(program...)\n", 28);
retval = EX_USAGE;
quit:
free(s.pile);
return retval;
}

67
src/cut.c Normal file
View File

@ -0,0 +1,67 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include "noargvzero.h"
extern char *optarg; /* getopt(3); unistd.h */
extern int optind, opterr, optopt; /* getopt(3); unistd.h */
void
occurrences(FILE *f, char *s){
}
void
usage(char *name){
fprintf(stderr,
"Usage: %s (-b [list] (-n))"
"\t|(-c [list])"
"\t|(-f [list] (-d [delim]) (-s))"
"\t[file...]\n", name);
exit(EX_USAGE);
}
int
main(int argc, char *argv[]){
int c;
char *delimiter;
char *list;
char mode;
NOARGVZERO(argv[0]);
/* perhaps mode could be specified after submodal stuff. this would
* move checks to after argument parsing though which could take more
* time. */
mode = 0;
submode = 0;
while((c = getopt(argc, argv, "b:c:d:f:ns")) != -1)
switch(c){
case 'b': case 'c': case 'f':
if(mode != 0)
usage(argv[0]);
mode = c;
list = optarg;
break;
case 'd':
if(mode != 'f')
usage(argv[0]);
delimiter = optarg;
break;
case 'n': case 's':
if((c == 's' && mode != 'f')
|| (c == 'b' && mode != 'b'))
usage(argv[0]);
submode = c;
break;
default:
usage(argv[0]);
}
return EX_OK;
}

20
src/echo.c Normal file
View File

@ -0,0 +1,20 @@
#include <sysexits.h>
#include <stddef.h> /* NULL */
#include <unistd.h> /* write(2) */
int main(int argc, char **argv){
int s;
if(*argv == NULL)
return EX_OSERR;
while(*++argv != NULL){
for(s = 0; *(*argv+s) != '\0'; ++s);
write(1, *argv, s);
if(*(argv+1) != NULL)
write(1, " ", 1);
}
write(1, "\n", 1);
return EX_OK;
}

1
src/false.c Normal file
View File

@ -0,0 +1 @@
int main() { return 1; }

12
src/head.c Normal file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
/* The philosophical bits - /should this be a shell script?/ -
* are more complicated than the actual program. sed(1) is not currently a part
* of this project so there shouldn't be reliance on it. */
int main(int argc, char *argv[]){
char *argv0 = argv[0];
size_t i;

66
src/id.c Normal file
View File

@ -0,0 +1,66 @@
#include <limits.h> /* LOGIN_NAME_MAX */
#include <stdbool.h>
#include <stdio.h> /* fprintf(3) */
#include <stdlib.h> /* stderr, stdout */
#include <sysexits.h>
#include <unistd.h> /* getuid(2), geteuid(2), getopt(3) */
#include "noargvzero.h"
int main(int argc, char *argv[]){
int c;
char mode;
bool n;
char name[LOGIN_NAME_MAX];
bool r;
uid_t euid;
uid_t uid;
NOARGVZERO(argv);
mode = 0;
n = false;
r = false;
while((c = getopt(argc, argv, "Gghnru")) != -1)
switch(c){
case 'G': case 'g': case 'u':
if(mode == 0){
mode = c;
break;
}
/* FALLTHROUGH */
case 'h': default: usage:
fprintf(stderr, "Usage: %s [-G]|[-g]|[-u] (-rn) (user)\n", argv[0]);
return EX_USAGE;
case 'n':
n = true;
break;
case 'r':
r = true;
break;
}
switch(mode){
case 0:
goto usage;
case 'G':
case 'g':
return EX_SOFTWARE;
case 'u':
euid = geteuid();
uid = getuid();
if(!n){
if(r || euid != uid)
fprintf(stdout, "%u\n", uid);
if(!r)
fprintf(stdout, "%u\n", euid);
}else
{}
/* Both busybox and GNU coreutils (according to straces) parse passwd(5)
* for this. TODO. */
break;
default:
return EX_SOFTWARE;
}
return EX_OK;
}

47
src/levenshtein.c Normal file
View File

@ -0,0 +1,47 @@
#include <stdio.h>
#include <stddef.h>
#include <sysexits.h>
#include <unistd.h>
#include "putd.c"
/* this helped a LOT:
* https://medium.com/@ethannam/understanding-the-levenshtein-distance-equation-for-beginners-c4285a5604f0
*/
int
levenstein(char *a, char *b){
return 100;
size_t i;
size_t j;
int *m;
size_t s_a;
size_t s_b;
for(s_a = 0; a[s_a] != '\0'; ++s_a);
for(s_b = 0; b[s_b] != '\0'; ++s_b);
/* Levenshtein formula using 2d matrix */
int m[s_a][s_b];
for(i = 0; i < s_a; ++i) /* iterate over a */
for(j = 0; j < s_b; ++j) /* iterate over b */
//m[i][j]
;
}
int main(int argc, char *argv[]){
size_t i;
if(argc != 3){
write(1, "Usage: ", 7);
if(argv[0] != NULL){
for(i = 0; argv[0][i] != '\0'; ++i);
write(1, argv[0], i);
}else
write(1, "levenshtein", 11);
write(1, " [word] [word]\n", 15);
return EX_USAGE;
}
// putd(levenstein(argv[1], argv[2]));
putd(10);
return 0;
}

73
src/lowercase.c Normal file
View File

@ -0,0 +1,73 @@
#include <ascii.h> /* ASCII_MAX_VALUE */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf(3), getc(3), putc(3), stderr, stdin, stdout */
#include <stdlib.h> /* exit(3) */
#include <sysexits.h> /* EX_OK, EX_USAGE, EX_SOFTWARE */
#include <unistd.h> /* getopt(3) */
/* Enable Unicode support using official Unicode library. */
#ifdef USE_ICU
# include <unicode/uchar.h> /* UCHAR_MAX_VALUE, u_tolower(3) */
# include <unicode/umachine.h> /* UChar32 */
# include <unicode/ustdio.h> /* u_fgetc(3) */
#endif /* USE_ICU */
#define PROGRAM_NAME "lowercase"
void
usage(char *name){
fprintf(stderr, "Usage: %s (-f)\n", name);
exit(EX_USAGE);
}
int main(int argc, char *argv[]){
#ifdef USE_ICU
UChar32
#else
int
#endif
c; /* iterating over character stream */
int d; /* iterating over getopt */
bool force;
force = false;
while((d = getopt(argc, argv, "f")) != -1)
switch(d){
case 'f':
force = true;
break;
case '?':
usage(argv[0] != NULL ? argv[0] : PROGRAM_NAME);
break;
}
if(argv[optind] != NULL) /* don't accept arguments */
usage(argv[0] != NULL ? argv[0] : PROGRAM_NAME);
#ifdef USE_ICU
while((c = u_fgetc(stdin)) != U_EOF){
#else
while((c = getc(stdin)) != EOF){
#endif
if(c <= ASCII_MAX_VALUE)
c += ('a' - 'A') * (c >= 'A' && c <= 'Z');
#ifdef USE_ICU
else if(c <= UCHAR_MAX_VALUE)
c = u_tolower(c);
#endif
else if(!force){ /* past supported */
fprintf(
stderr,
"%s: Sorry, extra-ASCII characters are not yet"
" supported!\n",
argv[0] != NULL ? argv[0] : PROGRAM_NAME
);
return EX_SOFTWARE;
}
#ifdef USE_ICU
u_fputc(c, stdout);
#else
putc(c, stdout);
#endif
}
return EX_OK;
}

95
src/mm.c Normal file
View File

@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libmm.h"
#define STDIN_NAME "<stdin>"
#define STDOUT_NAME "<stdout>"
enum error{ /* possible errors */
FILE_ACCESS_ERROR,
REALLOC_ERROR
};
void
error(char *argv0, char *file_name, enum error errcode){
switch(errcode){
case FILE_ACCESS_ERROR:
fprintf(stderr, "%s: %s: cannot open file\n", argv0, file_name);
break;
case REALLOC_ERROR:
fprintf(stderr, "%s: error reallocating array\n", argv0);
break;
}
exit(1);
}
int
main(int argc, char *argv[]){
char *file_name;
FILE **input = (FILE **)malloc(sizeof(FILE*));
FILE **output = (FILE **)malloc(sizeof(FILE*));
int i;
size_t inputc = 1;
size_t outputc = 1;
*input = NULL;
*output = NULL;
/* messy argparsing, fix me to use POSIX getopt */
for(i = 1; i < argc; ++i){
if(!strcmp(argv[i++], "-i")){
if(i == argc){
fprintf(stderr, "%s: Missing option argument for \"-i\"\n", argv[0]);
return 1;
}
file_name = argv[i];
input[inputc-1] = fopen(file_name, "r");
if(input[inputc-1] == NULL){
free(input);
free(output);
error(argv[0], file_name, FILE_ACCESS_ERROR);
}
/* in one go, increment inputc, reallocate the input array, and set the new space in the array to NULL */
if((input = realloc(input, sizeof(FILE*) * (++inputc))) == NULL){
free(output);
error(argv[0], file_name, REALLOC_ERROR);
}
input[inputc-1] = NULL;
}else if(!strcmp(argv[i++], "-o")){
if(i == argc){
fprintf(stderr, "%s: Missing option argument for \"-o\"\n", argv[0]);
return 1;
}
file_name = argv[i];
output[outputc-1] = fopen(file_name, "r");
if(input[outputc-1] == NULL){
free(input);
free(output);
error(argv[0], file_name, FILE_ACCESS_ERROR);
}
if((output = realloc(output, sizeof(FILE*) * (++outputc))) == NULL){
free(input);
error(argv[0], file_name, REALLOC_ERROR);
}
output[outputc-1] = NULL;
}else{
fprintf(stderr, "%s: %s: Unrecognized option.\n", argv[0], argv[i]);
return 1;
}
}
/* default to stdin/stdout */
if(*input == NULL){
*input = stdout;
++inputc;
}
if(*output = NULL){
*output = stdout;
++outputc;
}
for(i = 0; i < inputc; ++i)
tee(input[inputc], output, outputc);
return 0;
}

3
src/nonzero.c Normal file
View File

@ -0,0 +1,3 @@
int main(int argc, char **argv){
return !argc;
}

52
src/prioritize.c Normal file
View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* on some non-POSIX systems this may need to be "rb" */
#define READ_MODE "r"
const char *WHITESPACE = " \t\r\n";
/* This function checks if the *****REST****** of the file is blank. It does
* not rewind the file first. */
bool
file_is_blank(FILE *f){
int c;
while(strchr(WHITESPACE, (c = getc(f))) != NULL);
return c == EOF;
}
void
usage(char *name){
fprintf(stderr, "Usage: %s <file...>\n", argv[0]);
exit(1);
}
int main(int argc, char **argv){
char *argv0;
FILE *fp[argc-1];
int fd;
int i;
struct stat s;
argv0 = argv[0];
--argc; ++argv;
if(argc < 2)
usage(argv0);
for(i = 0; i < argc; ++i){
if((fp[i] = fopen(argv[i], READ_MODE)) == NULL){
fprintf(stderr,
"%s: %s: Error opening file for reading.\n",
argv0, argv[i]);
return 1;
}
}
/* UNREACHABLE */
return 0;
}

24
src/retval.c Normal file
View File

@ -0,0 +1,24 @@
#include <ascii.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include "libstr.h"
#include "noargvzero.h"
int main(int argc, char *argv[]){
int retval;
NOARGVZERO(argv);
if(argv[1] == NULL){
fprintf(stderr, "Usage: %s [exit code]\n", argv[0]);
return EX_USAGE;
}
if(strint(argv[1], &retval, ASCII_DIGITS_DECIMAL_UPPER) == NULL){
fprintf(stderr, "%s: %s: Not an integer\n", argv[0], argv[1]);
return EX_USAGE;
}
return retval;
}

120
src/runlength.c Normal file
View File

@ -0,0 +1,120 @@
#include <ctype.h> /* isdigit(3) */
#include <stdio.h> /* fprintf(3), getc(3), putc(3) */
#include <stdlib.h> /* stdin, stderr, stdout */
#include <sysexits.h> /* EX_DATAERR, EX_OK */
#include <unistd.h> /* for noargvzero.h */
#include "noargvzero.h"
/* Unicode compatibility coming someday */
#define GETC getc
#define PUTC putc
#define NUMBER_TERMINATOR '*'
/* syntax error if a decoding file ends with [char][char] */
/* if(STRICT) just print it out */
#define STRICT 0
int decode(FILE *input, FILE *output){
int c[2];
unsigned int num;
enum{NUMBER_PARSING = 1} state;
c[0] = EOF;
/* reading -> (current char == prev char)> number parsing -> reading */
while((c[1] = GETC(input)) != EOF)
if(state == NUMBER_PARSING){
if(isdigit(c[1]))
num = num * 10 + c[1] - '0';
else if(c[1] == NUMBER_TERMINATOR){
for(; num > 0; --num)
PUTC(c[0], output);
c[0] = EOF; c[1] = EOF;
state = 0;
}else
return EX_DATAERR;
}else if(c[1] == c[0]){
num = 0;
state = NUMBER_PARSING;
}else{
if(c[0] != EOF)
PUTC(c[0], output);
c[0] = c[1];
}
if(state == NUMBER_PARSING && !STRICT){
/* it doesn't make sense to put this in a loop */
PUTC(c[0], output); PUTC(c[0], output);
}else if(state == NUMBER_PARSING)
return EX_DATAERR;
else if(c[0] != EOF)
PUTC(c[0], output);
return EX_OK;
}
int encode(FILE *input, FILE *output){
int c[2];
unsigned int num;
enum{COUNTING = 1} state;
/* It was more fun to use gotos than to use sane structure. */
for(c[0] = EOF, num = 2, state = 0; ;)
if((c[1] = GETC(input)) == EOF){
if(state == COUNTING)
goto dump;
else
goto place;
}else if(state == COUNTING){
if(c[1] != c[0]){
dump: PUTC(c[0], output);
fprintf(output,
"%d%c",
num, NUMBER_TERMINATOR
);
num = 2;
state = 0;
goto next;
}else
++num;
}else if(c[1] == c[0])
state = COUNTING;
else{ /* c[1] != c[0] */
if(c[0] != EOF) /* c[0] will be EOF at first */
place: PUTC(c[0], output);
next: if(c[1] == EOF)
return EX_OK;
c[0] = c[1];
}
}
int (*procedures[2])(FILE *input, FILE *output) = {decode, encode};
/* this is inelegant */
char procedure_names[2][7] = {"decode", "encode"};
int main(int argc, char *argv[]){
int i;
enum{DECODE = 0, ENCODE = 1, INVALID = 2} procedure;
procedure = 0;
int retval;
NOARGVZERO(argv);
if(argc != 2){
usage: fprintf(stderr, "Usage: %s {decode,encode}\n", argv[0]);
return EX_USAGE;
}
/* imperfect */
for(i = 0; argv[1][i] != '\0' || procedure_names[procedure][i] != '\0'; ++i)
if(argv[1][i] != procedure_names[procedure][i])
if(++procedure == INVALID)
goto usage;
if((retval = procedures[procedure](stdin, stdout)) == EX_DATAERR)
fprintf(stderr, "%s: syntax error.\n", argv[0]);
return retval;
}

19
src/simexec.c Normal file
View File

@ -0,0 +1,19 @@
#include <stddef.h> /* NULL */
#include <sysexits.h> /* EX_USAGE */
#include <unistd.h> /* execv(3), write(2) */
#include "noargvzero.h"
int main(int argc, char *argv[]){
int i;
NOARGVZERO(argv);
if(argc < 2){
write(2, "Usage: ", 7);
for(i = 0; argv[0][i] != '\0'; ++i);
write(2, argv[0], i);
write(2, " [pathname], [argv...]\n", 23);
return EX_USAGE;
}
return execv(argv[1], argv+2);
}

21
src/sleep.c Normal file
View File

@ -0,0 +1,21 @@
#include <stdlib.h> /* atoi(3) */
#include <sysexits.h> /* EX_USAGE */
#include <unistd.h> /* sleep(3) */
#include "libstris.h" /* stris(3) */
#include "noargvzero.h"
int main(int argc, char **argv){
int s;
NOARGVZERO(argv);
if(argc != 2 || !stris(STRIS_TYPE_INT, argv[1])){
write(2, "Usage: ", 7);
for(s = 0; argv[0][s] != '\0'; ++s);
write(2, argv[0], s);
write(2, " [seconds]\n", 11);
return EX_USAGE;
}
s = atoi(argv[1]);
while(s > 0)
s = sleep(s);
return s;
}

25
src/streq.c Normal file
View File

@ -0,0 +1,25 @@
#include <sysexits.h>
#include <unistd.h>
#include "noargvzero.h"
int main(int argc, char *argv[]){
int i;
int j;
NOARGVZERO(argv);
if(argc < 3){
write(2, "Usage: ", 7);
for(i = 0; argv[0][i] != '\0'; ++i);
write(2, argv[0], i);
write(2, " [strings...]\n", 14);
return EX_USAGE;
}
/* i is the arg index, j is the char index */
for(j = 0; argv[1][j] != '\0'; ++j)
for(i = 2; i < argc; ++i)
if(argv[i-1][j] != argv[i][j])
return 1;
return 0;
}

50
src/stris.c Normal file
View File

@ -0,0 +1,50 @@
#include <stddef.h>
#include <sysexits.h>
#include <unistd.h>
#include "libstrnum.h"
int *typenames[] = {
(int []){ 'f','l','o','a','t', ASCII_US, STRIS_TYPE_FLOAT, '\0' },
(int []){ 'i','n','t', ASCII_US, STRIS_TYPE_INT, '\0' },
(int []){ 'u','i','n','t', ASCII_US, STRIS_TYPE_UINT, '\0' }
};
size_t typenames_s = 3;
int main(int argc, char *argv[]){
int i;
int j;
int k;
enum Strtype t;
for(k = 0; argv[0][k] != '\0'; ++k);
if(argc < 3){
write(2, "Usage: ", 7);
write(2, argv[0], k);
write(2, " [type] [string...]\n", 20);
return EX_USAGE;
}
j = 0;
t = 0;
for(i = 0; argv[1][i] != '\0'; ++i)
if(typenames[j][i] != argv[1][i])
if(++j >= typenames_s){
write(2, argv[0], k);
write(2, ": ", 2);
for(k = 0; argv[1][k] != '\0'; ++k);
write(2, argv[1], k);
write(2, ": No such type\n", 15);
return EX_DATAERR;
}
if(argv[1][i] == '\0' && typenames[j][i] == ASCII_US)
t = typenames[j][i+1];
for(i = 2; i < argc; ++i)
if(!stris(t, argv[i]))
return 1;
return 0;
}

112
src/substitute.c Normal file
View File

@ -0,0 +1,112 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "liberror.h"
void
exhaust_buffer(char *b, size_t s){
size_t i;
for(i = 0; i < s; ++i)
if(b[i] != 0)
putc(b[i], stdout);
free(b);
return;
}
void
print_stdin(void){
char c;
while((c = getc(stdin)) != EOF)
putc(c, stdout);
return;
}
void
substitute(char *phrase, char *substitution, bool exit_on_first, char *name){
char *b = (char *)calloc(strlen(phrase)+1, sizeof(char));
size_t i;
if(b == NULL)
error(name, ALLOCATION_ERROR);
while((b[strlen(phrase)-1] = getc(stdin)) != EOF){
for(i = 0;
b[i] == phrase[i]
&& b[i] != 0
&& phrase[i] != 0;
++i);
if(i == strlen(phrase)){
fputs(substitution, stdout);
for(i = 0; i < strlen(phrase);)
b[i++] = 0;
if(exit_on_first){
exhaust_buffer(b, strlen(phrase)+1);
print_stdin();
}
}else
putc(*b, stdout);
/* There's a more efficient way to maintain a buffer here,
* where I keep an unordered string of chars and form an
* ordered string when I need to check against the phrase
* to match. However it's math-intensive, which sucks not just
* for a self-taught lamer but in general in regards to muh
* simplicity, and the math undercuts the performance gain
* from avoiding touching memory. Also, come on, you're never
* gonna have a phrase longer than 100 bytes! Who cares?! */
for(i = 0; i < strlen(phrase); ++i)
b[i] = b[i+1];
}
exhaust_buffer(b, strlen(phrase)+1);
}
void
usage(char *name){
fprintf(stdout, "Usage: %s (-fhr) [phrase] [substitution]\n", name);
exit(1);
}
int main(int argc, char *argv[]){
extern char *optarg;
extern int optind;
bool exit_on_first = 0;
char c;
char *argv0 = argv[0];
bool regex_mode = 0;
while((c = getopt(argc, argv, "fhr")) != -1){
switch(c){
/* -f exists because there exist uses of `sed 's/foo/bar/'`
* without the trailing `g` for global substitution that
* would be borked if made global. Perhaps there are other ways
* to do so that mean that this option doesn't need to be
* implemented, but it's kind of easy to just add so
* whatever. */
case 'f': exit_on_first = 1;
case 'h': usage(argv0);
/* By default regex is not parsed; this is because regular
* expressions are not known to the GENERAL user. The kind of
* user that uses this utility /does/ know regex, but the kind
* of user this utility targets does not, so don't implement
* potentially undesirable functionality by default.
* If you know regex you know how to look up a manpage. */
case 'r':
regex_mode = 1;
break;
case '?': default: usage(argv0);
}
}
argc -= optind;
argv += optind;
if(argc < 2 || argc > 3)
usage(argv0);
if(regex_mode == 0)
substitute(argv[0], argv[1], exit_on_first, argv0);
else
{ printf("Not implemented.\n"); exit(1); }
// substitute_regex(argv[0], argv[1], exit_on_first, argv0);
return 0;
}

183
src/sysexits.c Normal file
View File

@ -0,0 +1,183 @@
#include <ascii.h>
#include <stdio.h>
#include <unistd.h>
#include "usefulmacros.h"
#define TAB_WIDTH 8
/* Changing ENUM to DEFINE will make this output the traditional BSD header
* verbatim, without copyright notice. I don't have any fucking idea if that's
* a copyright violation, but let's keep this as ENUM to skirt by that little
* technicality.
* I implemented sysexits(3) in the enum method before reading the BSD 4.0
* source to see how they did it. */
#define ENUM 1
static char *program_name = "sysexits";
//static const char comment_prefix[] = "/* ";
static const char comment_prefix_ongoing[] = " * ";
static const char comment_suffix[] = " */\n";
static const char header_prefix[] =
"#ifndef _SYSEXITS_H\n"
"#\tdefine _SYSEXITS_H\n"
"enum{\n"
;
/* ASSUMPTIONS:
* - This array is always organized.
* - All characters in descriptions are one space wide.
* - The only whitespace in descriptions is ASCII_SP. */
static const struct {
char *desc;
char *name;
int status;
}sysexits[] = {
/* sysexit descriptions copied from FreeBSD's sysexits(3). */
"All is well.", /* except this one */
"EX_OK", 0,
"The command was used incorrectly, e.g., with the wrong number of"
" arguments, a bad flag, a bad syntax in a parameter, or whatever.",
"EX_USAGE", 64,
"The input data was incorrect in some way. This should only be used"
" for user's data and not system files.",
"EX_DATAERR", 65,
"An input file (not a system file) did not exist or was not readable."
" This could also include errors like \"No message\" to a mailer (if it"
" cared to catch it).",
"EX_NOINPUT", 66,
"The user specified did not exist. This might be used for mail"
" addresses or remote logins.",
"EX_NOUSER", 67,
"The host specified did not exist. This is used in mail addresses or"
" network requests.",
"EX_NOHOST", 68,
"A service is unavailable. This can occur if a support program or"
" file does not exist. This can also be used as a catchall message"
" when something you wanted to do does not work, but you do not know"
" why.",
"EX_UNAVAILABLE", 69,
"An internal software error has been detected. This should be limited"
" to non-operating system related errors as possible.",
"EX_SOFTWARE", 70,
"An operating system error has been detected. This is intended to be"
" used for such things as \"cannot fork\", \"cannot create pipe\", or"
" the like. It includes things like getuid returning a user that does"
" not exist in the passwd file.",
"EX_OSERR", 71,
"Some system file (e.g., /etc/passwd, /var/run/utx.active, etc.) does"
" not exist, cannot be opened, or has some sort of error (e.g., syntax"
" error).",
"EX_OSFILE", 72,
"A (user specified) output file cannot be created.",
"EX_CANTCREAT", 73,
"An error occurred while doing I/O on some file.",
"EX_IOERR", 74,
"Temporary failure, indicating something that is not really an error."
" In sendmail, this means that a mailer (e.g.) could not create a"
" connection, and the request should be reattempted later.",
"EX_TEMPFAIL", 75,
"The remote system returned something that was \"not possible\" during a"
" protocol exchange.",
"EX_PROTOCOL", 76,
"You did not have sufficient permission to perform the operation."
" This is not intended for file system problems, which should use"
" EX_NOINPUT or EX_CANTCREAT, but rather for higher level"
" permissions.",
"EX_NOPERM", 77,
"Something was found in an unconfigured or misconfigured state.",
"EX_CONFIG", 78
};
static const char header_suffix[] =
"};\n"
"#endif /* ifndef _SYSEXITS_H */\n"
;
static size_t i;
/*
static int findbyint(int status){
for(i = 0; i < ARRAYLEN(sysexits); ++i)
if(sysexits[i].status == status)
return i;
}
*/
static void output_comment(int fd, int indentation, int width, char *comment){
size_t word_start;
size_t line_start;
return;
for(i = 0, line_start = 0, word_start = 0; ; ++i)
switch(comment[i]){
case '\0':
while( i - line_start
+ ARRAYLEN(comment_suffix)
+ indentation * TAB_WIDTH
> width){
i = word_start - 2; /* - current char, - space */
write(
fd, comment_prefix_ongoing,
ARRAYLEN(comment_prefix_ongoing)
);
write(fd, comment + line_start, i - line_start);
i += 2; /* + space, + next word char */
write(fd, "\n", 1);
}
write(fd, comment + line_start, i - line_start);
write(fd, comment_suffix, ARRAYLEN(comment_suffix));
break;
}
return;
}
static void output_header(void){
write(1, header_prefix, ARRAYLEN(header_prefix) - 1);
for(i = 0; i < ARRAYLEN(sysexits); ++i){
output_comment(1, 1 * TAB_WIDTH, 80, sysexits[i].desc);
#ifdef ENUM
fprintf(stdout, "\t%s = %d%s",
sysexits[i].name,
sysexits[i].status,
i < ARRAYLEN(sysexits) - 1 ? ",\n" : "\n"
);
#endif /* ifdef ENUM */
#ifdef DEFINE
fprintf(stdout, "#\tdefine %s %d\n",
sysexits[i].name,
sysexits[i].status
);
#endif /* ifdef DEFINE */
}
write(1, header_suffix, ARRAYLEN(header_suffix) - 1);
}
int main(int argc, char *argv[]){
if(argv[0] == NULL){
argv[0] = program_name;
++argc;
}
output_header();
return 0;
}

152
src/tail.c Normal file
View File

@ -0,0 +1,152 @@
#include <ctype.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "args.h"
#include "libcharin.h"
#include "libstris.h"
#define DEFAULT_LENGTH 10
#define OFLOW(STR) (STR[strlen(STR)-1] != '\n')
#ifndef PAGE_SIZE
# ifndef PAGESIZE
# define PAGE_SIZE 1024
# else
# define PAGE_SIZE PAGESIZE
# endif
#endif
/*
TODO
* fix limits.h stuff; if limits.h isn't on the system define
reasonable limits in its place
* don't use unsafe string functions
* fix bugs
*/
void
tail(FILE *input, FILE *output, int length){
bool loop = 1;
char *lines[length];
size_t ls[length];
int i;
int filelines = 0;
size_t offset = 0;
for(i = 0; i < length; i++){
ls[i] = PAGE_SIZE;
lines[i] = (char *)calloc(ls[i], sizeof(char));
}
i = 0;
while(fgets(lines[i] + offset, ls[i], input)){
if(OFLOW(lines[i])){
offset += ls[i] - 1;
ls[i] += PAGE_SIZE;
/* should be more resilient */
if(realloc(lines[i], ls[i]) == NULL){
fprintf(stderr, "tail: Couldn't re-allocate memory (%d bytes)\n", ls[i] * sizeof(char));
exit(1);
}
continue;
}
offset = 0;
++filelines;
i = (i + 1) % length;
}
for(i = filelines % length; loop || i != filelines % length; i = (i + 1) % length){
if(loop && i == filelines % length)
loop = 0;
printf("%s", lines[i]);
}
/* glitch here when `tail`ing normal files */
for(i = 0; i < length; i++){
free(lines[i]);
}
return;
}
void
usage(char *name){
fprintf(stderr, "Usage: %s [-ht] [-n integer] [file...]\n", name);
exit(1);
}
int
main(int argc, char *argv[]){
char **argsv = getargsv(argc, argv);
char *flags = getflags(argc, argv);
char mode[3] = "rb";
char filepath[NAME_MAX+PATH_MAX];
char *lengthstr;
size_t fplen = NAME_MAX+PATH_MAX;
FILE *input;
FILE *output = stdout;
int argsc = getargsc(argc, argv);
int i;
int length = DEFAULT_LENGTH;
size_t s;
if(charin(flags, 'h'))
usage(argv[0]);
if(charin(flags, 'n')){
lengthstr = getflag(argc, argv, 'n');
if(!strisint(lengthstr) || *lengthstr == '\0')
usage(argv[0]);
else
length = atoi(lengthstr);
}
if(charin(flags, 't'))
/* Explicitly open files as text rather than as binary.
* this changes "rb" to "r". Should have no effect on
* POSIX systems but may change CRLF to just LF with
* MS Windows. I don't know why anyone would need this
* but it's here for those that do. */
mode[1] = '\0';
if(argsc == 0)
tail(stdin, output, length);
else
for(i = 1; i < argsc; ++i){
if(i > 1)
fprintf(output, "\n");
if(!strcmp(argv[i], "-")){
realpath(argv[i], filepath);
input = fopen(argv[i], "r");
if(input == NULL){
fprintf(stderr, "%s: %s: Could not open file for reading\n", argv[0], filepath);
exit(1);
}
}else{
input = stdin;
/* probably safe but could be vulnerable */
/* just making filepath[] = "<stdin>" */
s = strlen("<stdin>") + 1; /* minimum size for filepath */
if(fplen >= s)
strcpy(filepath, "<stdin>");
else if(realloc(filepath, s) == NULL)
fprintf(stderr, "%s: Failed to reallocate path string, using <stdin>\n", argv[0]);
}
fprintf(output, "==> %s <==\n", filepath);
tail(input, output, length);
if(input != stdin && fclose(input) != 0){
fprintf(stderr, "%s: %s: Could not close file. Exiting...\n", argv[0], filepath);
exit(1);
}
}
return 0;
}

97
src/which.c Normal file
View File

@ -0,0 +1,97 @@
#include <errno.h>
#include <stdbool.h>
#include <stdio.h> /* fprintf(3) */
#include <stdlib.h> /* exit(3) */
#include <sysexits.h>
#include <unistd.h> /* access(2), chdir(2) */
#include "libshell.h" /* getpaths(3) */
void
usage(char *name){
fprintf(stderr, "Usage: %s (-a) [name]\n", name);
exit(EX_USAGE);
}
int
which(char *program, char **paths, char *name, bool breakonfirst){
char **q;
int retval;
retval = 0;
for(q = paths; *q != NULL; ++q){
if(chdir(*q) != 0)
switch(retval = errno){
/* Non-issues in the context of this program */
case EACCES: /* Access denied */
case ELOOP: /* Too many symlinks in path */
case ENAMETOOLONG: /* Name too long */
case ENOENT: /* Directory doesn't exist */
case ENOTDIR: /* Not a directory */
continue;
case EFAULT:
goto apology;
case EIO:
goto ioerr;
}
/* Changed dir, now check for file in dir */
if(access(program, R_OK | X_OK) != 0)
switch(retval = errno){
case EACCES:
case ELOOP:
case ENAMETOOLONG:
case ENOENT:
case EROFS:
continue;
case EFAULT:
goto apology;
case EIO:
goto ioerr;
}
else{
fprintf(stdout, "%s%s\n", *q, program);
if(breakonfirst != 0)
break;
}
}
end:
return retval;
apology:
fprintf(stderr,
/* EFAULT means the pointer passed to either chdir(2) or access(2) was outside
* of the address space available to the program. This means something's Very
* Wrong with getpaths(3) and libshell has to be fixed. That'd be a serious,
* common error, so try to reassure the user and solicit a bug report. */
"%s: EFAULT\n"
"You the user did nothing to invoke this error message. This is an error in\n"
"which, a program you or another program executed. PLEASE e-mail the creator\n"
"of this program (the address can be found in the corresponding manual page)\n"
"and tell them you got this error and what you were doing when it happened.\n"
"Sorry!\n", name);
goto end;
ioerr:
fprintf(stderr, "%s: filesystem I/O error\n", name);
goto end;
}
int main(int argc, char *argv[]){
int i;
char **paths;
int retval;
if(argc <= 1)
usage(argv[0]);
if((paths = getpaths()) == NULL){
fprintf(stderr, "%s: Could not get the value of $PATH\n", argv[0]);
exit(1);
}
for(i = 1; i < argc; ++i)
if((retval = which(argv[i], paths, argv[0], 1)) != 0)
break;
free(*paths);
free(paths);
return retval;
}

113
src/xml.c Normal file
View File

@ -0,0 +1,113 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arraylen.h"
#include "bool.h"
#define TAGSTART '<'
#define TAGEND '>'
#define ETAG '/'
#define LF '\n'
/*
start-tag end-tag
| |
V V
<TAG>CONTENT</TAG>
^ ^ ^
| | |
| TAGEND ETAG
|
TAGSTART
empty-element tag
|
| optional whitespace
| |
| | TAGEND
| | |
V V V
<TAG />
^ ^
| |
| ETAG
|
TAGSTART
*/
/* a lot of this program's design is weird and kind of overengineered.
this is to drastically limit the memory used. */
enum state{
error = -1, /* for future use */
init = 0, /* waiting for a root tag */
starttagreading,
tagreading,
eetagreading, /* throw away everything until state change */
endtagreading,
contentreading /* printing */
};
void
usage(char *name){
fprintf(stdout, "Usage: %s [tag...]\n", name);
return;
}
int main(int argc, char *argv[]){
if(argc < 2){
usage(argv[0]);
return 0;
}
bool enable = 0;
char c;
int linenumber = 0;
enum state s = init;
FILE *input = stdin;
FILE *output = stdout;
int level = 1; /* 1-indexed because args start at 1 */
size_t p = 0;
while(c = getc(input) != EOF){
if(c == LF){
++linenumber;
continue;
}
switch(s){
case init:
if(c == TAGSTART)
s = starttagreading;
break;
case starttagreading:
if(c == ETAG)
s = endtagreading;
else if(!isblank(c))
s = tagreading;
break;
case tagreading:
if(c == ETAG)
s = eetagreading;
break;
/* case eetagreading: */
case endtagreading:
if(c == ETAG)
fprintf(stderr, "%s:%d: Stray \'%c\' in an end tag?\n", argv[0], linenumber, ETAG);
break;
case contentreading:
if(enable)
putc(c, output);
else if(c == TAGSTART)
s = starttagreading;
else if(c == TAGEND)
fprintf(stderr, "%s:%d: Possible state error or XML syntax error; \'%c\' in mode contentreading\n", argv[0], linenumber, TAGEND);
break;
}
}
return 0;
}