lazysphere/src/commands/cat.c
2023-05-04 16:10:37 -04:00

140 lines
3.2 KiB
C

#include "../command.h"
#include <ctype.h>
static struct {
bool number_lines;
bool number_non_empty;
bool change_non_print;
bool change_tabs;
bool end_lines_dollar;
} flags;
static bool printable(char c) {
switch (c) {
case '\n': return true;
case '\r': return true;
case '\b': return true;
case '\t': return true;
default: return isprint(c) == 0;
}
}
static void help(void) {
printf("Usage: cat [-nbvteA] [FILE]...\n\n");
printf("Print FILEs to stdout\n\n");
printf("\t-n Number output lines\n");
printf("\t-b Number nonempty lines\n");
printf("\t-v Show nonprinting characters as ^x or M-x\n");
printf("\t-t ...and tabs as ^I\n");
printf("\t-e ...and end lines with $\n");
printf("\t-A Same as -vte\n");
}
static void cat_file(FILE* file) {
char c;
size_t read;
size_t line = 1;
bool empty = true;
bool newline = true;
while ((read = fread(&c, 1, 1, file)) != 0) {
if (c == '\n') {
if (empty && flags.number_lines) {
printf("\t%ld ", line);
}
if (flags.end_lines_dollar) {
printf("$");
}
line++;
newline = true;
empty = true;
goto print;
} else {
empty = false;
}
if (!newline) {
goto print;
}
if (!empty && (flags.number_non_empty || flags.number_lines)) {
printf("\t%ld ", line);
newline = false;
}
print:
if (!flags.change_non_print || printable(c)) {
if (flags.change_tabs && c == '\t') {
fwrite("^I", 1, 2, stdout);
} else {
fwrite(&c, 1, 1, stdout);
}
} else {
c |= '@';
fwrite(&c, 1, 1, stdout);
}
}
}
static int short_arg(char c, char* next) {
UNUSED(next);
switch (c) {
case 'n':
flags.number_lines = true;
break;
case 'b':
flags.number_non_empty = true;
break;
case 'v':
flags.change_non_print = true;
break;
case 't':
flags.change_non_print = true;
flags.change_tabs = true;
break;
case 'e':
flags.change_non_print = true;
flags.end_lines_dollar = true;
break;
case 'A':
flags.change_non_print = true;
flags.change_tabs = true;
flags.end_lines_dollar = true;
break;
default:
return ARG_INVALID;
}
return ARG_UNUSED;
}
COMMAND(cat) {
int start;
int arg_len;
int i;
flags.number_lines = false;
flags.number_non_empty = false;
flags.change_non_print = false;
flags.change_tabs = false;
flags.end_lines_dollar = false;
start = parse_args(argc, argv, help, short_arg, NULL);
arg_len = argc - start;
if (arg_len < 1) {
cat_file(stdin);
return EXIT_SUCCESS;
}
for (i = start; i < argc; i++) {
FILE* in = get_file(argv[i], "r");
cat_file(in);
if (in != stdin)
fclose(in);
}
return EXIT_SUCCESS;
}