diff options
| author | Charles Cabergs <me@cacharle.xyz> | 2020-08-22 19:43:03 +0200 |
|---|---|---|
| committer | Charles Cabergs <me@cacharle.xyz> | 2020-08-22 19:43:03 +0200 |
| commit | c40f79737a4ce6ae0cd8bd6ea7f302217333e486 (patch) | |
| tree | 5afe1af26880bd7f8b16ee33fb8e14f99f665938 /src | |
| parent | f204fe59bd5fa537bf84cb522339c92b16f5a909 (diff) | |
| download | coreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.tar.gz coreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.tar.bz2 coreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.zip | |
Added Makefile
Diffstat (limited to 'src')
| -rw-r--r-- | src/basename.c | 83 | ||||
| -rw-r--r-- | src/chown.c | 194 | ||||
| -rw-r--r-- | src/cut.c | 225 | ||||
| -rw-r--r-- | src/head.c | 166 | ||||
| -rw-r--r-- | src/mkdir.c | 86 | ||||
| -rw-r--r-- | src/mv.c | 37 | ||||
| -rw-r--r-- | src/rm.c | 130 | ||||
| -rw-r--r-- | src/seq.c | 126 | ||||
| -rw-r--r-- | src/shuf.c | 237 | ||||
| -rw-r--r-- | src/tee.c | 68 | ||||
| -rw-r--r-- | src/tr.c | 230 |
11 files changed, 1582 insertions, 0 deletions
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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <limits.h> +#include <dirent.h> +#include <errno.h> +#include <ctype.h> + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdbool.h> +#include <errno.h> + +#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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> + + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> + +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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <signal.h> + +#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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdbool.h> +#include <string.h> +#include <assert.h> + +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 MTAG_CLASS: + pushed->class = (int (*)(int))data; + break; + case MTAG_RANGE: + pushed->range[0] = ((short)data & 0x00ff) >> 0; + pushed->range[1] = ((short)data & 0xff00) >> 8; + break; + default: + abort(); + } +} + +bool isodigit(char c) { return c >= '0' && c <= '7'; } + +void parse(t_matchers *matchers, char *s) +{ + for (size_t i = 0; s[i] != '\0'; i++) + { + if (s[i] == '\\') + { + memmove(&s[i], &s[i + 1], strlen(&s[i + 1])); + + switch (s[i]) + { + case 'a': s[i] = '\a'; break; + case 'b': s[i] = '\b'; break; + case 'f': s[i] = '\f'; break; + case 'n': s[i] = '\n'; break; + case 'r': s[i] = '\r'; break; + case 't': s[i] = '\t'; break; + case 'v': s[i] = '\v'; break; + } + + if (isodigit(s[i])) + { + char num[4] = {'\0'}; + strncpy(num, &s[i], 3); + char *end; + s[i] = strtol(num, &end, 8); + memmove(&s[i + 1], &s[i + 1 + (end - num)], strlen(&s[i + 1 + (end - num)])); + } + matchers_push(matchers, MTAG_CHAR, (void*)s[i]); + } + else if (s[i + 1] == '-' && s[i + 2] != '\0') + matchers_push(matchers, MTAG_RANGE, (void*)(s[i] | (s[i] << 8))); + else if (s[i] == '[' && s[i + 1] == ':') + { + /* for (size_t j = 0; j < sizeof(char_classes) / sizeof(char_class_entry); j++) */ + /* { */ + /* if (strncmp( */ + /* } */ + } + else + matchers_push(matchers, MTAG_CHAR, (void*)s[i]); + } +} + +char *g_name = "tr"; + +void fatal(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); +} + +#define BUFFER_SIZE 256 + +/* +** no 't' flag, truncate is the default behavior +*/ + +int main(int argc, char **argv) +{ + int option; + t_flags flags = 0; + + while ((option = getopt(argc, argv, "cCds")) != -1) + { + switch (option) + { + case 'c': + case 'C': + flags |= FLAG_COMPLEMENT; + break; + case 'd': + flags |= FLAG_DELETE; + break; + case 's': + flags |= FLAG_SQUEEZE; + break; + } + } + if (optind == argc) + fatal("missing operand\n"); + if (argc - optind == 3) + fatal("extra operand '%s'\n", argv[argc - 1]); + + if (flags & FLAG_DELETE && flags & FLAG_SQUEEZE && argc - optind != 2) + fatal("missing operand after '%s'\n" + "Two strings must be given when both deleting and squezzing repeats.\n", + argv[optind]); + + if (flags & FLAG_DELETE && argc - optind != 1) + fatal("extra operand '%s'\n", + "Only one string may be given when deleting without squeezing repeats.\n", + argv[optind + 1]); + + t_matchers set1 = { .array = NULL, .count = 0 }; + t_matchers set2 = { .array = NULL, .count = 0 }; + + char buf[BUFFER_SIZE + 1] = {'\0'}; + size_t read_size; + + while ((read_size = fread(buf, sizeof(char), BUFFER_SIZE, stdin)) > 0) + { + for (size_t i = 0; i < read_size; i++) + { + /* if (flags & FLAG_DELETE && flags & FLAG_SQUEEZE) */ + /* { */ + /* */ + /* } */ + /* else if (flags & FLAG_DELETE && match()) */ + /* { */ + /* memmove(&buf[i], &buf[i + 1], read_size - i); */ + /* */ + /* } */ + /* else if (flags & FLAG_SQUEEZE) */ + /* { */ + /* while (match(s[i + 1])) */ + /* */ + /* memmove(&buf[i + 1], &buf[i + 2], read_size - i); */ + /* } */ + /* else */ + /* { */ + /* if (match()) */ + /* buf[i] = set2 */ + /* } */ + } + } + + return EXIT_SUCCESS; +} |
