2023-04-27 18:38:16 +00:00
|
|
|
#include "../command.h"
|
|
|
|
|
|
|
|
#include <ctype.h>
|
2023-04-28 19:13:10 +00:00
|
|
|
#include <errno.h>
|
2023-04-27 18:38:16 +00:00
|
|
|
#include <string.h>
|
2023-04-28 19:13:10 +00:00
|
|
|
#include <sys/stat.h>
|
2023-04-27 18:38:16 +00:00
|
|
|
|
|
|
|
struct Flags {
|
|
|
|
bool number_lines;
|
|
|
|
bool number_non_empty;
|
|
|
|
bool change_non_print;
|
|
|
|
bool change_tabs;
|
|
|
|
bool end_lines_dollar;
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void help() {
|
|
|
|
printf("Usage: cat [-nbvteA] [FILE]...\n\n");
|
|
|
|
printf("Print FILEs to stdout\n\n");
|
2023-04-27 23:13:40 +00:00
|
|
|
printf("-n Number output lines\n");
|
|
|
|
printf("-b Number nonempty lines\n");
|
|
|
|
printf("-v Show nonprinting characters as ^x or M-x\n");
|
|
|
|
printf("-t ...and tabs as ^I\n");
|
|
|
|
printf("-e ...and end lines with $\n");
|
|
|
|
printf("-A Same as -vte\n");
|
2023-04-27 18:38:16 +00:00
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cat_file(FILE* file, struct Flags flags) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
COMMAND(cat) {
|
|
|
|
|
|
|
|
struct Flags flags;
|
|
|
|
flags.number_lines = false;
|
|
|
|
flags.number_non_empty = false;
|
|
|
|
flags.change_non_print = false;
|
|
|
|
flags.change_tabs = false;
|
|
|
|
flags.end_lines_dollar = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
if (!prefix("-", argv[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streql("--help", argv[i])) {
|
|
|
|
help();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t len = strlen(argv[i] + 1);
|
|
|
|
for (size_t j = 0; j < len; j++) {
|
|
|
|
char c = argv[i][j + 1];
|
|
|
|
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:
|
|
|
|
error("error: unkown flag -%c", c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool files = false;
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
if (streql("-", argv[i])) {
|
|
|
|
files = true;
|
|
|
|
cat_file(stdin, flags);
|
|
|
|
} else if (prefix("-", argv[i])) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
files = true;
|
2023-04-28 19:13:10 +00:00
|
|
|
struct stat s;
|
|
|
|
if (stat(argv[i], &s) < 0) {
|
|
|
|
printf("error: failed to read %s: %s\n", argv[i], strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!S_ISREG(s.st_mode)) {
|
|
|
|
printf("error: %s is not a file\n", argv[i]);
|
|
|
|
continue;
|
|
|
|
}
|
2023-04-27 18:38:16 +00:00
|
|
|
FILE* in = get_file(argv[i], "r");
|
|
|
|
cat_file(in, flags);
|
|
|
|
fclose(in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!files) {
|
|
|
|
cat_file(stdin, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|