153 lines
3.3 KiB
C
153 lines
3.3 KiB
C
|
#include <ctype.h>
|
||
|
#include <limits.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#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[] = "<stdin>" */
|
||
|
s = strlen("<stdin>") + 1; /* minimum size for filepath */
|
||
|
if(fplen >= s)
|
||
|
strcpy(filepath, "<stdin>");
|
||
|
else if(realloc(filepath, s) == NULL)
|
||
|
fprintf(stderr, "%s: Failed to reallocate path string, using <stdin>\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;
|
||
|
}
|