From fd76bea5756442b7fb1454b85f6c1d14695fe8b9 Mon Sep 17 00:00:00 2001 From: mckenney5 Date: Wed, 2 Jun 2021 00:21:27 -0400 Subject: [PATCH] Remade cat using more system calls for better speed --- src/cat.c | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/cat.c b/src/cat.c index f6a4855..b3b0cdf 100644 --- a/src/cat.c +++ b/src/cat.c @@ -1,41 +1,42 @@ -/* Copyright 2020 (c) Adam McKenney - See LICENSE for details */ +/* Copyright 2021 (c) Adam McKenney - See LICENSE for details */ /* A way slimmer version of cat without the GNU bloat features that I have never used nor needed */ -#include //fgetc, putchar, FILE, fopen, perror, fclose, puts, fprintf +#include //fprintf, perror +#include //exit +#include //read, write, close +#include //open -#define NAME "slimcat" //Program name, can be changed +#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 -static char no_buf = 0; //-u option - -void throw_error(const char* extra_msg){ +void throw_error(char* extra_msg, 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(FILE *fp){ -/* loops through the file and puts the data one byte at a time into stdout until fread() returns 0 */ - char p = 0; - if(no_buf) if(setvbuf(fp, &p, _IONBF, 1)) throw_error(NULL); //tell the OS we dont want a buffer - do - if(!fread(&p, 1, 1, fp) || !fwrite(&p, 1, 1, stdout)) break; //if we were unable to read or write, we are done. Size must be 1 for binary files - while(1); +void cat(int fd, char no_buf){ +/* 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 } -int get_file(const char* file_name){ +int get_file(char* file_name, char no_buf){ /* tries to open the user's file, then calls cat() with its file pointer */ - FILE *fp = NULL; - if(file_name[0] == '-' && file_name[1] == '\0') fp = stdin; //if the user did 'cat -' or 'cat foo - bar' - else fp = fopen(file_name, "r"); - - if(fp == NULL){ throw_error(file_name); return 1; } - - cat(fp); - fclose(fp); + int fd = -1; + fd = open(file_name, O_RDONLY); + if(fd == -1) throw_error(file_name, 1); + cat(fd, no_buf); + close(fd); return 0; } int main(int argc, char *argv[]){ -/* takes the args -h for help and -u for unbuffered output */ - size_t i; + size_t i = 0; + char no_buf = 0; //-u option for(i=1; i < argc; i++){ //step through all the vars if(argv[i][0] == '-' && argv[i][1] != '\0'){ //if its a command line arg if(argv[i][1] == 'h') { puts(HELP); return 0; } @@ -43,7 +44,8 @@ int main(int argc, char *argv[]){ else { fprintf(stderr, "%s: unknown command line arg '%s'\n", NAME, argv[i]); return 2;} } else break; } - if(argv[i] == NULL) cat(stdin); //if no args - else for( ; i < argc; i++) get_file(argv[i]); //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; } +