lazysphere/command/cat.c

167 lines
4.6 KiB
C

/**
* file: cat.c
* 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 */
}