added tail and head
This commit is contained in:
parent
4403e7edc2
commit
a94b5bcd94
8 changed files with 385 additions and 51 deletions
|
@ -4,7 +4,7 @@
|
||||||
A terrible busybox/gnu coreutils clone.
|
A terrible busybox/gnu coreutils clone.
|
||||||
|
|
||||||
Currently the only supported commands are:
|
Currently the only supported commands are:
|
||||||
`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`
|
`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`
|
||||||
|
|
||||||
## How to
|
## How to
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,4 @@ COMMAND_EMPTY(groups);
|
||||||
COMMAND_EMPTY(user_id);
|
COMMAND_EMPTY(user_id);
|
||||||
COMMAND(ls);
|
COMMAND(ls);
|
||||||
COMMAND(tail);
|
COMMAND(tail);
|
||||||
|
COMMAND(head);
|
||||||
|
|
145
src/commands/head.c
Normal file
145
src/commands/head.c
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
#include "../command.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct Flags {
|
||||||
|
int count;
|
||||||
|
bool lines;
|
||||||
|
bool print_headers;
|
||||||
|
bool dont_print_headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void head_file_lines(FILE* file, struct Flags* flags) {
|
||||||
|
size_t len = 0;
|
||||||
|
char* line = NULL;
|
||||||
|
|
||||||
|
int count = flags->count;
|
||||||
|
while(count > 0 && getline(&line, &len, file) != -1) {
|
||||||
|
printf("%s", line);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void head_file_chars(FILE* file, struct Flags* flags) {
|
||||||
|
char c;
|
||||||
|
int count = flags->count;
|
||||||
|
while(count > 0 && (c = getc(file)) != EOF) {
|
||||||
|
putchar(c);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help() {
|
||||||
|
printf("Usage: head [OPTIONS] [FILE]...\n\n");
|
||||||
|
printf("Print first 10 lines of FILEs (or stdin)\n");
|
||||||
|
printf("With more than one FILE, precede each with a filename header.\n\n");
|
||||||
|
printf("\t-c [+]N[bkm]\tPrint first N bytes\n");
|
||||||
|
printf("\t-n N[bkm]\tPrint first 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");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* next_arg(int argc, char** argv, int index) {
|
||||||
|
if (index >= argc) {
|
||||||
|
error("error: expected another argument after option");
|
||||||
|
}
|
||||||
|
return argv[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_header(char* path, struct Flags* flags, 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 head_file(char* path, struct Flags* flags, bool many) {
|
||||||
|
FILE* file = get_file(path, "r");
|
||||||
|
print_header(path, flags, many);
|
||||||
|
if (flags->lines) {
|
||||||
|
head_file_lines(file, flags);
|
||||||
|
} else {
|
||||||
|
head_file_chars(file, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND(head) {
|
||||||
|
struct Flags flags;
|
||||||
|
flags.count = 10;
|
||||||
|
flags.lines = true;
|
||||||
|
flags.print_headers = false;
|
||||||
|
flags.dont_print_headers = false;
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
if (!prefix("-", argv[i])) break;
|
||||||
|
if (streql(argv[0], "--help")) help();
|
||||||
|
start++;
|
||||||
|
int i_s = i;
|
||||||
|
for (size_t j = 1; j < strlen(argv[i_s]); j++) {
|
||||||
|
char c = argv[i_s][j];
|
||||||
|
switch(c) {
|
||||||
|
case 'c': {
|
||||||
|
start++;
|
||||||
|
flags.lines = false;
|
||||||
|
char* arg = next_arg(argc, argv, ++i);
|
||||||
|
long int bkm = get_blkm(arg);
|
||||||
|
if (bkm < 1) {
|
||||||
|
error("error: bkm cannot be less than 1");
|
||||||
|
}
|
||||||
|
flags.count = bkm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n': {
|
||||||
|
start++;
|
||||||
|
flags.lines = true;
|
||||||
|
char* arg = next_arg(argc, argv, ++i);
|
||||||
|
long int bkm = get_blkm(arg);
|
||||||
|
if (bkm < 1) {
|
||||||
|
error("error: bkm cannot be less than 1");
|
||||||
|
}
|
||||||
|
flags.count = bkm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'q':
|
||||||
|
flags.dont_print_headers = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
flags.print_headers = true;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
error("error: unknown option -%c", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = argc - start;
|
||||||
|
|
||||||
|
if (count < 1) {
|
||||||
|
head_file_lines(stdin, &flags);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 1) {
|
||||||
|
head_file(argv[start], &flags, false);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
head_file(argv[start + i], &flags, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ struct Flags {
|
||||||
bool more_info;
|
bool more_info;
|
||||||
bool one_column;
|
bool one_column;
|
||||||
bool recurse;
|
bool recurse;
|
||||||
|
bool colored;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
|
@ -34,10 +35,11 @@ struct FileInfo {
|
||||||
struct group* grp;
|
struct group* grp;
|
||||||
char size[5];
|
char size[5];
|
||||||
char date[13];
|
char date[13];
|
||||||
struct dirent* file;
|
char name[PATH_MAX];
|
||||||
bool set_uid;
|
bool set_uid;
|
||||||
bool set_gid;
|
bool set_gid;
|
||||||
bool exec;
|
bool exec;
|
||||||
|
unsigned char type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileListInfo {
|
struct FileListInfo {
|
||||||
|
@ -61,14 +63,17 @@ static DIR* get_directory(char* path) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void append_path(char buf[PATH_MAX], const char* dir_path, const struct dirent* file) {
|
static void append_path(char buf[PATH_MAX], const char* dir_path, const char* file_path) {
|
||||||
size_t dir_len = strlen(dir_path);
|
size_t dir_len = strlen(dir_path);
|
||||||
|
size_t file_len = strlen(file_path);
|
||||||
|
|
||||||
memcpy(buf, dir_path, dir_len);
|
memcpy(buf, dir_path, dir_len);
|
||||||
if (buf[dir_len-1] != '/') {
|
if (buf[dir_len-1] != '/') {
|
||||||
buf[dir_len] = '/';
|
buf[dir_len] = '/';
|
||||||
dir_len++;
|
dir_len++;
|
||||||
}
|
}
|
||||||
memcpy(buf + dir_len, file->d_name, strlen(file->d_name) + 1);
|
memcpy(buf + dir_len, file_path, file_len);
|
||||||
|
buf[dir_len + file_len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_file_info(struct dirent* file, const char* dir_path, struct FileInfo* info) {
|
static bool get_file_info(struct dirent* file, const char* dir_path, struct FileInfo* info) {
|
||||||
|
@ -77,17 +82,20 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
|
||||||
gid_t gid = getgid();
|
gid_t gid = getgid();
|
||||||
|
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
append_path(buf, dir_path, file);
|
append_path(buf, dir_path, file->d_name);
|
||||||
|
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
memset(&s, 0, sizeof(struct stat));
|
||||||
|
|
||||||
if (stat(buf, &s) < 0) {
|
if (stat(buf, &s) < 0) {
|
||||||
printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
|
// printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->set_uid = false;
|
info->set_uid = false;
|
||||||
info->set_gid = false;
|
info->set_gid = false;
|
||||||
info->exec = false;
|
info->exec = false;
|
||||||
|
info->name[0] = '\0';
|
||||||
|
|
||||||
if (file->d_type == DT_DIR)
|
if (file->d_type == DT_DIR)
|
||||||
info->mode[0] = 'd';
|
info->mode[0] = 'd';
|
||||||
|
@ -137,8 +145,10 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
|
||||||
|
|
||||||
info->usr = getpwuid(s.st_uid);
|
info->usr = getpwuid(s.st_uid);
|
||||||
info->grp = getgrgid(s.st_gid);
|
info->grp = getgrgid(s.st_gid);
|
||||||
info->file = file;
|
|
||||||
info->links = s.st_nlink;
|
info->links = s.st_nlink;
|
||||||
|
info->type = file->d_type;
|
||||||
|
|
||||||
|
strcpy(info->name, file->d_name);
|
||||||
|
|
||||||
print_file_size(s.st_size, info->size);
|
print_file_size(s.st_size, info->size);
|
||||||
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date);
|
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date);
|
||||||
|
@ -147,9 +157,9 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
|
||||||
|
|
||||||
static char* get_file_color(struct FileInfo* info) {
|
static char* get_file_color(struct FileInfo* info) {
|
||||||
char* color;
|
char* color;
|
||||||
if (info->file->d_type == DT_DIR) {
|
if (info->type == DT_DIR) {
|
||||||
color = DIR_COLOR;
|
color = DIR_COLOR;
|
||||||
} else if (info->file->d_type == DT_LNK) {
|
} else if (info->type == DT_LNK) {
|
||||||
color = LINK_COLOR;
|
color = LINK_COLOR;
|
||||||
} else {
|
} else {
|
||||||
if (info->set_uid) {
|
if (info->set_uid) {
|
||||||
|
@ -171,6 +181,7 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
|
||||||
|
|
||||||
if (!isatty(1)) {
|
if (!isatty(1)) {
|
||||||
flags->one_column = true;
|
flags->one_column = true;
|
||||||
|
flags->colored = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* color;
|
char* color;
|
||||||
|
@ -179,7 +190,6 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
|
||||||
|
|
||||||
for (int i = 0; i < file_len; i++) {
|
for (int i = 0; i < file_len; i++) {
|
||||||
struct FileInfo finfo = files[i];
|
struct FileInfo finfo = files[i];
|
||||||
if (strlen(finfo.file->d_name) < 1) continue;
|
|
||||||
color = get_file_color(&finfo);
|
color = get_file_color(&finfo);
|
||||||
if (flags->more_info) {
|
if (flags->more_info) {
|
||||||
printf("%s %*d %*s %*s %*s %s %s%s\x1b[0m",
|
printf("%s %*d %*s %*s %*s %s %s%s\x1b[0m",
|
||||||
|
@ -193,12 +203,12 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
|
||||||
info.max_size,
|
info.max_size,
|
||||||
finfo.size,
|
finfo.size,
|
||||||
finfo.date,
|
finfo.date,
|
||||||
color,
|
flags->colored ? color : FILE_COLOR,
|
||||||
finfo.file->d_name
|
finfo.name
|
||||||
);
|
);
|
||||||
if (finfo.file->d_type == DT_LNK) {
|
if (finfo.type == DT_LNK) {
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
append_path(path, dir_path, finfo.file);
|
append_path(path, dir_path, finfo.name);
|
||||||
|
|
||||||
char lnk[PATH_MAX];
|
char lnk[PATH_MAX];
|
||||||
if (readlink(path, lnk, PATH_MAX) < 0) {
|
if (readlink(path, lnk, PATH_MAX) < 0) {
|
||||||
|
@ -211,13 +221,13 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
} else if (flags->one_column) {
|
} else if (flags->one_column) {
|
||||||
printf("%s%s\x1b[0m\n", color, finfo.file->d_name);
|
printf("%s%s\x1b[0m\n", color, finfo.name);
|
||||||
} else {
|
} else {
|
||||||
if (info.total_len > w.ws_col) {
|
if (info.total_len > w.ws_col) {
|
||||||
if (i != 0 && i % row_count == 0) putchar('\n');
|
if (i != 0 && i % row_count == 0) putchar('\n');
|
||||||
printf("%s%*s\x1b[0m", color, column_width, finfo.file->d_name);
|
printf("%s%*s\x1b[0m", flags->colored ? color : FILE_COLOR, -column_width, finfo.name);
|
||||||
} else {
|
} else {
|
||||||
printf("%s%s\x1b[0m ", color, finfo.file->d_name);
|
printf("%s%s\x1b[0m ", flags->colored ? color : FILE_COLOR, finfo.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +328,7 @@ static bool recurse_directory(char* path, struct Flags* flags) {
|
||||||
if (!flags->hidden && prefix(".", file->d_name)) continue;
|
if (!flags->hidden && prefix(".", file->d_name)) continue;
|
||||||
if (is_dot_dir(file->d_name)) continue;
|
if (is_dot_dir(file->d_name)) continue;
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
append_path(buf, path, file);
|
append_path(buf, path, file->d_name);
|
||||||
recurse_directory(buf, flags);
|
recurse_directory(buf, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,11 +384,17 @@ COMMAND(ls) {
|
||||||
flags.hide_dot = false;
|
flags.hide_dot = false;
|
||||||
flags.one_column = false;
|
flags.one_column = false;
|
||||||
flags.recurse = false;
|
flags.recurse = false;
|
||||||
|
flags.colored = false;
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = 0; i < argc; i++) {
|
for (int i = 0; i < argc; i++) {
|
||||||
if (!prefix("-", argv[i])) break;
|
if (!prefix("-", argv[i])) break;
|
||||||
if (streql("--help", argv[i])) help();
|
if (streql("--help", argv[i])) help();
|
||||||
start++;
|
start++;
|
||||||
|
if (streql("--color=auto", argv[i]) || streql("--color=yes", argv[i])) {
|
||||||
|
flags.colored = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (size_t j = 1; j < strlen(argv[i]); j++) {
|
for (size_t j = 1; j < strlen(argv[i]); j++) {
|
||||||
char c = argv[i][j];
|
char c = argv[i][j];
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
|
@ -2,19 +2,31 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
static void tail_file(FILE* file) {
|
struct Flags {
|
||||||
char* ring[10];
|
bool lines;
|
||||||
memset(ring, 0, sizeof(char*) * 10);
|
int count;
|
||||||
|
bool print_headers;
|
||||||
|
bool dont_print_headers;
|
||||||
|
bool print_as_grow;
|
||||||
|
int grow_wait;
|
||||||
|
};
|
||||||
|
|
||||||
int ring_len[10];
|
static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) {
|
||||||
|
char* ring[count];
|
||||||
|
memset(ring, 0, sizeof(char*) * count);
|
||||||
|
|
||||||
|
int ring_len[count];
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int size = 0;
|
unsigned int size = 0;
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
size_t len = 0;
|
fseek(file, skip, SEEK_SET);
|
||||||
|
size_t len = skip;
|
||||||
char* line = NULL;
|
char* line = NULL;
|
||||||
while ((read = getline(&line, &len, file)) != -1) {
|
while ((read = getline(&line, &len, file)) != -1) {
|
||||||
|
|
||||||
|
@ -23,57 +35,189 @@ static void tail_file(FILE* file) {
|
||||||
ring_len[index] = read;
|
ring_len[index] = read;
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
index %= 10;
|
index %= count;
|
||||||
if (size < 10) size++;
|
if (size < count) size++;
|
||||||
|
|
||||||
line = NULL;
|
line = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
index += 10 - size;
|
index += count - size;
|
||||||
index %= 10;
|
index %= count;
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (unsigned int i = 0; i < size; i++) {
|
||||||
fwrite(ring[index], ring_len[index], 1, stdout);
|
fwrite(ring[index], ring_len[index], 1, stdout);
|
||||||
free(ring[index]);
|
free(ring[index]);
|
||||||
index += 1;
|
index += 1;
|
||||||
index %= 10;
|
index %= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) {
|
||||||
|
char ring[count];
|
||||||
|
memset(ring, 0, count);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
unsigned int size = 0;
|
||||||
|
|
||||||
|
fseek(file, skip, SEEK_SET);
|
||||||
|
int read = skip;
|
||||||
|
int c;
|
||||||
|
while((c = getc(file)) != EOF) {
|
||||||
|
ring[index] = c;
|
||||||
|
index++;
|
||||||
|
read++;
|
||||||
|
index %= count;
|
||||||
|
if (size < count) size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += count - size;
|
||||||
|
index %= count;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < size; i++) {
|
||||||
|
putchar(ring[index]);
|
||||||
|
index += 1;
|
||||||
|
index %= count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help() {
|
static void help() {
|
||||||
printf("Usage: tail [FILE]...\n\n");
|
printf("Usage: tail [OPTIONS] [FILE]...\n\n");
|
||||||
printf("Print last 10 lines of FILEs (or stdin) to.\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");
|
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);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
COMMAND(tail) {
|
static char* next_arg(int argc, char** argv, int index) {
|
||||||
if (argc < 1) {
|
if (index >= argc) {
|
||||||
tail_file(stdin);
|
error("error: expected another argument after option");
|
||||||
return EXIT_SUCCESS;
|
}
|
||||||
|
return argv[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (streql(argv[0], "--help")) help();
|
static void print_header(char* path, struct Flags* flags, bool many) {
|
||||||
|
if (flags->dont_print_headers) return;
|
||||||
if (argc == 1) {
|
if (!many && !flags->print_headers) return;
|
||||||
FILE* file = get_file(argv[0], "r");
|
if (streql("-", path)) {
|
||||||
tail_file(file);
|
printf("\n==> standard input <==\n");
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
FILE* file;
|
|
||||||
if (streql("-", argv[i])) {
|
|
||||||
file = stdin;
|
|
||||||
} else {
|
} else {
|
||||||
file = get_file_s(argv[i], "r");
|
printf("\n=>> %s <==\n", path);
|
||||||
if (file == NULL) continue;
|
|
||||||
}
|
}
|
||||||
printf("\n==> %s <==\n", argv[i]);
|
}
|
||||||
tail_file(file);
|
|
||||||
|
static void tail_file(char* path, struct Flags* flags, bool many) {
|
||||||
|
FILE* file = get_file(path, "r");
|
||||||
|
print_header(path, flags, many);
|
||||||
|
|
||||||
|
size_t 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");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND(tail) {
|
||||||
|
|
||||||
|
struct Flags flags;
|
||||||
|
flags.count = 10;
|
||||||
|
flags.dont_print_headers = false;
|
||||||
|
flags.print_headers = false;
|
||||||
|
flags.lines = true;
|
||||||
|
flags.print_as_grow = false;
|
||||||
|
flags.grow_wait = 10;
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
if (!prefix("-", argv[i])) break;
|
||||||
|
if (streql(argv[0], "--help")) help();
|
||||||
|
start++;
|
||||||
|
int i_s = i;
|
||||||
|
for (size_t j = 1; j < strlen(argv[i_s]); j++) {
|
||||||
|
char c = argv[i_s][j];
|
||||||
|
switch(c) {
|
||||||
|
case 'c': {
|
||||||
|
start++;
|
||||||
|
flags.lines = false;
|
||||||
|
char* arg = next_arg(argc, argv, ++i);
|
||||||
|
long int bkm = get_blkm(arg);
|
||||||
|
if (bkm < 1) {
|
||||||
|
error("error: bkm cannot be less than 1");
|
||||||
|
}
|
||||||
|
flags.count = bkm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n': {
|
||||||
|
start++;
|
||||||
|
flags.lines = true;
|
||||||
|
char* arg = next_arg(argc, argv, ++i);
|
||||||
|
long int bkm = get_blkm(arg);
|
||||||
|
if (bkm < 1) {
|
||||||
|
error("error: bkm cannot be less than 1");
|
||||||
|
}
|
||||||
|
flags.count = bkm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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': {
|
||||||
|
start++;
|
||||||
|
char* arg = next_arg(argc, argv, ++i);
|
||||||
|
long int sec = get_number(arg);
|
||||||
|
if (sec < 1) {
|
||||||
|
error("error: wait seconds cannot be less than 1");
|
||||||
|
}
|
||||||
|
flags.grow_wait = sec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
error("error: unknown option -%c", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = argc - start;
|
||||||
|
|
||||||
|
if (count < 1) {
|
||||||
|
tail_file_lines(stdin, 10, 0);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 1) {
|
||||||
|
tail_file(argv[start], &flags, false);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
tail_file(argv[start + i], &flags, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
@ -20,7 +20,7 @@ int main (ARGUMENTS) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
printf("usage: lazysphere [function [arguments]...]\n\n");
|
printf("usage: lazysphere [function [arguments]...]\n\n");
|
||||||
printf("currently defined functions:\n");
|
printf("currently defined functions:\n");
|
||||||
printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail\n");
|
printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
argc--;
|
argc--;
|
||||||
|
@ -52,6 +52,8 @@ int main (ARGUMENTS) {
|
||||||
return ls(NEXT_ARGS);
|
return ls(NEXT_ARGS);
|
||||||
} else if (streql(cmd, "tail")) {
|
} else if (streql(cmd, "tail")) {
|
||||||
return tail(NEXT_ARGS);
|
return tail(NEXT_ARGS);
|
||||||
|
} else if (streql(cmd, "head")) {
|
||||||
|
return head(NEXT_ARGS);
|
||||||
} else {
|
} else {
|
||||||
error("error: invalid command %s", cmd);
|
error("error: invalid command %s", cmd);
|
||||||
}
|
}
|
||||||
|
|
25
src/shared.c
25
src/shared.c
|
@ -34,6 +34,7 @@ FILE* get_file_s(const char* path, const char* type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* get_file(const char* path, const char* type) {
|
FILE* get_file(const char* path, const char* type) {
|
||||||
|
if (streql("-", path)) return stdin;
|
||||||
FILE* file = get_file_s(path, type);
|
FILE* file = get_file_s(path, type);
|
||||||
if (file == NULL) exit(EXIT_FAILURE);
|
if (file == NULL) exit(EXIT_FAILURE);
|
||||||
return file;
|
return file;
|
||||||
|
@ -48,6 +49,30 @@ long int get_number(const char* text) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long int get_blkm(const char* text) {
|
||||||
|
char* end;
|
||||||
|
long int n = strtol(text, &end, 10);
|
||||||
|
if (text == end) {
|
||||||
|
error("error: %s is not a valid bkm", text);
|
||||||
|
}
|
||||||
|
if (*end == '\0') return n;
|
||||||
|
switch (*end) {
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
return n * 1024;
|
||||||
|
case 'B':
|
||||||
|
case 'b':
|
||||||
|
return n * 512;
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
return n * 1024 * 1204;
|
||||||
|
default:
|
||||||
|
error("error: invalid bkm type %c", *end);
|
||||||
|
}
|
||||||
|
// shouldnt get here anyways
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool streql(const char* a, const char* b) {
|
bool streql(const char* a, const char* b) {
|
||||||
if (*a != *b) return false;
|
if (*a != *b) return false;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
|
@ -10,6 +10,7 @@ void error(const char* format, ...);
|
||||||
FILE* get_file_s(const char* path, const char* type);
|
FILE* get_file_s(const char* path, const char* type);
|
||||||
FILE* get_file(const char* path, const char* type);
|
FILE* get_file(const char* path, const char* type);
|
||||||
long int get_number(const char* text);
|
long int get_number(const char* text);
|
||||||
|
long int get_blkm(const char* text);
|
||||||
|
|
||||||
bool streql(const char* a, const char* b);
|
bool streql(const char* a, const char* b);
|
||||||
bool prefix(const char* pre, const char* str);
|
bool prefix(const char* pre, const char* str);
|
||||||
|
|
Loading…
Reference in a new issue