harakit/src/tail.c

190 lines
5.1 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of YAC coreutils.
*
* YAC coreutils is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* YAC coreutils is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <errno.h>
#include <sysexits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "yac.h"
void tailc(FILE *file, long num) {
int byte;
char buf[num];
long offset = -num;
fseek(file, offset, SEEK_END);
for (int i = 0; (byte = fgetc(file)) != EOF; i++) {
buf[i] = byte;
}
fputs(buf, stdout);
}
void tailf(FILE *file) {
int byte;
while(true) {
if ((byte = fgetc(file)) != EOF) { putchar(byte); }
}
}
void tailn(FILE *file, long num) {
char *buf = calloc(4096, 1);
char *lines[num];
int byte;
int lc = 0;
for (int bc = 0; (byte = fgetc(file)) != EOF; bc++) {
if (bc == sizeof(buf)) {
int err;
if (err = realloc(buf, sizeof(buf) * 2) == NULL) {
// TODO: error handling
}
}
buf[bc] = byte;
if (byte == '\n') {
lines[lc] = buf;
bc = 0;
lc++;
}
if (lc == num) {
for (int i = 0; i < lc; i++) {
lines[i] = lines[i + 1];
}
lc--;
}
}
int i;
if ((i = lc - num) < 0) { i = 0; }
for (; i < lc; i++) {
fputs(lines[i], stdout);
}
}
int main(int argc, char *argv[]) {
bool c = false;
bool f = false;
bool n = false;
int i;
int opt;
long num;
void (*fn)(FILE *, long) = tailn;
extern int optind;
while ((opt = getopt(argc, argv, "c:fn:")) != -1) {
switch (opt) {
/*
* From tail(1p):
*
* -c number The application shall ensure that the number option-argument
* is a decimal integer, optionally including a sign. The sign
* shall affect the location in the file, measured in bytes, to
* begin the copying:
*
* ┌─────┬────────────────────────────────────────┐
* │Sign │ Copying Starts │
* ├─────┼────────────────────────────────────────┤
* │ + │ Relative to the beginning of the file. │
* │ - │ Relative to the end of the file. │
* │none │ Relative to the end of the file. │
* └─────┴────────────────────────────────────────┘
* The application shall ensure that if the sign of the number
* option-argument is '+', the number option-argument is a non-
* zero decimal integer.
*
* The origin for counting shall be 1; that is, -c +1 represents
* the first byte of the file, -c -1 the last.
*
* -f If the input file is a regular file or if the file operand
* specifies a FIFO, do not terminate after the last line of the
* input file has been copied, but read and copy further bytes
* from the input file when they become available. If no file
* operand is specified and standard input is a pipe or FIFO,
* the -f option shall be ignored. If the input file is not a
* FIFO, pipe, or regular file, it is unspecified whether or not
* the -f option shall be ignored.
*
* -n number This option shall be equivalent to -c number, except the
* starting location in the file shall be measured in lines
* instead of bytes. The origin for counting shall be 1; that
* is, -n +1 represents the first line of the file, -n -1 the
* last.
*
* If neither -c nor -n is specified, -n 10 shall be assumed.
*/
case 'c':
c = true;
fn = tailc;
num = (long)optarg;
break;
case 'f':
f = true;
case 'n':
n = true;
num = (long)optarg;
break;
default:
fprintf(
stderr,
"Usage: %s (-f) [-c characters] [-n lines] file...\n",
argv[0]
);
return EX_USAGE;
}
}
if (!n && !c) {
num = 10;
} else if (n && c) {
fprintf(
stderr,
"Usage: %s (-f) [-c characters] [-n lines] file...\n",
argv[0]
);
return EX_USAGE;
}
FILE *file;
if (optind = argc) {
fn(stdin, num);
if (f) { tailf(stdin); }
} else {
for (i = optind; i < argc; i++) {
if ((file = rpath(argv[0], argv[i])) != NULL) {
fn(file, num);
fclose(file);
}
if (f) { tailf(file); }
}
}
return EX_OK;
}