129 lines
2.9 KiB
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/types.h>
|
|
#include <sys/stat.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) {
|
|
int buf_size = MIN(BUF_CAP, file_size); // We can't seek back before the beginning of the file
|
|
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;
|
|
}
|
|
}
|