#include "command.h" #include "lslib.h" #include #include #include static struct { bool lines; int count; bool print_headers; bool dont_print_headers; bool print_as_grow; int grow_wait; } flags; static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) { char** ring; int* ring_len; int index, read; unsigned int size, i; size_t len; char* line; ring = xalloc(sizeof(char*) * count); memset(ring, 0, sizeof(char*) * count); ring_len = xalloc(sizeof(int) * count); index = 0; size = 0; fseek(file, skip, SEEK_SET); len = skip; line = NULL; while ((read = getline(&line, &len, file)) != -1) { if (ring[index] != NULL) free(ring[index]); ring[index] = line; ring_len[index] = read; index++; index %= count; if (size < count) size++; line = NULL; } index += count - size; index %= count; for (i = 0; i < size; i++) { fwrite(ring[index], ring_len[index], 1, stdout); free(ring[index]); index += 1; index %= count; } free(line); fclose(file); free(ring); free(ring_len); return len; } static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) { char* ring; int index; unsigned int size, i; int read, c; ring = xalloc(sizeof(char) * count); memset(ring, 0, count); index = 0; size = 0; fseek(file, skip, SEEK_SET); read = skip; while((c = getc(file)) != EOF) { ring[index] = c; index++; read++; index %= count; if (size < count) size++; } index += count - size; index %= count; for (i = 0; i < size; i++) { putchar(ring[index]); index += 1; index %= count; } fclose(file); return read; } static void help(void) { printf("Usage: tail [OPTIONS] [FILE]...\n\n"); printf("Print last 10 lines of FILEs (or stdin) to.\n"); printf("With more than one FILE, precede each with a filename header.\n\n"); printf("\t-c [+]N[bkm]\tPrint last N bytes\n"); printf("\t-n N[bkm]\tPrint last N lines\n"); printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); printf("\t-q\t\tNever print headers\n"); printf("\t-v\t\tAlways print headers\n"); printf("\t-f\t\tPrint data as file grows\n"); printf("\t-s SECONDS\tWait SECONDS between reads with -f\n"); exit(EXIT_SUCCESS); } static void print_header(char* path, bool many) { if (flags.dont_print_headers) return; if (!many && !flags.print_headers) return; if (streql("-", path)) { printf("\n==> standard input <==\n"); } else { printf("\n=>> %s <==\n", path); } } static void tail_file(char* path, bool many) { FILE* file; size_t skip; file = get_file(path, "r"); print_header(path, many); skip = 0; while (true) { if (flags.lines) { skip = tail_file_lines(file, flags.count, skip); } else { skip = tail_file_chars(file, flags.count, skip); } if (!flags.print_as_grow) break; sleep(flags.grow_wait); get_file(path, "r"); }; } static int short_arg(char c, char* next) { switch (c) { case 'c': { long int bkm; flags.lines = false; check_arg(next); bkm = get_blkm(next); if (bkm < 1) { error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; } case 'n': { long int bkm; flags.lines = true; check_arg(next); bkm = get_blkm(next); if (bkm < 1) { error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; } case 'q': flags.dont_print_headers = true; break; case 'v': flags.print_headers = true; break; case 'f': flags.print_as_grow = true; break; case 's': { long int sec; check_arg(next); sec = get_number(next); if (sec < 1) { error("wait seconds cannot be less than 1"); } flags.grow_wait = sec; return ARG_USED; } default: return ARG_INVALID; } return ARG_UNUSED; } COMMAND(tail_main) { int start, count, i; flags.count = 10; flags.dont_print_headers = false; flags.print_headers = false; flags.lines = true; flags.print_as_grow = false; flags.grow_wait = 10; start = parse_args(argc, argv, help, short_arg, NULL); count = argc - start; if (count < 1) { tail_file_lines(stdin, 10, 0); return EXIT_SUCCESS; } if (count == 1) { tail_file(argv[start], false); return EXIT_SUCCESS; } for (i = 0; i < count; i++) { tail_file(argv[start + i], true); } return EXIT_SUCCESS; }