realpath.c (4638B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #define _XOPEN_SOURCE 700 // realpath is in XSI
- #include "../lib/fs.h"
- #include <errno.h>
- #include <limits.h> // PATH_MAX
- #include <stdbool.h>
- #include <stdio.h> // fprintf(), puts()
- #include <stdlib.h> // realpath()
- #include <string.h> // strncmp(), strnlen, strerror
- #include <unistd.h> // getopt
- static bool must_exists = false;
- static bool offline = false;
- static char *argv0 = NULL;
- static char sep = '\n';
- static int
- print_realpath(char *path)
- {
- char *file = NULL;
- if(offline)
- file = offline_realpath(path, NULL);
- else
- file = realpath(path, NULL);
- if(file)
- {
- if(printf("%s", file) < 0)
- {
- fprintf(stderr, "%s: error: Failed writing to stdout: %s\n", argv0, strerror(errno));
- free(file);
- return 1;
- }
- free(file);
- return 0;
- }
- if(must_exists || errno != ENOENT)
- {
- fprintf(stderr, "%s: error: Failed canonilizing \"%s\": %s\n", argv0, path, strerror(errno));
- return 1;
- }
- char *child = path_split_static(path, true);
- if(child == NULL)
- {
- // Return as if realpath just failed
- fprintf(stderr, "%s: error: Failed canonilizing \"%s\": %s\n", argv0, path, strerror(errno));
- return 1;
- }
- errno = 0;
- char *parent = realpath(path, NULL);
- if(!parent)
- {
- fprintf(stderr,
- "%s: error: Failed canonilizing parent of full path \"%s/%s\": %s\n",
- argv0,
- path,
- child,
- strerror(errno));
- return 1;
- }
- if(printf("%s/%s", parent, child) < 0)
- {
- fprintf(stderr, "%s: error: Failed writing to stdout: %s\n", argv0, strerror(errno));
- free(parent);
- return 1;
- }
- free(parent);
- return 0;
- }
- static void
- usage_realpath(void)
- {
- fprintf(stderr, "Usage: realpath [-E|-e] [-n|-z] path...\n");
- }
- static int
- main_realpath(int argc, char *argv[])
- {
- must_exists = false;
- int offset_sep = 0;
- int c = -1;
- while((c = getopt(argc, argv, ":Eemnsz")) != -1)
- {
- switch(c)
- {
- case 'E':
- must_exists = false;
- break;
- case 'e':
- must_exists = true;
- break;
- case 'n':
- offset_sep = 1;
- break;
- case 's':
- offline = true;
- break;
- case 'z':
- sep = '\0';
- break;
- case ':':
- fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
- usage_realpath();
- return 1;
- case '?':
- fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
- usage_realpath();
- return 1;
- }
- }
- argv += optind;
- argc -= optind;
- if(argc == 0)
- {
- fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
- usage_realpath();
- return 1;
- }
- for(int i = 0; i < argc; i++)
- {
- if(print_realpath(argv[i]) != 0) return 1;
- if(i != argc - offset_sep) printf("%c", sep);
- }
- return 0;
- }
- static void
- usage_readlink(void)
- {
- fprintf(stderr, "Usage: readlink [-f|-e] [-n|-z] file...\n");
- }
- static int
- main_readlink(int argc, char *argv[])
- {
- must_exists = true;
- bool canonicalize = false;
- int offset_sep = 0;
- int c = -1;
- while((c = getopt(argc, argv, ":femnz")) != -1)
- {
- switch(c)
- {
- case 'f':
- canonicalize = true;
- must_exists = false;
- break;
- case 'e':
- must_exists = true;
- break;
- case 'n':
- offset_sep = 1;
- break;
- case 'z':
- sep = '\0';
- break;
- case ':':
- fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
- usage_readlink();
- return 1;
- case '?':
- fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
- usage_readlink();
- return 1;
- }
- }
- argv += optind;
- argc -= optind;
- if(argc == 0)
- {
- fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
- usage_readlink();
- return 1;
- }
- for(int i = 0; i < argc; i++)
- {
- char *path = argv[i];
- if(canonicalize)
- {
- if(print_realpath(path) != 0) return 1;
- if(i != argc) printf("%c", sep);
- continue;
- }
- char buf[PATH_MAX] = "";
- if(readlink(path, buf, sizeof(buf) - 1) < 0)
- {
- fprintf(stderr,
- "%s: error: Failed reading symbolic link of '%s': %s\n",
- argv0,
- path,
- strerror(errno));
- return 1;
- }
- printf("%s", buf);
- if(i != argc - offset_sep) printf("%c", sep);
- }
- return 0;
- }
- int
- main(int argc, char *argv[])
- {
- argv0 = static_basename(argv[0]);
- if(strcmp(argv0, "realpath") == 0) return main_realpath(argc, argv);
- if(strcmp(argv0, "readlink") == 0) return main_readlink(argc, argv);
- fprintf(stderr, "%s: error: Unknown utility '%s', expected realpath or readlink\n", argv0, argv0);
- return 1;
- }