diff options
| author | Victor Mignot <victor@vmignot.fr> | 2026-02-19 17:35:50 +0100 |
|---|---|---|
| committer | Victor Mignot <victor@vmignot.fr> | 2026-03-09 19:53:52 +0100 |
| commit | 309765abe1eedb58c2b8271793257b198012f445 (patch) | |
| tree | 01fc4e195f70e262709d480cc933306653567c11 /src | |
| parent | c856ca9339e7154699a8ac81ec943d45d8c5ffe8 (diff) | |
| download | futiles-309765abe1eedb58c2b8271793257b198012f445.tar.gz | |
Add `basename` implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/basename.c | 92 | ||||
| -rw-r--r-- | src/main.c | 4 |
2 files changed, 95 insertions, 1 deletions
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)) |
