-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remade cat using more system calls for better speed
- Loading branch information
Showing
1 changed file
with
28 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,51 @@ | ||
/* 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 <stdio.h> //fgetc, putchar, FILE, fopen, perror, fclose, puts, fprintf | ||
#include <stdio.h> //fprintf, perror | ||
#include <stdlib.h> //exit | ||
#include <unistd.h> //read, write, close | ||
#include <fcntl.h> //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; } | ||
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; | ||
} | ||
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; | ||
} | ||
|