diff options
Diffstat (limited to 'src/stdlib')
-rw-r--r-- | src/stdlib/abspath.c | 106 | ||||
-rw-r--r-- | src/stdlib/relpath.c | 98 |
2 files changed, 204 insertions, 0 deletions
diff --git a/src/stdlib/abspath.c b/src/stdlib/abspath.c new file mode 100644 index 0000000..a0f0871 --- /dev/null +++ b/src/stdlib/abspath.c @@ -0,0 +1,106 @@ +/** + * slibc — Yet another C library + * Copyright © 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + + + +/** + * Get the absolute path of a file. + * It will remove all redundant slashes, all "./":s, + * and all "../":s, but not resolve symbolic links. + * + * This is a slibc extension. + * + * @param file The file. + * @param ref The directory the file's specified path is + * relative, `NULL` for the current working directory. + * @return The file's absolute pathname. Will end with a slash + * if `file` does. (Or if `ref` does but file is empty.) + * + * @throws ENOMEM The process cannot allocate more memory. + */ +char* abspath(const char* file, const char* ref) /* XXX may also fail as get_current_dir_name */ +{ + char* ref_ = NULL; + char* rc; + char* rc_; + size_t p, q; + int saved_errno; + + if (ref == NULL) + { + ref = ref_ = get_current_dir_name(); + if (ref == NULL) + return NULL; + } + + rc = malloc((strlen(file) + strlen(ref) + 3) * sizeof(char)); + if (rc_ = rc, rc == NULL) + { + saved_errno = errno; + free(ref_); + errno = saved_errno; + return NULL; + } + + /* Create absolute path, with //, /./, and /../. */ + if (*file != '/') + { + rc_ = stpcpy(rc_, ref); + *rc_++ = '/'; + } + strcpy(rc_, file); + + /* Remove redundant slashes. */ + p = q = 1; + while (rc[p]) + if ((rc[p] == '/') && (rc[p - 1] == '/')) + p++; + else + rc[q++] = rc[p++]; + rc[q] = '\0'; + + /* Remove ./:s. */ + p = q = 0; + while (rc[p]) + if ((rc[p] == '/') && (rc[p + 1] == '.') && (rc[p + 2] == '/')) + p += 2; + else + rc[q++] = rc[p++]; + rc[q] = '\0'; + + /* Remove ../:s. */ + for (;;) + { + rc_ = strstr(rc, "/../"); + if (rc_ == NULL) + break; + memmove(rc_, rc_ + 3, strlen(rc_ + 2) * sizeof(char)); + if (rc_ == rc) + continue; + for (p = 0; rc_[-p] != '/'; p++); + memmove(rc_ - p, rc_, (strlen(rc_) + 1) * sizeof(char)); + } + + free(ref_); + return rc; +} + diff --git a/src/stdlib/relpath.c b/src/stdlib/relpath.c new file mode 100644 index 0000000..92888fa --- /dev/null +++ b/src/stdlib/relpath.c @@ -0,0 +1,98 @@ +/** + * slibc — Yet another C library + * Copyright © 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + + + +/** + * Get the relative path of a file. + * + * This is a slibc extension. + * + * @param file The file. + * @param ref The file the result shall be relative to, + * `NULL` for the current working directory. + * If and only if this path ends with a slash, + * (or if it is `NULL`,) it will be treated as + * a directory in which a symbolic link, with + * the result as it target, can be located to + * point to `file`. + * + * @throws ENOMEM The process cannot allocate more memory. + */ +char* relpath(const char* file, const char* ref) /* XXX may also fail as get_current_dir_name */ +{ + int saved_errno; + char* cwd = NULL; + char* absfile = NULL; + char* absref = NULL; + char* rc; + char* prc; + size_t ptr, p, n = 0; + + if ((cwd = get_current_dir_name()) == NULL) goto fail; + if ((absfile = abspath(file, cwd)) == NULL) goto fail; + if (ref) + if ((absref = abspath(ref, cwd)) == NULL) goto fail; + if (absref == NULL) + { + p = strlen(cwd); + absref = malloc((p + 1) + sizeof(char)); + memcpy(absref, cwd, p); + absref[p] = '/'; + absref[p + 1] = '\0'; + } + else + strrchr(absref, '/')[1] = '\0'; + + ptr = 0; + while (absfile[ptr] && (absfile[ptr] == absref[ptr])) + ptr++; + while (absfile[ptr] != '/') + ptr--; + ptr++; + + for (p = ptr; absref[p]; p++) + if (absref[p] == '/') + n += 1; + + rc = malloc((strlen(absfile + ptr) + 1 + 3 * n) * sizeof(char)); + if (prc = rc, rc == NULL) + goto fail; + + while (n--) + prc = stpcpy(prc, "../"); + strcpy(prc, absfile + ptr); + + free(cwd); + free(absfile); + free(absref); + return rc; + + fail: + saved_errno = errno; + free(cwd); + free(absfile); + free(absref); + errno = saved_errno; + return NULL; +} + |