utilities/src
This commit is contained in:
parent
7dcc2ecac8
commit
293436c5ad
121
src/calculate.c
Normal file
121
src/calculate.c
Normal 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
67
src/cut.c
Normal 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
20
src/echo.c
Normal 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
1
src/false.c
Normal file
@ -0,0 +1 @@
|
||||
int main() { return 1; }
|
12
src/head.c
Normal file
12
src/head.c
Normal 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
66
src/id.c
Normal 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
47
src/levenshtein.c
Normal 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
73
src/lowercase.c
Normal 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
95
src/mm.c
Normal 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
3
src/nonzero.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main(int argc, char **argv){
|
||||
return !argc;
|
||||
}
|
52
src/prioritize.c
Normal file
52
src/prioritize.c
Normal 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
24
src/retval.c
Normal 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
120
src/runlength.c
Normal 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
19
src/simexec.c
Normal 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
21
src/sleep.c
Normal 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
25
src/streq.c
Normal 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
50
src/stris.c
Normal 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
112
src/substitute.c
Normal 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
183
src/sysexits.c
Normal 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
152
src/tail.c
Normal 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
97
src/which.c
Normal 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
113
src/xml.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user