diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/basename.c | 92 | ||||
| -rw-r--r-- | src/main.c | 4 |
3 files changed, 96 insertions, 2 deletions
@@ -3,7 +3,7 @@ CFLAGS := CFLAGS_EXTRA := -std=c23 -Wpedantic -Wall -Wextra LDFLAGS := PREFIX := -UTILS := true false +UTILS := true false basename BIN := futiles OBJS := $(patsubst %.c, %.o, $(wildcard src/*.c)) diff --git a/src/basename.c b/src/basename.c new file mode 100644 index 0000000..1785754 --- /dev/null +++ b/src/basename.c @@ -0,0 +1,92 @@ +#include <getopt.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define BASENAME_USAGE "Usage: basename NAME [SUFFIX]\n" +#define BASENAME_USE "Return non-directory portion of a pathname\n" + +static char *remove_trailing_slashes(char *pathname) { + size_t j = strlen(pathname); + char *last_char = pathname + j - 1; + for (;last_char > pathname; last_char--) { + if (*last_char != '/') { + break; + } + *last_char = '\0'; + } + + return last_char; +} + +int basename_main(int argc, char *argv[]) { + char *result = NULL; + char *suffix = NULL; + char *last_slash = NULL; + char *last_char = NULL; + char *suffix_end = NULL; + bool suffix_match = true; + size_t suffix_len = 0; + char *cursor = NULL; + + + if (getopt(argc, argv, "") != -1) { + fputs(BASENAME_USAGE, stderr); + return EXIT_FAILURE; + } + + if (argc < 2 || argc > 3) { + fprintf(stderr, "%s%s", BASENAME_USAGE, BASENAME_USE); + return EXIT_FAILURE; + } + + result = argv[1]; + suffix = argc == 3 ? argv[2] : NULL; + + /* Return empty string on empty input string */ + if (result[0] == '\0') { + puts(""); + return EXIT_SUCCESS; + } + + /* Remove trailing whitespace and return "/" if the path was all slashes */ + last_char = remove_trailing_slashes(result); + if (last_char == result) { + puts("/"); + return EXIT_SUCCESS; + } + + /* Keep only the last node */ + last_slash = strrchr(result, '/'); + result = last_slash == NULL ? result : last_slash + 1; + + + /* Remove suffix or return it as is if it's equal to last_node */ + if (suffix == NULL) { + puts(result); + return EXIT_SUCCESS; + } + + suffix_len = strlen(suffix); + if (suffix_len > strlen(result)) { + puts(result); + return EXIT_SUCCESS; + } + + cursor = last_char; + for (suffix_end = suffix + suffix_len - 1; suffix_end > suffix; suffix_end--) { + if (*cursor != *suffix_end) { + suffix_match = false; + break; + } + cursor--; + } + + if (suffix_match && cursor != result) { + *cursor = '\0'; + } + + puts(result); + return EXIT_SUCCESS; +} @@ -7,6 +7,7 @@ typedef int util_main(int, char **); extern util_main true_main; extern util_main false_main; +extern util_main basename_main; typedef struct futiles_util futiles_util_t; struct futiles_util { @@ -18,7 +19,8 @@ int display_utils(int, char **); const futiles_util_t utils[] = { { .name = "futiles", .main = display_utils }, { .name = "true", .main = true_main }, - { .name = "false", .main = false_main } + { .name = "false", .main = false_main }, + { .name = "basename", .main = basename_main } }; #define UTILS_NB (sizeof(utils) / sizeof(futiles_util_t)) |
