168 lines
4.7 KiB
C
168 lines
4.7 KiB
C
/**
|
|
* file: cat.c
|
|
* command: cat
|
|
* author: Tyler Murphy
|
|
*/
|
|
|
|
#include "command.h"
|
|
#include "lslib.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
/**
|
|
* Flags that are to be used with cat
|
|
*/
|
|
static struct {
|
|
bool number_lines; /* if to number the lines */
|
|
bool number_non_empty; /* if to number empty lines */
|
|
bool change_non_print; /* if to change non printable characters to be printable */
|
|
bool change_tabs; /* if to change tabs to ^I */
|
|
bool end_lines_dollar; /* if to print a $ at the end of lines */
|
|
} flags;
|
|
|
|
/**
|
|
* Help function for cat
|
|
*/
|
|
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");
|
|
}
|
|
|
|
/**
|
|
* Give a file pointer, cat all its contents
|
|
* @param file the file to cat
|
|
*/
|
|
static void cat_file(FILE* file) {
|
|
char c; /* current character read */
|
|
size_t read; /* amount read */
|
|
|
|
/* default arguments */
|
|
size_t line = 1; /* what line we are on, 1 indexed */
|
|
bool empty = true; /* if the current line is empty (nothing printed) */
|
|
bool newline = true; /* if we are on a new line */
|
|
|
|
/* read file a character at a time */
|
|
while ((read = fread(&c, 1, 1, file)) != 0) {
|
|
/* if its a new line, update the new line pointer */
|
|
if (c == '\n') {
|
|
if (empty && flags.number_lines) { /* print line number if set to */
|
|
printf("\t%ld ", line);
|
|
}
|
|
if (flags.end_lines_dollar) { /* pint $ if set to */
|
|
printf("$");
|
|
}
|
|
line++;
|
|
newline = true; /* tell that the new line number is set to be printed */
|
|
empty = true; /* set line to empty */
|
|
goto print;
|
|
} else {
|
|
empty = false;
|
|
}
|
|
|
|
/* skip next check if not newline */
|
|
if (!newline) {
|
|
goto print;
|
|
}
|
|
|
|
/* if on a new line and is a valid char, print line number */
|
|
if (!empty && (flags.number_non_empty || flags.number_lines)) {
|
|
printf("\t%ld ", line);
|
|
newline = false;
|
|
}
|
|
print:
|
|
/* print character */
|
|
if (!flags.change_non_print || printable_char(c)) {
|
|
if (flags.change_tabs && c == '\t') {
|
|
fwrite("^I", 1, 2, stdout); /* print ^I instead of tab */
|
|
} else {
|
|
fwrite(&c, 1, 1, stdout); /* print character to stdout */
|
|
}
|
|
} else { /* if set to change non print, fix char code */
|
|
c |= '@';
|
|
fwrite(&c, 1, 1, stdout);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Takes in each argument that has a single - and parses it
|
|
* @param c the character after the -
|
|
* @param next the next argument in argv that hasnt been parsed
|
|
* @reutrn if the next arg was used or if the arg was invalid
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Output a files contents
|
|
*/
|
|
COMMAND(cat_main) {
|
|
|
|
int start;
|
|
int arg_len;
|
|
int i;
|
|
|
|
/* set flag defaults */
|
|
flags.number_lines = false;
|
|
flags.number_non_empty = false;
|
|
flags.change_non_print = false;
|
|
flags.change_tabs = false;
|
|
flags.end_lines_dollar = false;
|
|
|
|
/* parse user arguments */
|
|
start = parse_args(argc, argv, help, short_arg, NULL);
|
|
|
|
/* get the number of arguments to cat */
|
|
arg_len = argc - start;
|
|
|
|
/* if not read from stdin */
|
|
if (arg_len < 1) {
|
|
cat_file(stdin);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* foreach file, try to open it, and then cat it */
|
|
for (i = start; i < argc; i++) {
|
|
FILE* in = get_file(argv[i], "r"); /* open file as read */
|
|
cat_file(in); /* print it out */
|
|
if (in != stdin)
|
|
fclose(in);
|
|
}
|
|
|
|
return EXIT_SUCCESS; /* success */
|
|
}
|