From c40f79737a4ce6ae0cd8bd6ea7f302217333e486 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sat, 22 Aug 2020 19:43:03 +0200 Subject: Added Makefile --- .gitignore | 1 + Makefile | 52 +++++++++++++ basename.c | 83 -------------------- chown.c | 193 ---------------------------------------------- cut.c | 225 ------------------------------------------------------ head.c | 166 ---------------------------------------- mkdir.c | 86 --------------------- mv.c | 37 --------- rm.c | 130 ------------------------------- seq.c | 126 ------------------------------ shuf.c | 237 --------------------------------------------------------- src/basename.c | 83 ++++++++++++++++++++ src/chown.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++ src/cut.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/head.c | 166 ++++++++++++++++++++++++++++++++++++++++ src/mkdir.c | 86 +++++++++++++++++++++ src/mv.c | 37 +++++++++ src/rm.c | 130 +++++++++++++++++++++++++++++++ src/seq.c | 126 ++++++++++++++++++++++++++++++ src/shuf.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tee.c | 68 +++++++++++++++++ src/tr.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tee.c | 68 ----------------- tr.c | 229 ------------------------------------------------------- 24 files changed, 1635 insertions(+), 1580 deletions(-) create mode 100644 Makefile delete mode 100644 basename.c delete mode 100644 chown.c delete mode 100644 cut.c delete mode 100644 head.c delete mode 100644 mkdir.c delete mode 100644 mv.c delete mode 100644 rm.c delete mode 100644 seq.c delete mode 100644 shuf.c create mode 100644 src/basename.c create mode 100644 src/chown.c create mode 100644 src/cut.c create mode 100644 src/head.c create mode 100644 src/mkdir.c create mode 100644 src/mv.c create mode 100644 src/rm.c create mode 100644 src/seq.c create mode 100644 src/shuf.c create mode 100644 src/tee.c create mode 100644 src/tr.c delete mode 100644 tee.c delete mode 100644 tr.c diff --git a/.gitignore b/.gitignore index d7756c2..11269e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ a.out *.o +bin/* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..edb7787 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +BINDIR = bin +SRCDIR = src + +CC = gcc +CCFLAGS = -Wall -Wextra -Wpedantic + +all: prebuild $(BINDIR)/basename $(BINDIR)/chown $(BINDIR)/cut $(BINDIR)/head \ + $(BINDIR)/mkdir $(BINDIR)/mv $(BINDIR)/rm $(BINDIR)/seq \ + $(BINDIR)/shuf $(BINDIR)/tee $(BINDIR)/tr + +prebuild: + mkdir -vp $(BINDIR) + +$(BINDIR)/basename: $(SRCDIR)/basename.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/chown: $(SRCDIR)/chown.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/cut: $(SRCDIR)/cut.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/head: $(SRCDIR)/head.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/mkdir: $(SRCDIR)/mkdir.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/mv: $(SRCDIR)/mv.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/rm: $(SRCDIR)/rm.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/seq: $(SRCDIR)/seq.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/shuf: $(SRCDIR)/shuf.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/tee: $(SRCDIR)/tee.c + $(CC) $(CCFLAGS) -o $@ $^ + +$(BINDIR)/tr: $(SRCDIR)/tr.c + $(CC) $(CCFLAGS) -o $@ $^ + +clean: + rm -vf $(BINDIR)/* + +re: clean all + +.PHONY: prebuild all clean re diff --git a/basename.c b/basename.c deleted file mode 100644 index 83f3b8f..0000000 --- a/basename.c +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include -#include - -char *g_name = "basename"; - -void fatal(char *message) -{ - fprintf(stderr, "%s: %s\n", g_name, message); - exit(EXIT_FAILURE); -} - -char *truncate_path(char *s, char *suffix) -{ - if (*s == '\0') - return s; - - size_t last = strlen(s) - 1; - while (s[last] == '/' && last != 0) - { - s[last] = '\0'; - last--; - } - - char *last_slash = strrchr(s, '/'); - if (last_slash != NULL && last_slash != s) - s = last_slash + 1; - - if (suffix != NULL) - { - char *end = s + strlen(s) - strlen(suffix); - if (end > s && strcmp(end, suffix) == 0) - *end = '\0'; - } - return s; -} - -int main(int argc, char **argv) -{ - int option; - char *suffix = NULL; - bool multiple = false; - char line_delim = '\n'; - - g_name = argv[0]; - while ((option = getopt(argc, argv, "as:z")) != -1) - { - switch (option) - { - case 's': - suffix = optarg; - case 'a': - multiple = true; - break; - case 'z': - line_delim = '\0'; - break; - default: - return EXIT_FAILURE; - } - } - - if (optind == argc) - fatal("missing operand"); - - if (!multiple) - { - argv[optind] = truncate_path(argv[optind], argv[optind + 1]); - fputs(argv[optind], stdout); - putchar(line_delim); - return EXIT_SUCCESS; - } - - for (; optind < argc; optind++) - { - argv[optind] = truncate_path(argv[optind], suffix); - fputs(argv[optind], stdout); - putchar(line_delim); - } - return EXIT_SUCCESS; -} diff --git a/chown.c b/chown.c deleted file mode 100644 index d273afd..0000000 --- a/chown.c +++ /dev/null @@ -1,193 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef enum -{ - RMODE_FOLLOW_ROOT, - RMODE_FOLLOW_ALL, - RMODE_NO_FOLLOW, -} t_recursive_mode; - -typedef enum -{ - VLEVEL_CHANGE, - VLEVEL_QUIET, - VLEVEL_VERBOSE, -} t_verbose_level; - -static t_verbose_level g_verbosity = VLEVEL_QUIET; -static bool g_follow_link = true; -static bool g_recursive = false; -static t_recursive_mode g_recursive_mode = RMODE_NO_FOLLOW; -static char *g_name = "chown"; -static uid_t g_user_id = -1; -static gid_t g_group_id = -1; - -void verror_log(const char *format, va_list ap) -{ - fprintf(stderr, "%s: ", g_name); - vfprintf(stderr, format, ap); - fputc('\n', stderr); -} - -void error_log(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - verror_log(format, ap); - va_end(ap); -} - -void fatal(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - verror_log(format, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -bool parse_id(char *id_str, id_t *id_ptr, bool group) -{ - char *end; - - if (isdigit(*id_str)) - { - errno = 0; - long tmp = strtol(id_str, &end, 10); - if (errno != 0 || tmp > UINT_MAX || *end != '\0') - return false; - *id_ptr = (id_t)tmp; - } - else - { - struct passwd *pw = getpwnam(id_str); - if (pw == NULL) - return false; - if (group) - *id_ptr = pw->pw_gid; - else - *id_ptr = pw->pw_uid; - } - return true; -} - -bool file_chown(char *path, bool root) -{ - char recursive_path[PATH_MAX + 1] = {'\0'}; - struct stat statbuf; - - - if (stat(path, &statbuf) == -1) - { - error_log("cannot access '%s': %s", path, strerror(errno)); - return false; - } - - errno = 0; - if ((!g_recursive && g_follow_link) - || (g_recursive && (g_recursive_mode == RMODE_FOLLOW_ALL - || (root && g_recursive_mode == RMODE_FOLLOW_ROOT)))) - chown(path, g_user_id, g_group_id); - else - lchown(path, g_user_id, g_group_id); - if (errno != 0) - { - error_log("changing ownership of '%s': %s", path, strerror(errno)); - return false; - } - - if (g_recursive) - { - errno = 0; - DIR *directory = opendir(path); - - if (directory == NULL && errno == ENOTDIR) - return true; - if (directory == NULL) - { - error_log("cannot access '%s': %s", path, strerror(errno)); - return false; - } - - strcpy(recursive_path, path); - if (path[strlen(path) - 1] != '/') - strcat(recursive_path, "/"); - - struct dirent *entry; - while ((entry = readdir(directory)) != NULL) - { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - strcat(recursive_path, entry->d_name); - file_chown(recursive_path, false); - strrchr(recursive_path, '/')[1] = '\0'; - } - closedir(directory); - } - return true; -} - -/* -** -** TODO -** don't change group/user if not asked -** change group to login group is trailling : after user in cmd -** -*/ - -int main(int argc, char **argv) -{ - int option; - - while ((option = getopt(argc, argv, "cfvhRHLP")) != -1) - { - switch (option) - { - case 'c': g_verbosity = VLEVEL_CHANGE; break; - case 'f': g_verbosity = VLEVEL_QUIET; break; - case 'v': g_verbosity = VLEVEL_VERBOSE; break; - case 'h': g_follow_link = false; break; - case 'R': g_recursive = true; break; - case 'H': g_recursive_mode = RMODE_FOLLOW_ROOT; break; - case 'L': g_recursive_mode = RMODE_FOLLOW_ALL; break; - case 'P': g_recursive_mode = RMODE_NO_FOLLOW; break; - } - } - - if (argc - optind == 0) - fatal("missing operand"); - if (argc - optind == 1) - fatal("missing operand after '%s'", argv[optind]); - - char *user_str = argv[optind]; - char *group_str = strchr(argv[optind], ':'); - if (group_str != NULL) - { - *group_str = '\0'; - group_str++; - } - - if (!parse_id(user_str, &g_user_id, false)) - fatal("invalid user '%s'", user_str); - if (group_str != NULL && !parse_id(group_str, &g_group_id, true)) - fatal("invalid group '%s:%s'", user_str, group_str); - - for (optind++; optind < argc; optind++) - file_chown(argv[optind], true); - - return EXIT_SUCCESS; -} diff --git a/cut.c b/cut.c deleted file mode 100644 index 2a7990d..0000000 --- a/cut.c +++ /dev/null @@ -1,225 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#define _DEFAULT_SOURCE 1 - -#include -#include -#include -#include -#include -#include -#include - -#define LIST_VALUE_INFINITY -1 -#define LIST_VALUE_SINGLETON -2 - -#define LINE_BUFFER_SIZE 2024 - -typedef enum -{ - LIST_BYTE, - LIST_CHAR, - LIST_FIELD, - LIST_UNDEFINED, -} t_list_type; - -char *g_name = "cut"; - -void fatal_msg(char *message) -{ - fprintf(stderr, "%s: %s\n", g_name, message); - exit(EXIT_FAILURE); -} - -void fatal_errno(void) -{ - perror(g_name); - exit(EXIT_FAILURE); -} - -int parse_uint(char *s, char **endptr) -{ - if (!isdigit(*s)) - return -1; - unsigned long x = strtoul(s, endptr, 10); - return (int)x; -} - -int main(int argc, char **argv) -{ - int option; - int list_start = LIST_VALUE_INFINITY; - int list_end = LIST_VALUE_INFINITY; - t_list_type list_type = LIST_UNDEFINED; - wchar_t delimiter = '\t'; - bool print_only_delimiter = false; - char line_delimiter = '\n'; - - g_name = argv[0]; - while ((option = getopt(argc, argv, "b:c:d:f:sz")) > 0) - { - - switch (option) - { - case 'b': - case 'c': - case 'f': - if (list_type != LIST_UNDEFINED) - fatal_msg("only one type of list may be specified"); - - switch (option) - { - case 'b': list_type = LIST_BYTE; break; - case 'c': list_type = LIST_CHAR; break; - case 'f': list_type = LIST_FIELD; break; - } - - char *ptr = optarg; - - char *hyphen = strchr(ptr, '-'); - if (hyphen == NULL) // N - { - list_start = parse_uint(ptr, &ptr); - if (*ptr != '\0') - fatal_msg(""); - list_end = LIST_VALUE_SINGLETON; - } - else - { - if (ptr == hyphen) // -M - { - list_end = parse_uint(ptr + 1, &ptr); - printf(">> %d %d |%s|\n", list_start, list_end, ptr); - if (*ptr != '\0') - fatal_msg(""); - } - else - { - list_start = parse_uint(ptr, &ptr); - if (*ptr != '-') - fatal_msg(""); - ptr++; - if (*ptr != '\0') // N-M - { - list_end = parse_uint(ptr, &ptr); - } - if (*ptr != '\0') - fatal_msg(""); - } - } - break; - - case 'd': - delimiter = optarg[0]; - if (delimiter != '\0' && optarg[1] != '\0') - fatal_msg("the delimiter must be a single character"); - break; - - case 's': - print_only_delimiter = true; - break; - case 'z': - line_delimiter = '\0'; - break; - } - } - if (list_type == LIST_UNDEFINED) - fatal_msg("you must specify a list of bytes, characters, or fields"); - if (list_type != LIST_FIELD && delimiter != '\t') - fatal_msg("an input delimiter may be specified only when operating on fields"); - if (list_type != LIST_FIELD && print_only_delimiter) - fatal_msg("suppressing non-delimited lines makes sense only when operating on fields"); - - if (list_start == 0) - fatal_msg("list are numbered from 1"); - if (list_end > 0 && list_start > list_end) - fatal_msg("invalid decreasing range"); - - char *line = NULL; - size_t line_buffer_size = LINE_BUFFER_SIZE; - ssize_t read_size = -1; - - if ((line = malloc(LINE_BUFFER_SIZE)) == NULL) - exit(1); - - char *field; - int counter = 1; - char delimiter_buf[2] = {delimiter, '\0'}; - - if (optarg == NULL) - { - errno = 0; - while ((read_size = getdelim(&line, &line_buffer_size, line_delimiter, stdin)) > 0) - { - switch (list_type) - { - case LIST_BYTE: - case LIST_CHAR: // TODO unicode - if (list_end == LIST_VALUE_SINGLETON) - { - if (read_size - 1 >= list_start) - fputc(line[list_start - 1], stdout); - } - else if (list_start == LIST_VALUE_INFINITY) - { - line[list_end] = '\0'; - fputs(line, stdout); - } - else if (list_end == LIST_VALUE_INFINITY) - { - fputs(&line[list_start - 1], stdout); - } - else - { - line[list_end] = '\0'; - fputs(&line[list_start - 1], stdout); - } - break; - case LIST_FIELD: - while ((field = strsep(&line, delimiter_buf)) != NULL) - { - if (list_end == LIST_VALUE_SINGLETON && counter == list_start) - fputs(field, stdout); - else if (list_start == LIST_VALUE_INFINITY && counter <= list_end) - fputs(field, stdout); - else if (list_end == LIST_VALUE_INFINITY && counter >= list_start) - fputs(field, stdout); - else if (counter >= list_start && counter <= list_end) - fputs(field, stdout); - counter++; - } - break; - } - putchar('\n'); - } - free(line); - if (errno != 0) - fatal_errno(); - return EXIT_SUCCESS; - } - - - /* while (optarg != NULL) */ - /* { */ - /* if (strcmp(optarg, "-") == 0) */ - /* { */ - /* // read stdin */ - /* } */ - /* else */ - /* { */ - /* FILE *file; */ - /* */ - /* if ((file = fopen(optarg, "r")) == NULL) */ - /* fatal_errno(); */ - /* */ - /* while (getdelim(&line, 0, line_delimiter, file)) */ - /* { */ - /* if (field) */ - /* strsep(line, delimiter); */ - /* } */ - /* } */ - /* } */ - - - - return EXIT_SUCCESS; -} diff --git a/head.c b/head.c deleted file mode 100644 index f2201b5..0000000 --- a/head.c +++ /dev/null @@ -1,166 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -typedef enum -{ - VLEVEL_QUIET, - VLEVEL_NORMAL, - VLEVEL_VERBOSE, -} t_verbose_level; - -typedef enum -{ - UNIT_LINE, - UNIT_BYTE, -} t_unit; - -char *g_name = "head"; - -#define UNIT_BYTE_BUFFER_SIZE 1028 - -void fatal_printf(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - fprintf(stderr, "%s: ", g_name); - vfprintf(stderr, format, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -int file_head(FILE *file, - char *header, t_unit unit, - unsigned long count, char line_delimiter) -{ - char buf[UNIT_BYTE_BUFFER_SIZE] = {'\0'}; - size_t read_size; - size_t write_size; - char *line = NULL; - size_t line_size = 0; - ssize_t ret; - - if (header != NULL) - printf("==> %s <==\n", header); - switch (unit) - { - case UNIT_BYTE: - while (count > 0) - { - read_size = count > UNIT_BYTE_BUFFER_SIZE ? UNIT_BYTE_BUFFER_SIZE : count; - write_size = fread(buf, 1, read_size, file); - fwrite(buf, 1, write_size, stdout); - if (write_size < read_size) - break; - count -= read_size; - } - break; - - case UNIT_LINE: - for (; count > 0 && (ret = getdelim(&line, &line_size, line_delimiter, file)) != -1; count--) - fwrite(line, 1, ret, stdout); - free(line); - break; - } - return 0; -} - -int file_path_head(char *path, - char *header, t_unit unit, - unsigned long count, char line_delimiter) -{ - FILE *file; - - if ((file = fopen(path, "r")) == NULL) - { - fprintf(stderr, "%s: cannot open '%s' for reading: %s\n", g_name, path, strerror(errno)); - return -1; - } - file_head(file, header, unit, count, line_delimiter); - fclose(file); - return 0; -} - -int main(int argc, char **argv) -{ - int option; - t_verbose_level verbose_level = VLEVEL_NORMAL; - t_unit unit = UNIT_LINE; - unsigned long count = 10; - char line_delimiter = '\n'; - - g_name = argv[0]; - - while ((option = getopt(argc, argv, "c:n:qvz")) != -1) - { - switch (option) - { - case 'c': - case 'n': - if (strchr(optarg, '-') != NULL) - fatal_printf("invalid number of bytes: '%s'\n", optarg); - char *endptr; - errno = 0; - // TODO '-' prefix result in tail - // TODO human readable size suffix - count = strtoul(optarg, &endptr, 10); - if (*endptr != '\0') - fatal_printf("invalid number of bytes: '%s'\n", optarg); - if (errno != 0 || *endptr != '\0') - fatal_printf("invalid number of bytes: '%s': %s\n", optarg, strerror(errno)); - switch (option) - { - case 'c': unit = UNIT_BYTE; break; - case 'n': unit = UNIT_LINE; break; - } - break; - - case 'q': verbose_level = VLEVEL_QUIET; break; - case 'v': verbose_level = VLEVEL_VERBOSE; break; - case 'z': line_delimiter = '\0'; break; - } - } - - if (argv[optind] == NULL) - { - file_head( - stdin, verbose_level == VLEVEL_VERBOSE ? "standard input" : NULL, - unit, count, line_delimiter); - return EXIT_SUCCESS; - } - - if (argv[optind + 1] == NULL) - { - if (strcmp(argv[optind], "-") == 0) - file_head( - stdin, verbose_level == VLEVEL_VERBOSE ? "standard input" : NULL, - unit, count, line_delimiter); - else - file_path_head( - argv[optind], verbose_level == VLEVEL_VERBOSE ? argv[optind] : NULL, - unit, count, line_delimiter); - return EXIT_SUCCESS; - } - - for (; optind < argc; optind++) - { - int ret = 0; - - if (strcmp(argv[optind], "-") == 0) - ret = file_head( - stdin, verbose_level != VLEVEL_QUIET ? "standard input" : NULL, - unit, count, line_delimiter); - else - ret = file_path_head( - argv[optind], verbose_level != VLEVEL_QUIET ? argv[optind] : NULL, - unit, count, line_delimiter); - if (argv[optind + 1] != NULL && ret != -1) - putchar('\n'); - } - return EXIT_SUCCESS; -} diff --git a/mkdir.c b/mkdir.c deleted file mode 100644 index 3ab73d1..0000000 --- a/mkdir.c +++ /dev/null @@ -1,86 +0,0 @@ -#define _POSIX_C_SOURCE 2 - -#include -#include -#include -#include -#include -#include -#include -#include - -char *g_name = "mkdir"; - -int mkdir_wrapper(const char *path, mode_t mode, bool verbose) -{ - int ret; - - if ((ret = mkdir(path, mode)) == -1 && (verbose || errno != EEXIST)) - fprintf(stderr, "%s: cannot create directory '%s': %s\n", g_name, path, strerror(errno)); - else if (verbose) - printf("%s: created directory '%s'\n", g_name, path); - return ret; -} - -int main(int argc, char **argv) -{ - int option; - mode_t mode = 0755; - bool parent = false; - bool verbose = false; - - g_name = argv[0]; - if (argc == 1) - { - fprintf(stderr, "%s: missing operand\n", g_name); - return EXIT_FAILURE; - } - while ((option = getopt(argc, argv, "m:pv")) != -1) - { - switch (option) - { - case 'm': - sscanf(optarg, "%o", &mode); // TODO mode not working - break; - case 'p': - parent = true; - break; - case 'v': - verbose = true; - break; - } - } - for (; optind < argc; optind++) - { - // remove duplicate slash - for (size_t i = 0; argv[optind][i] != '\0'; i++) - { - if (argv[optind][i] == '/' && argv[optind][i + 1] == '/') - { - memmove(&argv[optind][i], &argv[optind][i + 1], strlen(&argv[optind][i + 1]) + 1); - i--; - } - } - - if (parent) - { - char *tmp; - char *searched = argv[optind]; - - if (*searched == '/') - searched++; - for (; (tmp = strchr(searched, '/')) != NULL; searched = tmp + 1) - { - if (tmp[1] == '\0') - continue; - *tmp = '\0'; - mkdir_wrapper(argv[optind], mode, verbose); - *tmp = '/'; - } - mkdir_wrapper(argv[optind], mode, verbose); - } - else - mkdir_wrapper(argv[optind], mode, verbose); - } - return EXIT_SUCCESS; -} diff --git a/mv.c b/mv.c deleted file mode 100644 index 46eab14..0000000 --- a/mv.c +++ /dev/null @@ -1,37 +0,0 @@ -#define _POSIX_C_SOURCE 2 -#include -#include -#include - - -int main(int argc, char **argv) -{ - int option; - - while ((option = getopt(argc, argv, "bfinS:t:Tuv")) != -1) - { - switch (option) - { - case 'b': - break; - case 'f': - break; - case 'i': - break; - case 'n': - break; - case 'S': - break; - case 't': - break; - case 'T': - break; - case 'u': - break; - case 'v': - break; - } - } - - return EXIT_SUCCESS; -} diff --git a/rm.c b/rm.c deleted file mode 100644 index 2bcf68f..0000000 --- a/rm.c +++ /dev/null @@ -1,130 +0,0 @@ -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef enum -{ - FLAG_FORCE = 1 << 0, - FLAG_INTERACTIVE = 1 << 1, - /* FLAG_INTERACTIVE_ONCE = 1 << 2, */ // TODO - FLAG_RECURSIVE = 1 << 3, - FLAG_DIRECTORY = 1 << 4, - FLAG_VERBOSE = 1 << 5, -} t_flags; - -char *g_name = "rm"; - -void log_errno(char *path) -{ - fprintf(stderr, "%s: cannot remove '%s': %s\n", g_name, path, strerror(errno)); -} - -void fatal_errno(char *path) -{ - log_errno(path); - exit(EXIT_FAILURE); -} - -int rm_file(char *path, t_flags flags) -{ - struct stat statbuf; - char recursive_path[PATH_MAX + 1] = {'\0'}; - - if (lstat(path, &statbuf) == -1) - fatal_errno(path); - if (flags & FLAG_INTERACTIVE - || (!(flags & FLAG_FORCE) - && !((statbuf.st_mode & S_IWUSR && statbuf.st_uid == getuid()) || - (statbuf.st_mode & S_IWGRP && statbuf.st_gid == getgid()) || - statbuf.st_mode & S_IWOTH))) - { - printf("%s: remove regular file '%s'? ", g_name, path); - if (tolower(getchar()) != 'y') - return 0; - } - - if (S_ISDIR(statbuf.st_mode) && flags & FLAG_RECURSIVE) - { - DIR *directory; - struct dirent *entry; - - if ((directory = opendir(path)) == NULL) - { - log_errno(path); - return 0; - } - strcpy(recursive_path, path); - if (path[strlen(path) - 1] != '/') - strcat(recursive_path, "/"); - while ((entry = readdir(directory)) != NULL) - { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - strcat(recursive_path, entry->d_name); - rm_file(recursive_path, flags); - strrchr(recursive_path, '/')[1] = '\0'; - } - closedir(directory); - if (rmdir(path) == -1) - log_errno(path); - else if (flags & FLAG_VERBOSE) - printf("removed directory '%s'\n", path); - } - else if (S_ISDIR(statbuf.st_mode) && flags & FLAG_DIRECTORY) - { - if (rmdir(path) == -1) - log_errno(path); - else if (flags & FLAG_VERBOSE) - printf("removed directory '%s'\n", path); - } - else - { - if (unlink(path) == -1) - log_errno(path); - else if (flags & FLAG_VERBOSE) - printf("removed '%s'\n", path); - } - return 0; -} - -int main(int argc, char **argv) -{ - if (argc == 1) - { - fprintf(stderr, "%s: missing operand\n", argv[0]); - return EXIT_FAILURE; - } - g_name = argv[0]; - - int option; - t_flags flags = 0; - - while ((option = getopt(argc, argv, "firRdv")) != -1) - { - switch (option) - { - case 'f': flags |= FLAG_FORCE; break; - case 'i': flags |= FLAG_INTERACTIVE; break; - /* case 'I': flags |= FLAG_INTERACTIVE_ONCE; break; */ - case 'd': flags |= FLAG_DIRECTORY; break; - case 'v': flags |= FLAG_VERBOSE; break; - - case 'r': - case 'R': - flags |= FLAG_RECURSIVE; break; - break; - } - } - - for (; optind < argc; optind++) - rm_file(argv[optind], flags); - return EXIT_SUCCESS; -} diff --git a/seq.c b/seq.c deleted file mode 100644 index 8307fc4..0000000 --- a/seq.c +++ /dev/null @@ -1,126 +0,0 @@ -#define _POSIX_C_SOURCE 2 -#include -#include -#include -#include -#include -#include -#include - -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -typedef struct -{ - long double value; - int precision; - int integer_len; -} t_num; - -char *g_name = "seq"; - -int parse_num(char *s, t_num *num) -{ - char *tmp; - - errno = 0; - num->value = strtold(s, &tmp); - if (errno != 0 || *tmp != '\0') - { - fprintf(stderr, "%s: invalid floating point argument: '%s'\n", g_name, s); - exit(EXIT_FAILURE); - } - num->precision = 0; - num->integer_len = strlen(s); - if ((tmp = strchr(s, '.')) != NULL) - { - num->precision = strlen(tmp + 1); - num->integer_len = tmp - s; - } - return 0; -} - -int main(int argc, char **argv) -{ - if (argc == 1) - { - fprintf(stderr, "%s: missing operand\n", argv[0]); - return EXIT_FAILURE; - } - - int option; - char *separator = "\n"; - bool padding = false; - - g_name = argv[0]; - - while ((option = getopt(argc, argv, "f:s:w0123456789")) != -1) - { - if (isdigit(option)) - break; - switch (option) - { - case 'f': - exit(EXIT_FAILURE); // TODO - break; - case 's': - separator = optarg; - break; - case 'w': - padding = true; - break; - } - } - - t_num first; - t_num increment; - t_num last; - - first.value = 1.0; - first.precision = 0; - increment.value = 1.0; - increment.precision = 0; - - switch (argc - optind) - { - case 1: - parse_num(argv[optind], &last); - break; - case 2: - parse_num(argv[optind], &first); - parse_num(argv[optind + 1], &last); - break; - case 3: - parse_num(argv[optind], &first); - parse_num(argv[optind + 1], &increment); - if (increment.value == 0) - { - fprintf(stderr, "%s: invalid Zero increment value '%s'\n", argv[0], argv[optind + 3]); - exit(EXIT_FAILURE); - } - parse_num(argv[optind + 2], &last); - break; - default: - fprintf(stderr, "%s: extra operand '%s'\n", argv[0], argv[optind + 3]); - exit(EXIT_FAILURE); - break; - } - - while (increment.value > 0 ? (first.value <= last.value) : (first.value >= last.value)) - { - int precision = MAX(first.precision, increment.precision); - if (!padding) - printf("%.*llf", precision, first.value); - else - { - int width = MAX(last.integer_len, first.integer_len); - if (precision != 0) - width += precision + 1; - printf("%0*.*llf", width, precision, first.value); - } - first.value += increment.value; - if (increment.value > 0 ? (first.value <= last.value) : (first.value >= last.value)) - fputs(separator, stdout); - } - putchar('\n'); - return EXIT_SUCCESS; -} diff --git a/shuf.c b/shuf.c deleted file mode 100644 index 5b4808a..0000000 --- a/shuf.c +++ /dev/null @@ -1,237 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include - -#define NUMBER_LEN(x) strlen(#x); - -static char *g_name = "shuf"; - -void fatal(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - fprintf(stderr, "%s: ", g_name); - vfprintf(stderr, format, ap); - fputc('\n', stderr); - va_end(ap); - exit(EXIT_FAILURE); -} - -long parse_ulong(char *str, char *error_msg) -{ - char *end; - long ret; - - errno = 0; - ret = strtol(str, &end, 10); - if (errno != 0 || ret < 0 || *end != '\0') - fatal("%s '%s'", error_msg, optarg); - return ret; -} - -typedef struct -{ - char **data; - size_t len; - size_t cap; -} t_lines; - -#define INITIAL_CAPACITY 64 - -void lines_init(t_lines *lines) -{ - lines->len = 0; - lines->cap = INITIAL_CAPACITY; - lines->data = malloc(lines->cap * sizeof(char*)); - if (lines->data == NULL) - fatal("%s", strerror(errno)); -} - -void lines_destroy(t_lines *lines) -{ - for (size_t i = 0; i < lines->len; i++) - free(lines->data[i]); - free(lines->data); -} - -#define GROWTH_FACTOR 2 - -void lines_push(t_lines *lines, char *line) -{ - if (lines->len >= lines->cap) - { - lines->cap *= GROWTH_FACTOR; - lines->data = realloc(lines->data, lines->cap * sizeof(char*)); - if (lines->data == NULL) - fatal("%s", strerror(errno)); - } - lines->data[lines->len] = line; - lines->len++; -} - -#define RANDOM_FILEPATH "/dev/random" - -int main(int argc, char **argv) -{ - int option; - long max_output = -1; - char *output_filepath = NULL; - bool repeat = false; - bool echo = false; - char delimiter = '\n'; - long range_start = -1; - long range_stop = -1; - - while ((option = getopt(argc, argv, "ei:n:o:rz")) != -1) - { - - switch (option) - { - case 'e': echo = true; break; - case 'r': repeat = true; break; - case 'o': output_filepath = optarg; break; - case 'z': delimiter = '\0'; break; - - case 'i': - { - char *hyphen = strchr(optarg, '-'); - if (hyphen == NULL) - fatal("invalid input range '%s'", optarg); - *hyphen = '\0'; - range_start = parse_ulong(optarg, "invalid input range"); - *hyphen = '-'; - range_stop = parse_ulong(hyphen + 1, "invalid input range"); - if (range_start > range_stop) - fatal("invalid input range '%s'", optarg); - break; - } - - case 'n': - max_output = parse_ulong(optarg, "invalid line count"); - break; - } - - } - if (range_start != -1 && echo) - fatal("cannot combine -e and -i options"); - if (range_start != -1 && optind != argc) - fatal("extra operand '%s'", argv[optind]); - if (!echo && argc - optind > 1) - fatal("extra operand '%s'", argv[optind]); - - unsigned int seed; - FILE *seed_file = fopen(RANDOM_FILEPATH, "r"); - if (seed_file == NULL) - fatal("%s: %s", RANDOM_FILEPATH, strerror(errno)); - fread(&seed, sizeof(seed), 1, seed_file); - srand(seed); - - t_lines lines; - lines_init(&lines); - - if (range_start != -1) - { - size_t buf_len = NUMBER_LEN(LONG_MAX); - for (; range_start <= range_stop; range_start++) - { - char *line = malloc(buf_len * sizeof(char)); - if (line == NULL) - fatal("%s", strerror(errno)); - sprintf(line, "%ld", range_start); - lines_push(&lines, line); - } - } - else if (echo) - { - for (; optind < argc; optind++) - { - char *line = strdup(argv[optind]); - if (line == NULL) - fatal("%s", strerror(errno)); - lines_push(&lines, line); - } - } - else - { - FILE *input = stdin; - if (optind != argc) - { - input = fopen(argv[optind], "r"); - if (input == NULL) - fatal("%s: %s", argv[optind], strerror(errno)); - } - - char *line = NULL; - size_t line_size = 0; - errno = 0; - while (getdelim(&line, &line_size, delimiter, input) != -1) - { - lines_push(&lines, line); - line = NULL; - line_size = 0; - } - if (errno != 0) - { - fclose(input); - fatal("%s: %s", argv[optind], strerror(errno)); - } - fclose(input); - } - - if (lines.len == 0) - { - lines_destroy(&lines); - return EXIT_SUCCESS; - } - for (size_t i = lines.len - 1; i > 0; i--) - { - size_t j = rand() % i; - char *tmp = lines.data[i]; - lines.data[i] = lines.data[j]; - lines.data[j] = tmp; - } - - FILE *output = stdout; - if (output_filepath != NULL) - { - output = fopen(output_filepath, "w"); - if (output == NULL) - fatal("%s: %s", output_filepath, strerror(errno)); - } - - if (repeat) - { - for (size_t i = 0; true; i = (i + 1) % lines.len) - { - if (fputs(lines.data[i], output) == EOF) - break; - if (echo || range_start != -1) - if (fputc('\n', output) == EOF) - break; - } - } - else - { - for (size_t i = 0; - max_output == -1 ? (i < lines.len) : (i < (unsigned long)max_output); - i++) - { - if (fputs(lines.data[i], output) == EOF) - break; - if (echo || range_start != -1) - if (fputc('\n', output) == EOF) - break; - } - } - fclose(output); - - lines_destroy(&lines); - return EXIT_SUCCESS; -} diff --git a/src/basename.c b/src/basename.c new file mode 100644 index 0000000..83f3b8f --- /dev/null +++ b/src/basename.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +char *g_name = "basename"; + +void fatal(char *message) +{ + fprintf(stderr, "%s: %s\n", g_name, message); + exit(EXIT_FAILURE); +} + +char *truncate_path(char *s, char *suffix) +{ + if (*s == '\0') + return s; + + size_t last = strlen(s) - 1; + while (s[last] == '/' && last != 0) + { + s[last] = '\0'; + last--; + } + + char *last_slash = strrchr(s, '/'); + if (last_slash != NULL && last_slash != s) + s = last_slash + 1; + + if (suffix != NULL) + { + char *end = s + strlen(s) - strlen(suffix); + if (end > s && strcmp(end, suffix) == 0) + *end = '\0'; + } + return s; +} + +int main(int argc, char **argv) +{ + int option; + char *suffix = NULL; + bool multiple = false; + char line_delim = '\n'; + + g_name = argv[0]; + while ((option = getopt(argc, argv, "as:z")) != -1) + { + switch (option) + { + case 's': + suffix = optarg; + case 'a': + multiple = true; + break; + case 'z': + line_delim = '\0'; + break; + default: + return EXIT_FAILURE; + } + } + + if (optind == argc) + fatal("missing operand"); + + if (!multiple) + { + argv[optind] = truncate_path(argv[optind], argv[optind + 1]); + fputs(argv[optind], stdout); + putchar(line_delim); + return EXIT_SUCCESS; + } + + for (; optind < argc; optind++) + { + argv[optind] = truncate_path(argv[optind], suffix); + fputs(argv[optind], stdout); + putchar(line_delim); + } + return EXIT_SUCCESS; +} diff --git a/src/chown.c b/src/chown.c new file mode 100644 index 0000000..c181d37 --- /dev/null +++ b/src/chown.c @@ -0,0 +1,194 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + RMODE_FOLLOW_ROOT, + RMODE_FOLLOW_ALL, + RMODE_NO_FOLLOW, +} t_recursive_mode; + +typedef enum +{ + VLEVEL_CHANGE, + VLEVEL_QUIET, + VLEVEL_VERBOSE, +} t_verbose_level; + +static t_verbose_level g_verbosity = VLEVEL_QUIET; +static bool g_follow_link = true; +static bool g_recursive = false; +static t_recursive_mode g_recursive_mode = RMODE_NO_FOLLOW; +static char *g_name = "chown"; +static uid_t g_user_id = -1; +static gid_t g_group_id = -1; + +void verror_log(const char *format, va_list ap) +{ + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + fputc('\n', stderr); +} + +void error_log(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + verror_log(format, ap); + va_end(ap); +} + +void fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + verror_log(format, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +bool parse_id(char *id_str, id_t *id_ptr, bool group) +{ + char *end; + + if (isdigit(*id_str)) + { + errno = 0; + long tmp = strtol(id_str, &end, 10); + if (errno != 0 || tmp > UINT_MAX || *end != '\0') + return false; + *id_ptr = (id_t)tmp; + } + else + { + struct passwd *pw = getpwnam(id_str); + if (pw == NULL) + return false; + if (group) + *id_ptr = pw->pw_gid; + else + *id_ptr = pw->pw_uid; + } + return true; +} + +bool file_chown(char *path, bool root) +{ + char recursive_path[PATH_MAX + 1] = {'\0'}; + struct stat statbuf; + + + if (stat(path, &statbuf) == -1) + { + error_log("cannot access '%s': %s", path, strerror(errno)); + return false; + } + + errno = 0; + if ((!g_recursive && g_follow_link) + || (g_recursive && (g_recursive_mode == RMODE_FOLLOW_ALL + || (root && g_recursive_mode == RMODE_FOLLOW_ROOT)))) + chown(path, g_user_id, g_group_id); + else + lchown(path, g_user_id, g_group_id); + if (errno != 0) + { + error_log("changing ownership of '%s': %s", path, strerror(errno)); + return false; + } + + if (g_recursive) + { + errno = 0; + DIR *directory = opendir(path); + + if (directory == NULL && errno == ENOTDIR) + return true; + if (directory == NULL) + { + error_log("cannot access '%s': %s", path, strerror(errno)); + return false; + } + + strcpy(recursive_path, path); + if (path[strlen(path) - 1] != '/') + strcat(recursive_path, "/"); + + struct dirent *entry; + while ((entry = readdir(directory)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + strcat(recursive_path, entry->d_name); + file_chown(recursive_path, false); + strrchr(recursive_path, '/')[1] = '\0'; + } + closedir(directory); + } + return true; +} + +/* +** +** TODO +** don't change group/user if not asked +** change group to login group is trailling : after user in cmd +** +*/ + +int main(int argc, char **argv) +{ + int option; + + while ((option = getopt(argc, argv, "cfvhRHLP")) != -1) + { + switch (option) + { + case 'c': g_verbosity = VLEVEL_CHANGE; break; + case 'f': g_verbosity = VLEVEL_QUIET; break; + case 'v': g_verbosity = VLEVEL_VERBOSE; break; + case 'h': g_follow_link = false; break; + case 'R': g_recursive = true; break; + case 'H': g_recursive_mode = RMODE_FOLLOW_ROOT; break; + case 'L': g_recursive_mode = RMODE_FOLLOW_ALL; break; + case 'P': g_recursive_mode = RMODE_NO_FOLLOW; break; + } + } + + if (argc - optind == 0) + fatal("missing operand"); + if (argc - optind == 1) + fatal("missing operand after '%s'", argv[optind]); + + char *user_str = argv[optind]; + char *group_str = strchr(argv[optind], ':'); + if (group_str != NULL) + { + *group_str = '\0'; + group_str++; + } + + if (!parse_id(user_str, &g_user_id, false)) + fatal("invalid user '%s'", user_str); + if (group_str != NULL && !parse_id(group_str, &g_group_id, true)) + fatal("invalid group '%s:%s'", user_str, group_str); + + for (optind++; optind < argc; optind++) + file_chown(argv[optind], true); + + return EXIT_SUCCESS; +} diff --git a/src/cut.c b/src/cut.c new file mode 100644 index 0000000..2a7990d --- /dev/null +++ b/src/cut.c @@ -0,0 +1,225 @@ +#define _POSIX_C_SOURCE 200809L +#define _DEFAULT_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include + +#define LIST_VALUE_INFINITY -1 +#define LIST_VALUE_SINGLETON -2 + +#define LINE_BUFFER_SIZE 2024 + +typedef enum +{ + LIST_BYTE, + LIST_CHAR, + LIST_FIELD, + LIST_UNDEFINED, +} t_list_type; + +char *g_name = "cut"; + +void fatal_msg(char *message) +{ + fprintf(stderr, "%s: %s\n", g_name, message); + exit(EXIT_FAILURE); +} + +void fatal_errno(void) +{ + perror(g_name); + exit(EXIT_FAILURE); +} + +int parse_uint(char *s, char **endptr) +{ + if (!isdigit(*s)) + return -1; + unsigned long x = strtoul(s, endptr, 10); + return (int)x; +} + +int main(int argc, char **argv) +{ + int option; + int list_start = LIST_VALUE_INFINITY; + int list_end = LIST_VALUE_INFINITY; + t_list_type list_type = LIST_UNDEFINED; + wchar_t delimiter = '\t'; + bool print_only_delimiter = false; + char line_delimiter = '\n'; + + g_name = argv[0]; + while ((option = getopt(argc, argv, "b:c:d:f:sz")) > 0) + { + + switch (option) + { + case 'b': + case 'c': + case 'f': + if (list_type != LIST_UNDEFINED) + fatal_msg("only one type of list may be specified"); + + switch (option) + { + case 'b': list_type = LIST_BYTE; break; + case 'c': list_type = LIST_CHAR; break; + case 'f': list_type = LIST_FIELD; break; + } + + char *ptr = optarg; + + char *hyphen = strchr(ptr, '-'); + if (hyphen == NULL) // N + { + list_start = parse_uint(ptr, &ptr); + if (*ptr != '\0') + fatal_msg(""); + list_end = LIST_VALUE_SINGLETON; + } + else + { + if (ptr == hyphen) // -M + { + list_end = parse_uint(ptr + 1, &ptr); + printf(">> %d %d |%s|\n", list_start, list_end, ptr); + if (*ptr != '\0') + fatal_msg(""); + } + else + { + list_start = parse_uint(ptr, &ptr); + if (*ptr != '-') + fatal_msg(""); + ptr++; + if (*ptr != '\0') // N-M + { + list_end = parse_uint(ptr, &ptr); + } + if (*ptr != '\0') + fatal_msg(""); + } + } + break; + + case 'd': + delimiter = optarg[0]; + if (delimiter != '\0' && optarg[1] != '\0') + fatal_msg("the delimiter must be a single character"); + break; + + case 's': + print_only_delimiter = true; + break; + case 'z': + line_delimiter = '\0'; + break; + } + } + if (list_type == LIST_UNDEFINED) + fatal_msg("you must specify a list of bytes, characters, or fields"); + if (list_type != LIST_FIELD && delimiter != '\t') + fatal_msg("an input delimiter may be specified only when operating on fields"); + if (list_type != LIST_FIELD && print_only_delimiter) + fatal_msg("suppressing non-delimited lines makes sense only when operating on fields"); + + if (list_start == 0) + fatal_msg("list are numbered from 1"); + if (list_end > 0 && list_start > list_end) + fatal_msg("invalid decreasing range"); + + char *line = NULL; + size_t line_buffer_size = LINE_BUFFER_SIZE; + ssize_t read_size = -1; + + if ((line = malloc(LINE_BUFFER_SIZE)) == NULL) + exit(1); + + char *field; + int counter = 1; + char delimiter_buf[2] = {delimiter, '\0'}; + + if (optarg == NULL) + { + errno = 0; + while ((read_size = getdelim(&line, &line_buffer_size, line_delimiter, stdin)) > 0) + { + switch (list_type) + { + case LIST_BYTE: + case LIST_CHAR: // TODO unicode + if (list_end == LIST_VALUE_SINGLETON) + { + if (read_size - 1 >= list_start) + fputc(line[list_start - 1], stdout); + } + else if (list_start == LIST_VALUE_INFINITY) + { + line[list_end] = '\0'; + fputs(line, stdout); + } + else if (list_end == LIST_VALUE_INFINITY) + { + fputs(&line[list_start - 1], stdout); + } + else + { + line[list_end] = '\0'; + fputs(&line[list_start - 1], stdout); + } + break; + case LIST_FIELD: + while ((field = strsep(&line, delimiter_buf)) != NULL) + { + if (list_end == LIST_VALUE_SINGLETON && counter == list_start) + fputs(field, stdout); + else if (list_start == LIST_VALUE_INFINITY && counter <= list_end) + fputs(field, stdout); + else if (list_end == LIST_VALUE_INFINITY && counter >= list_start) + fputs(field, stdout); + else if (counter >= list_start && counter <= list_end) + fputs(field, stdout); + counter++; + } + break; + } + putchar('\n'); + } + free(line); + if (errno != 0) + fatal_errno(); + return EXIT_SUCCESS; + } + + + /* while (optarg != NULL) */ + /* { */ + /* if (strcmp(optarg, "-") == 0) */ + /* { */ + /* // read stdin */ + /* } */ + /* else */ + /* { */ + /* FILE *file; */ + /* */ + /* if ((file = fopen(optarg, "r")) == NULL) */ + /* fatal_errno(); */ + /* */ + /* while (getdelim(&line, 0, line_delimiter, file)) */ + /* { */ + /* if (field) */ + /* strsep(line, delimiter); */ + /* } */ + /* } */ + /* } */ + + + + return EXIT_SUCCESS; +} diff --git a/src/head.c b/src/head.c new file mode 100644 index 0000000..f2201b5 --- /dev/null +++ b/src/head.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + VLEVEL_QUIET, + VLEVEL_NORMAL, + VLEVEL_VERBOSE, +} t_verbose_level; + +typedef enum +{ + UNIT_LINE, + UNIT_BYTE, +} t_unit; + +char *g_name = "head"; + +#define UNIT_BYTE_BUFFER_SIZE 1028 + +void fatal_printf(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +int file_head(FILE *file, + char *header, t_unit unit, + unsigned long count, char line_delimiter) +{ + char buf[UNIT_BYTE_BUFFER_SIZE] = {'\0'}; + size_t read_size; + size_t write_size; + char *line = NULL; + size_t line_size = 0; + ssize_t ret; + + if (header != NULL) + printf("==> %s <==\n", header); + switch (unit) + { + case UNIT_BYTE: + while (count > 0) + { + read_size = count > UNIT_BYTE_BUFFER_SIZE ? UNIT_BYTE_BUFFER_SIZE : count; + write_size = fread(buf, 1, read_size, file); + fwrite(buf, 1, write_size, stdout); + if (write_size < read_size) + break; + count -= read_size; + } + break; + + case UNIT_LINE: + for (; count > 0 && (ret = getdelim(&line, &line_size, line_delimiter, file)) != -1; count--) + fwrite(line, 1, ret, stdout); + free(line); + break; + } + return 0; +} + +int file_path_head(char *path, + char *header, t_unit unit, + unsigned long count, char line_delimiter) +{ + FILE *file; + + if ((file = fopen(path, "r")) == NULL) + { + fprintf(stderr, "%s: cannot open '%s' for reading: %s\n", g_name, path, strerror(errno)); + return -1; + } + file_head(file, header, unit, count, line_delimiter); + fclose(file); + return 0; +} + +int main(int argc, char **argv) +{ + int option; + t_verbose_level verbose_level = VLEVEL_NORMAL; + t_unit unit = UNIT_LINE; + unsigned long count = 10; + char line_delimiter = '\n'; + + g_name = argv[0]; + + while ((option = getopt(argc, argv, "c:n:qvz")) != -1) + { + switch (option) + { + case 'c': + case 'n': + if (strchr(optarg, '-') != NULL) + fatal_printf("invalid number of bytes: '%s'\n", optarg); + char *endptr; + errno = 0; + // TODO '-' prefix result in tail + // TODO human readable size suffix + count = strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + fatal_printf("invalid number of bytes: '%s'\n", optarg); + if (errno != 0 || *endptr != '\0') + fatal_printf("invalid number of bytes: '%s': %s\n", optarg, strerror(errno)); + switch (option) + { + case 'c': unit = UNIT_BYTE; break; + case 'n': unit = UNIT_LINE; break; + } + break; + + case 'q': verbose_level = VLEVEL_QUIET; break; + case 'v': verbose_level = VLEVEL_VERBOSE; break; + case 'z': line_delimiter = '\0'; break; + } + } + + if (argv[optind] == NULL) + { + file_head( + stdin, verbose_level == VLEVEL_VERBOSE ? "standard input" : NULL, + unit, count, line_delimiter); + return EXIT_SUCCESS; + } + + if (argv[optind + 1] == NULL) + { + if (strcmp(argv[optind], "-") == 0) + file_head( + stdin, verbose_level == VLEVEL_VERBOSE ? "standard input" : NULL, + unit, count, line_delimiter); + else + file_path_head( + argv[optind], verbose_level == VLEVEL_VERBOSE ? argv[optind] : NULL, + unit, count, line_delimiter); + return EXIT_SUCCESS; + } + + for (; optind < argc; optind++) + { + int ret = 0; + + if (strcmp(argv[optind], "-") == 0) + ret = file_head( + stdin, verbose_level != VLEVEL_QUIET ? "standard input" : NULL, + unit, count, line_delimiter); + else + ret = file_path_head( + argv[optind], verbose_level != VLEVEL_QUIET ? argv[optind] : NULL, + unit, count, line_delimiter); + if (argv[optind + 1] != NULL && ret != -1) + putchar('\n'); + } + return EXIT_SUCCESS; +} diff --git a/src/mkdir.c b/src/mkdir.c new file mode 100644 index 0000000..3ab73d1 --- /dev/null +++ b/src/mkdir.c @@ -0,0 +1,86 @@ +#define _POSIX_C_SOURCE 2 + +#include +#include +#include +#include +#include +#include +#include +#include + +char *g_name = "mkdir"; + +int mkdir_wrapper(const char *path, mode_t mode, bool verbose) +{ + int ret; + + if ((ret = mkdir(path, mode)) == -1 && (verbose || errno != EEXIST)) + fprintf(stderr, "%s: cannot create directory '%s': %s\n", g_name, path, strerror(errno)); + else if (verbose) + printf("%s: created directory '%s'\n", g_name, path); + return ret; +} + +int main(int argc, char **argv) +{ + int option; + mode_t mode = 0755; + bool parent = false; + bool verbose = false; + + g_name = argv[0]; + if (argc == 1) + { + fprintf(stderr, "%s: missing operand\n", g_name); + return EXIT_FAILURE; + } + while ((option = getopt(argc, argv, "m:pv")) != -1) + { + switch (option) + { + case 'm': + sscanf(optarg, "%o", &mode); // TODO mode not working + break; + case 'p': + parent = true; + break; + case 'v': + verbose = true; + break; + } + } + for (; optind < argc; optind++) + { + // remove duplicate slash + for (size_t i = 0; argv[optind][i] != '\0'; i++) + { + if (argv[optind][i] == '/' && argv[optind][i + 1] == '/') + { + memmove(&argv[optind][i], &argv[optind][i + 1], strlen(&argv[optind][i + 1]) + 1); + i--; + } + } + + if (parent) + { + char *tmp; + char *searched = argv[optind]; + + if (*searched == '/') + searched++; + for (; (tmp = strchr(searched, '/')) != NULL; searched = tmp + 1) + { + if (tmp[1] == '\0') + continue; + *tmp = '\0'; + mkdir_wrapper(argv[optind], mode, verbose); + *tmp = '/'; + } + mkdir_wrapper(argv[optind], mode, verbose); + } + else + mkdir_wrapper(argv[optind], mode, verbose); + } + return EXIT_SUCCESS; +} diff --git a/src/mv.c b/src/mv.c new file mode 100644 index 0000000..46eab14 --- /dev/null +++ b/src/mv.c @@ -0,0 +1,37 @@ +#define _POSIX_C_SOURCE 2 +#include +#include +#include + + +int main(int argc, char **argv) +{ + int option; + + while ((option = getopt(argc, argv, "bfinS:t:Tuv")) != -1) + { + switch (option) + { + case 'b': + break; + case 'f': + break; + case 'i': + break; + case 'n': + break; + case 'S': + break; + case 't': + break; + case 'T': + break; + case 'u': + break; + case 'v': + break; + } + } + + return EXIT_SUCCESS; +} diff --git a/src/rm.c b/src/rm.c new file mode 100644 index 0000000..2bcf68f --- /dev/null +++ b/src/rm.c @@ -0,0 +1,130 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + FLAG_FORCE = 1 << 0, + FLAG_INTERACTIVE = 1 << 1, + /* FLAG_INTERACTIVE_ONCE = 1 << 2, */ // TODO + FLAG_RECURSIVE = 1 << 3, + FLAG_DIRECTORY = 1 << 4, + FLAG_VERBOSE = 1 << 5, +} t_flags; + +char *g_name = "rm"; + +void log_errno(char *path) +{ + fprintf(stderr, "%s: cannot remove '%s': %s\n", g_name, path, strerror(errno)); +} + +void fatal_errno(char *path) +{ + log_errno(path); + exit(EXIT_FAILURE); +} + +int rm_file(char *path, t_flags flags) +{ + struct stat statbuf; + char recursive_path[PATH_MAX + 1] = {'\0'}; + + if (lstat(path, &statbuf) == -1) + fatal_errno(path); + if (flags & FLAG_INTERACTIVE + || (!(flags & FLAG_FORCE) + && !((statbuf.st_mode & S_IWUSR && statbuf.st_uid == getuid()) || + (statbuf.st_mode & S_IWGRP && statbuf.st_gid == getgid()) || + statbuf.st_mode & S_IWOTH))) + { + printf("%s: remove regular file '%s'? ", g_name, path); + if (tolower(getchar()) != 'y') + return 0; + } + + if (S_ISDIR(statbuf.st_mode) && flags & FLAG_RECURSIVE) + { + DIR *directory; + struct dirent *entry; + + if ((directory = opendir(path)) == NULL) + { + log_errno(path); + return 0; + } + strcpy(recursive_path, path); + if (path[strlen(path) - 1] != '/') + strcat(recursive_path, "/"); + while ((entry = readdir(directory)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + strcat(recursive_path, entry->d_name); + rm_file(recursive_path, flags); + strrchr(recursive_path, '/')[1] = '\0'; + } + closedir(directory); + if (rmdir(path) == -1) + log_errno(path); + else if (flags & FLAG_VERBOSE) + printf("removed directory '%s'\n", path); + } + else if (S_ISDIR(statbuf.st_mode) && flags & FLAG_DIRECTORY) + { + if (rmdir(path) == -1) + log_errno(path); + else if (flags & FLAG_VERBOSE) + printf("removed directory '%s'\n", path); + } + else + { + if (unlink(path) == -1) + log_errno(path); + else if (flags & FLAG_VERBOSE) + printf("removed '%s'\n", path); + } + return 0; +} + +int main(int argc, char **argv) +{ + if (argc == 1) + { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return EXIT_FAILURE; + } + g_name = argv[0]; + + int option; + t_flags flags = 0; + + while ((option = getopt(argc, argv, "firRdv")) != -1) + { + switch (option) + { + case 'f': flags |= FLAG_FORCE; break; + case 'i': flags |= FLAG_INTERACTIVE; break; + /* case 'I': flags |= FLAG_INTERACTIVE_ONCE; break; */ + case 'd': flags |= FLAG_DIRECTORY; break; + case 'v': flags |= FLAG_VERBOSE; break; + + case 'r': + case 'R': + flags |= FLAG_RECURSIVE; break; + break; + } + } + + for (; optind < argc; optind++) + rm_file(argv[optind], flags); + return EXIT_SUCCESS; +} diff --git a/src/seq.c b/src/seq.c new file mode 100644 index 0000000..8307fc4 --- /dev/null +++ b/src/seq.c @@ -0,0 +1,126 @@ +#define _POSIX_C_SOURCE 2 +#include +#include +#include +#include +#include +#include +#include + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +typedef struct +{ + long double value; + int precision; + int integer_len; +} t_num; + +char *g_name = "seq"; + +int parse_num(char *s, t_num *num) +{ + char *tmp; + + errno = 0; + num->value = strtold(s, &tmp); + if (errno != 0 || *tmp != '\0') + { + fprintf(stderr, "%s: invalid floating point argument: '%s'\n", g_name, s); + exit(EXIT_FAILURE); + } + num->precision = 0; + num->integer_len = strlen(s); + if ((tmp = strchr(s, '.')) != NULL) + { + num->precision = strlen(tmp + 1); + num->integer_len = tmp - s; + } + return 0; +} + +int main(int argc, char **argv) +{ + if (argc == 1) + { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return EXIT_FAILURE; + } + + int option; + char *separator = "\n"; + bool padding = false; + + g_name = argv[0]; + + while ((option = getopt(argc, argv, "f:s:w0123456789")) != -1) + { + if (isdigit(option)) + break; + switch (option) + { + case 'f': + exit(EXIT_FAILURE); // TODO + break; + case 's': + separator = optarg; + break; + case 'w': + padding = true; + break; + } + } + + t_num first; + t_num increment; + t_num last; + + first.value = 1.0; + first.precision = 0; + increment.value = 1.0; + increment.precision = 0; + + switch (argc - optind) + { + case 1: + parse_num(argv[optind], &last); + break; + case 2: + parse_num(argv[optind], &first); + parse_num(argv[optind + 1], &last); + break; + case 3: + parse_num(argv[optind], &first); + parse_num(argv[optind + 1], &increment); + if (increment.value == 0) + { + fprintf(stderr, "%s: invalid Zero increment value '%s'\n", argv[0], argv[optind + 3]); + exit(EXIT_FAILURE); + } + parse_num(argv[optind + 2], &last); + break; + default: + fprintf(stderr, "%s: extra operand '%s'\n", argv[0], argv[optind + 3]); + exit(EXIT_FAILURE); + break; + } + + while (increment.value > 0 ? (first.value <= last.value) : (first.value >= last.value)) + { + int precision = MAX(first.precision, increment.precision); + if (!padding) + printf("%.*llf", precision, first.value); + else + { + int width = MAX(last.integer_len, first.integer_len); + if (precision != 0) + width += precision + 1; + printf("%0*.*llf", width, precision, first.value); + } + first.value += increment.value; + if (increment.value > 0 ? (first.value <= last.value) : (first.value >= last.value)) + fputs(separator, stdout); + } + putchar('\n'); + return EXIT_SUCCESS; +} diff --git a/src/shuf.c b/src/shuf.c new file mode 100644 index 0000000..5b4808a --- /dev/null +++ b/src/shuf.c @@ -0,0 +1,237 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUMBER_LEN(x) strlen(#x); + +static char *g_name = "shuf"; + +void fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + fputc('\n', stderr); + va_end(ap); + exit(EXIT_FAILURE); +} + +long parse_ulong(char *str, char *error_msg) +{ + char *end; + long ret; + + errno = 0; + ret = strtol(str, &end, 10); + if (errno != 0 || ret < 0 || *end != '\0') + fatal("%s '%s'", error_msg, optarg); + return ret; +} + +typedef struct +{ + char **data; + size_t len; + size_t cap; +} t_lines; + +#define INITIAL_CAPACITY 64 + +void lines_init(t_lines *lines) +{ + lines->len = 0; + lines->cap = INITIAL_CAPACITY; + lines->data = malloc(lines->cap * sizeof(char*)); + if (lines->data == NULL) + fatal("%s", strerror(errno)); +} + +void lines_destroy(t_lines *lines) +{ + for (size_t i = 0; i < lines->len; i++) + free(lines->data[i]); + free(lines->data); +} + +#define GROWTH_FACTOR 2 + +void lines_push(t_lines *lines, char *line) +{ + if (lines->len >= lines->cap) + { + lines->cap *= GROWTH_FACTOR; + lines->data = realloc(lines->data, lines->cap * sizeof(char*)); + if (lines->data == NULL) + fatal("%s", strerror(errno)); + } + lines->data[lines->len] = line; + lines->len++; +} + +#define RANDOM_FILEPATH "/dev/random" + +int main(int argc, char **argv) +{ + int option; + long max_output = -1; + char *output_filepath = NULL; + bool repeat = false; + bool echo = false; + char delimiter = '\n'; + long range_start = -1; + long range_stop = -1; + + while ((option = getopt(argc, argv, "ei:n:o:rz")) != -1) + { + + switch (option) + { + case 'e': echo = true; break; + case 'r': repeat = true; break; + case 'o': output_filepath = optarg; break; + case 'z': delimiter = '\0'; break; + + case 'i': + { + char *hyphen = strchr(optarg, '-'); + if (hyphen == NULL) + fatal("invalid input range '%s'", optarg); + *hyphen = '\0'; + range_start = parse_ulong(optarg, "invalid input range"); + *hyphen = '-'; + range_stop = parse_ulong(hyphen + 1, "invalid input range"); + if (range_start > range_stop) + fatal("invalid input range '%s'", optarg); + break; + } + + case 'n': + max_output = parse_ulong(optarg, "invalid line count"); + break; + } + + } + if (range_start != -1 && echo) + fatal("cannot combine -e and -i options"); + if (range_start != -1 && optind != argc) + fatal("extra operand '%s'", argv[optind]); + if (!echo && argc - optind > 1) + fatal("extra operand '%s'", argv[optind]); + + unsigned int seed; + FILE *seed_file = fopen(RANDOM_FILEPATH, "r"); + if (seed_file == NULL) + fatal("%s: %s", RANDOM_FILEPATH, strerror(errno)); + fread(&seed, sizeof(seed), 1, seed_file); + srand(seed); + + t_lines lines; + lines_init(&lines); + + if (range_start != -1) + { + size_t buf_len = NUMBER_LEN(LONG_MAX); + for (; range_start <= range_stop; range_start++) + { + char *line = malloc(buf_len * sizeof(char)); + if (line == NULL) + fatal("%s", strerror(errno)); + sprintf(line, "%ld", range_start); + lines_push(&lines, line); + } + } + else if (echo) + { + for (; optind < argc; optind++) + { + char *line = strdup(argv[optind]); + if (line == NULL) + fatal("%s", strerror(errno)); + lines_push(&lines, line); + } + } + else + { + FILE *input = stdin; + if (optind != argc) + { + input = fopen(argv[optind], "r"); + if (input == NULL) + fatal("%s: %s", argv[optind], strerror(errno)); + } + + char *line = NULL; + size_t line_size = 0; + errno = 0; + while (getdelim(&line, &line_size, delimiter, input) != -1) + { + lines_push(&lines, line); + line = NULL; + line_size = 0; + } + if (errno != 0) + { + fclose(input); + fatal("%s: %s", argv[optind], strerror(errno)); + } + fclose(input); + } + + if (lines.len == 0) + { + lines_destroy(&lines); + return EXIT_SUCCESS; + } + for (size_t i = lines.len - 1; i > 0; i--) + { + size_t j = rand() % i; + char *tmp = lines.data[i]; + lines.data[i] = lines.data[j]; + lines.data[j] = tmp; + } + + FILE *output = stdout; + if (output_filepath != NULL) + { + output = fopen(output_filepath, "w"); + if (output == NULL) + fatal("%s: %s", output_filepath, strerror(errno)); + } + + if (repeat) + { + for (size_t i = 0; true; i = (i + 1) % lines.len) + { + if (fputs(lines.data[i], output) == EOF) + break; + if (echo || range_start != -1) + if (fputc('\n', output) == EOF) + break; + } + } + else + { + for (size_t i = 0; + max_output == -1 ? (i < lines.len) : (i < (unsigned long)max_output); + i++) + { + if (fputs(lines.data[i], output) == EOF) + break; + if (echo || range_start != -1) + if (fputc('\n', output) == EOF) + break; + } + } + fclose(output); + + lines_destroy(&lines); + return EXIT_SUCCESS; +} diff --git a/src/tee.c b/src/tee.c new file mode 100644 index 0000000..4df5074 --- /dev/null +++ b/src/tee.c @@ -0,0 +1,68 @@ +#define _POSIX_C_SOURCE 2 // getopt +#define _XOPEN_SOURCE 500 // sigaction +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 2048 + +char *g_name = "tee"; + +int main(int argc, char **argv) +{ + int option; + bool append = false; + + g_name = argv[0]; + + struct sigaction action; + action.sa_handler = SIG_IGN; + action.sa_flags = SA_NODEFER; + + while ((option = getopt(argc, argv, "ai")) != -1) + { + switch (option) + { + case 'a': + append = true; + break; + case 'i': + sigaction(SIGINT, &action, NULL); + break; + } + } + + size_t files_num = argc - optind; + FILE **files = calloc(files_num, sizeof(FILE*)); + if (files == NULL) + return EXIT_FAILURE; + + for (size_t i = 0; i < files_num; i++) + { + files[i] = fopen(argv[optind + i], append ? "a" : "w"); + if (files[i] == NULL) + { + fprintf(stderr, "%s: %s: %s\n", g_name, argv[optind + i], strerror(errno)); + i--; + files_num--; + } + } + + char buf[BUFFER_SIZE + 1] = {0}; + size_t read_size = 0; + while ((read_size = fread(buf, 1, BUFFER_SIZE, stdin)) > 0) + { + fwrite(buf, 1, read_size, stdout); + for (size_t i = 0; i < files_num; i++) + fwrite(buf, 1, read_size, files[i]); + } + + for (size_t i = 0; i < files_num; i++) + fclose(files[i]); + free(files); + return EXIT_SUCCESS; +} diff --git a/src/tr.c b/src/tr.c new file mode 100644 index 0000000..a423794 --- /dev/null +++ b/src/tr.c @@ -0,0 +1,230 @@ +#define _POSIX_C_SOURCE 2 +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + FLAG_COMPLEMENT = 1 << 0, + FLAG_DELETE = 1 << 1, + FLAG_SQUEEZE = 1 << 2, +} t_flags; + + +typedef enum +{ + MTAG_CHAR, + MTAG_CLASS, + MTAG_RANGE, + /* MTAG_REPEAT, */ + /* MTAG_EQUIVALENT, */ // e.g [=e=] == eéèê +} t_matcher_tag; + +typedef struct +{ + t_matcher_tag tag; + union + { + char c; + int (*class)(int c); + char range[2]; + struct + { + char c; + size_t count; + } repeat; + }; +} t_matcher; + +struct char_class_entry { + char *id; + int (*func)(int c); +}; + +struct char_class_entry char_classes[] = { + {"[:alnum:]", isalnum}, + {"[:alpha:]", isalpha}, + {"[:blank:]", isblank}, + {"[:cntrl:]", iscntrl}, + {"[:digit:]", isdigit}, + {"[:graph:]", isgraph}, + {"[:lower:]", islower}, + {"[:print:]", isprint}, + {"[:punct:]", ispunct}, + {"[:space:]", isspace}, + {"[:upper:]", isupper}, + {"[:xdigit:]", isxdigit}, +}; + +typedef struct +{ + t_matcher *array; + size_t count; +} t_matchers; + +void matchers_push(t_matchers *matchers, t_matcher_tag tag, void *data) +{ + t_matcher *pushed; + + matchers->count++; + matchers->array = realloc(matchers->array, matchers->count); + assert(matchers->array != NULL); + pushed = &matchers->array[matchers->count - 1]; + pushed->tag = tag; + switch (tag) + { + case MTAG_CHAR: + pushed->c = *(char*)data; + break; + case