shorten/shorten.c

129 lines
2.9 KiB
C

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_CAP 1024
int main(int argc, char **argv) {
if (argc != 3) {
printf("%s: Invalid number of arguments.\n", argv[0]);
printf("Usage: %s linecount filename\n", argv[0]);
printf("to remove linecount lines from the end of the file\n");
}
int number = atoi(argv[1]);
char *file = argv[2]; // No copy
int count = 0;
if (number <= 0) {
printf("Bad number of line\n");
exit(1);
}
struct stat file_stat;
if (stat(file, &file_stat) < 0) {
printf("Stat failed: %d\n", errno);
return 2;
}
int file_size = file_stat.st_size;
if (file_size == 0) {
printf("No change: file does not end with a newline\n");
return 1;
}
int fd = open(file, O_RDWR | O_APPEND);
if (lseek(fd, -1, SEEK_END) < 0) {
printf("Lseek1 failed: %d\n", errno);
close(fd);
return 2;
}
char c;
if (read(fd, &c, 1) < 0) {
printf("Read1 failed: %d\n", errno);
close(fd);
return 2;
}
if (c != '\n') {
printf("No change: file does not end with a newline\n");
close(fd);
return 1;
}
while (1) {
// We can't seek back before the beginning of the file
int buf_size = MIN(BUF_CAP, file_size);
char buf[buf_size];
// Go back in the file to read the length of the buffer
if (lseek(fd, -buf_size, SEEK_CUR) < 0) {
printf("Lseek2 failed: %d\n", errno);
close(fd);
return 2;
}
// ~lseek(fd, buf_size, os.SEEK_CUR)
int buf_len = read(fd, buf, buf_size);
if (buf_len < 0) {
printf("Read2 failed: %d\n", errno);
close(fd);
return 2;
}
char *char_pos = memrchr(buf, '\n', buf_len);
if (char_pos != NULL) {
count++;
// Go to position found ; a read would read a '\n'
if (lseek(fd, (ssize_t)(char_pos - buf) - buf_len, SEEK_CUR) < 0) {
printf("Lseek3 failed: %d\n", errno);
close(fd);
return 2;
}
// "resize" the file to look for next '\n' from this point
file_size -= buf_len - (ssize_t)(char_pos - buf);
} else {
// Go back to previous position
if (lseek(fd, -buf_len, SEEK_CUR) < 0) {
printf("Lseek4 failed: %d\n", errno);
close(fd);
return 2;
}
file_size -= buf_len;
}
if (file_size < 0)
break;
if (count == number + 1) {
file_size++; // Keep the last '\n'
if (ftruncate(fd, file_size) < 0) {
printf("Ftruncate failed: %d\n", errno);
close(fd);
return 2;
}
close(fd);
return 0;
}
// Beginning of file?
if (lseek(fd, 0, SEEK_CUR) == 0) {
printf("No change: file does not end with a newline\n");
return 1;
}
}
close(fd);
if (count < number + 1) {
printf("No change: requested removal would leave empty file\n");
return 3;
}
}