diff --git a/slimcat.1 b/slimcat.1 index e904a18..06b253f 100644 --- a/slimcat.1 +++ b/slimcat.1 @@ -1,4 +1,4 @@ -.TH SLIMCAT "1" "August 2020" "SLIM CAT 1.1.0" "User Commands" +.TH SLIMCAT "1" "August 2020" "SLIM CAT 1.2.0" "User Commands" .SH NAME slimcat \- still concatenating files and printing on the standard output, just slimmer. .SH SYNOPSIS @@ -8,7 +8,7 @@ slimcat \- still concatenating files and printing on the standard output, just s .PP This is a rewrite of GNU cat where the unused features are removed. The only option is -h for help and -u for unbuffered output. .PP -We concatenate files(s) to standard output with less than 50 lines of code. +We concatenate files(s) to standard output with less than 60 lines of code. .PP With no file, or when file is \-, read standard input. .SH EXAMPLES @@ -37,6 +37,10 @@ Unknown command line argument. .TP 3 Error while writing. +.TP +4 +Error while allocating memory. + .SH AUTHOR Written by Adam McKenney .SH "REPORTING BUGS" diff --git a/src/cat.c b/src/cat.c index b3b0cdf..aa9aa9b 100644 --- a/src/cat.c +++ b/src/cat.c @@ -3,36 +3,46 @@ #include //fprintf, perror #include //exit #include //read, write, close -#include //open +#include //stat +#include //stat +#include //sendfile +#include //open, STDIN_FILENO, STDOUT_FILENO #define NAME "slimcat" //Program name, can be changed so a user can keep their bloated version of cat #define HELP "Usage: " NAME " [-hu] [file...]\nWe concatenate files(s) to standard output with less than 50 lines of code.\n\nWith no file, or when file is -, read standard input.\nExamples:\n " NAME " -h Displays the help\n " NAME " -u Tells the OS that we dont want a buffer (warning may be a lot slower with a large file).\n " NAME " x - y Output x's contents, then standard input, then y's contents.\n " NAME " Copy standard input to standard output." #define BUFF_SIZE 10000 //a larger buffer trades memory for speed -void throw_error(char* extra_msg, int exit_code){ +void throw_error(const char* extra_msg, const int exit_code){ /* function to writes the program name, optionally extra info, then the error to stderr */ if(extra_msg != NULL) fprintf(stderr, "%s: %s: ", NAME, extra_msg); else fprintf(stderr, "%s: ", NAME); perror(""); if(exit_code) exit(exit_code); //allows non-fatal errors to keep going } -void cat(int fd, char no_buf){ +void cat(const int fd, const char no_buf, size_t size){ /* reads from the file descriptor into buf, until zero bytes are read */ - char buf[BUFF_SIZE]; - size_t r = 0, size = BUFF_SIZE; - if(no_buf) size = sizeof(char); - while((r=read(fd, buf, size)) > 0) - if(write(1, buf, r) != r) throw_error(NULL, 3); //if writing stops for some reason - if(r == -1) throw_error(NULL, 1); //if read(2) throws an error + if(fd == STDIN_FILENO || size > 2147479552 /* max file size for sendfile in Linux */ ){ + size_t r = 0; + if(no_buf) size = 1; + void *buf = malloc(size); + if(buf == NULL) throw_error("Buffer Allocation", 4); + while((r=read(fd, buf, size)) > 0) + if(write(STDOUT_FILENO, buf, r) != r) throw_error(NULL, 3); //if writing stops for some reason + if(r == -1) throw_error(NULL, 1); //if read(2) throws an error + free(buf); + } else //if we are given a file, use the sendfile syscall for better speed + if(sendfile(STDOUT_FILENO, fd, 0, size) != size) throw_error("In sendfile", 1); } -int get_file(char* file_name, char no_buf){ +void get_file(const char* file_name, const char no_buf){ /* tries to open the user's file, then calls cat() with its file pointer */ - int fd = -1; + if(file_name[0] == '-' && file_name[1] == '\0'){ cat(STDIN_FILENO, no_buf, BUFF_SIZE); return;} + struct stat buffer; + int fd = -1, status = stat(file_name, &buffer); + if(status != 0) throw_error(file_name, 1); fd = open(file_name, O_RDONLY); if(fd == -1) throw_error(file_name, 1); - cat(fd, no_buf); + cat(fd, no_buf, buffer.st_size); close(fd); - return 0; } int main(int argc, char *argv[]){ size_t i = 0; @@ -42,10 +52,10 @@ int main(int argc, char *argv[]){ if(argv[i][1] == 'h') { puts(HELP); return 0; } else if(argv[i][1] == 'u') no_buf = 1; //required by POSIX, turns off buffered output else { fprintf(stderr, "%s: unknown command line arg '%s'\n", NAME, argv[i]); return 2;} - } else break; + } else { + if(argv[i] == NULL || argv[i][0] == '-') cat(STDIN_FILENO, no_buf, BUFF_SIZE); //if no args + else for( ; i < argc; i++) get_file(argv[i], no_buf); //step through each arg as if it's a file + } } - if(argv[i] == NULL) cat(STDIN_FILENO, no_buf); //if no args - else for( ; i < argc; i++) get_file(argv[i], no_buf); //step through each arg as if it's a file return 0; } -