Adding base
This commit is contained in:
parent
f2f081bd42
commit
9dcdbcdc5e
|
@ -0,0 +1,13 @@
|
|||
CC=clang
|
||||
CFLAGS=-pedantic -Wall -Wextra -std=c99 -D_GNU_SOURCE -O3
|
||||
|
||||
all: shorten
|
||||
|
||||
shorten: shorten.o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm *.o
|
|
@ -0,0 +1,128 @@
|
|||
#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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue