#include #include #include #include #include #include #include #include "args.h" #include "libcharin.h" #include "libstris.h" #define DEFAULT_LENGTH 10 #define OFLOW(STR) (STR[strlen(STR)-1] != '\n') #ifndef PAGE_SIZE # ifndef PAGESIZE # define PAGE_SIZE 1024 # else # define PAGE_SIZE PAGESIZE # endif #endif /* TODO * fix limits.h stuff; if limits.h isn't on the system define reasonable limits in its place * don't use unsafe string functions * fix bugs */ void tail(FILE *input, FILE *output, int length){ bool loop = 1; char *lines[length]; size_t ls[length]; int i; int filelines = 0; size_t offset = 0; for(i = 0; i < length; i++){ ls[i] = PAGE_SIZE; lines[i] = (char *)calloc(ls[i], sizeof(char)); } i = 0; while(fgets(lines[i] + offset, ls[i], input)){ if(OFLOW(lines[i])){ offset += ls[i] - 1; ls[i] += PAGE_SIZE; /* should be more resilient */ if(realloc(lines[i], ls[i]) == NULL){ fprintf(stderr, "tail: Couldn't re-allocate memory (%d bytes)\n", ls[i] * sizeof(char)); exit(1); } continue; } offset = 0; ++filelines; i = (i + 1) % length; } for(i = filelines % length; loop || i != filelines % length; i = (i + 1) % length){ if(loop && i == filelines % length) loop = 0; printf("%s", lines[i]); } /* glitch here when `tail`ing normal files */ for(i = 0; i < length; i++){ free(lines[i]); } return; } void usage(char *name){ fprintf(stderr, "Usage: %s [-ht] [-n integer] [file...]\n", name); exit(1); } int main(int argc, char *argv[]){ char **argsv = getargsv(argc, argv); char *flags = getflags(argc, argv); char mode[3] = "rb"; char filepath[NAME_MAX+PATH_MAX]; char *lengthstr; size_t fplen = NAME_MAX+PATH_MAX; FILE *input; FILE *output = stdout; int argsc = getargsc(argc, argv); int i; int length = DEFAULT_LENGTH; size_t s; if(charin(flags, 'h')) usage(argv[0]); if(charin(flags, 'n')){ lengthstr = getflag(argc, argv, 'n'); if(!strisint(lengthstr) || *lengthstr == '\0') usage(argv[0]); else length = atoi(lengthstr); } if(charin(flags, 't')) /* Explicitly open files as text rather than as binary. * this changes "rb" to "r". Should have no effect on * POSIX systems but may change CRLF to just LF with * MS Windows. I don't know why anyone would need this * but it's here for those that do. */ mode[1] = '\0'; if(argsc == 0) tail(stdin, output, length); else for(i = 1; i < argsc; ++i){ if(i > 1) fprintf(output, "\n"); if(!strcmp(argv[i], "-")){ realpath(argv[i], filepath); input = fopen(argv[i], "r"); if(input == NULL){ fprintf(stderr, "%s: %s: Could not open file for reading\n", argv[0], filepath); exit(1); } }else{ input = stdin; /* probably safe but could be vulnerable */ /* just making filepath[] = "" */ s = strlen("") + 1; /* minimum size for filepath */ if(fplen >= s) strcpy(filepath, ""); else if(realloc(filepath, s) == NULL) fprintf(stderr, "%s: Failed to reallocate path string, using \n", argv[0]); } fprintf(output, "==> %s <==\n", filepath); tail(input, output, length); if(input != stdin && fclose(input) != 0){ fprintf(stderr, "%s: %s: Could not close file. Exiting...\n", argv[0], filepath); exit(1); } } return 0; }