diff --git a/LICENSE b/LICENSE index a0ea720..396072b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Adam McKenney +Copyright (c) 2024 Adam McKenney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/slimcat.1 b/slimcat.1 index 72ccf59..1eb7116 100644 --- a/slimcat.1 +++ b/slimcat.1 @@ -17,7 +17,7 @@ slimcat -h Displays help. .TP slimcat -u -Tells the OS that we dont want a buffer (warning may be a lot slower with a large file). +Tells the OS that we do not want a buffer (warning may be a lot slower with a large file). .TP slimcat x \- y Output x's contents, then standard input, then y's contents. @@ -46,7 +46,7 @@ Written by Adam McKenney .SH "REPORTING BUGS" See the offical repo: .SH COPYRIGHT -Copyright \(co 2023 Adam McKenney +Copyright \(co 2024 Adam McKenney Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/cat.c b/src/cat.c index 11f8bbe..ffea0ec 100644 --- a/src/cat.c +++ b/src/cat.c @@ -1,42 +1,53 @@ -/* Copyright 2023 (c) Adam McKenney - See LICENSE for details */ +/* Copyright 2024 (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 //malloc -#include //err, errx -#include //EX_OK, EX_OSERR, EX_IOERR, EX_NOINPUT -#include //read, write, close -#include //stat -#include //stat +#include //malloc +#include //err, errx +#include //EX_OK, EX_OSERR, EX_IOERR, EX_NOINPUT +#include //read, write, close +#include //stat +#include //stat #include //sendfile -#include //open, STDIN_FILENO, STDOUT_FILENO +#include //open, STDIN_FILENO, STDOUT_FILENO -#define NAME "cat" //Program name, can be changed so a user can keep their bloated version of cat +#define NAME "cat" //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 about 60 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 #define MAX_SENDFILE_LINUX 2147479552 //max file size for sendfile in Linux -void cat(const int fd, const char no_buf, size_t size){ +void cat(const int fd, const char no_buf, size_t size, const size_t files_pending){ /* reads from the file descriptor into buf, until zero bytes are read */ //tries sendfile if available (since it is faster, if not, do sys read & write) if(fd == STDIN_FILENO || size > MAX_SENDFILE_LINUX || sendfile(STDOUT_FILENO, fd, 0, size) != size){ + //if we cannot use sendfile, use the code below size_t r = 0; if(no_buf) size = 1; - void *buf = (void*)malloc(size); + static void *buf = NULL; + static size_t buf_size = 0; + if(buf_size < size){ + buf = realloc(buf, size); //reuse buffer if we can, if buf starts as null then it will malloc automatically + buf_size = size; + } if(buf == NULL) err(EX_OSERR, "while allocating buf in cat()"); + while((r=read(fd, buf, size)) > 0) if(write(STDOUT_FILENO, buf, r) != r) err(EX_IOERR, "while writing to fd in cat(). %ld bytes were written", r); //if writing stops for some reason if(r == -1) err(EX_IOERR, "while reading fd in cat()."); //if read(2) throws an error - free(buf); + if(files_pending == 1){ + //allows us to reuse the buffer until we are at the last file + free(buf); + buf_size = 0; + } } } -void get_file(const char* file_name, const char no_buf){ +void get_file(const char* file_name, const char no_buf, const size_t files_pending){ /* tries to open the user's file, then calls cat() with its file pointer */ - if(file_name[0] == '-' && file_name[1] == '\0'){ cat(STDIN_FILENO, no_buf, BUFF_SIZE); return;} + if(file_name[0] == '-' && file_name[1] == '\0'){ cat(STDIN_FILENO, no_buf, BUFF_SIZE, files_pending); return;} struct stat buffer; int fd = -1, status = stat(file_name, &buffer); if(status != 0) err(EX_NOINPUT, "while checking file '%s' in get_file()", file_name); fd = open(file_name, O_RDONLY); if(fd == -1) err(EX_IOERR, "while opening file '%s' in get_file()", file_name); - cat(fd, no_buf, buffer.st_size); + cat(fd, no_buf, buffer.st_size, files_pending); close(fd); } int main(int argc, char *argv[]){ @@ -44,13 +55,13 @@ int main(int argc, char *argv[]){ char no_buf = 0; //-u option for(i=1; i <= argc; i++){ //step through all the vars, this is more readable than pointer arythmatic if(argv[1] == NULL) - cat(STDIN_FILENO, no_buf, BUFF_SIZE); + cat(STDIN_FILENO, no_buf, BUFF_SIZE, argc-i); else if(argv[i][0] == '-' && argv[i][1] != '\0'){ //if its a command line arg if(argv[i][1] == 'h') errx(EX_OK, "\n%s\n", HELP); else if(argv[i][1] == 'u') no_buf = 1; //required by POSIX, turns off buffered output else errx(EX_USAGE, "while reading command line arg %ld in main(): unknown command line arg '%s'\n", i, argv[i]); } else { - for( ; i < argc; i++) get_file(argv[i], no_buf); //step through each arg as if it's a file, even if its a - + for( ; i < argc; i++) get_file(argv[i], no_buf, argc-i); //step through each arg as if it's a file, even if its a - } } return EX_OK;