/tests/libpakfire/package
/tests/libpakfire/packager
/tests/libpakfire/parser
+/tests/libpakfire/path
/tests/libpakfire/progressbar
/tests/libpakfire/repo
/tests/libpakfire/snapshot
src/libpakfire/packagelist.c \
src/libpakfire/pakfire.c \
src/libpakfire/parser.c \
+ src/libpakfire/path.c \
src/libpakfire/problem.c \
src/libpakfire/progress.c \
src/libpakfire/pwd.c \
src/libpakfire/include/pakfire/packagelist.h \
src/libpakfire/include/pakfire/pakfire.h \
src/libpakfire/include/pakfire/parser.h \
+ src/libpakfire/include/pakfire/path.h \
src/libpakfire/include/pakfire/private.h \
src/libpakfire/include/pakfire/problem.h \
src/libpakfire/include/pakfire/progress.h \
tests/libpakfire/package \
tests/libpakfire/packager \
tests/libpakfire/parser \
+ tests/libpakfire/path \
tests/libpakfire/repo \
tests/libpakfire/snapshot \
tests/libpakfire/string \
tests_libpakfire_parser_LDADD = \
$(TESTSUITE_LDADD)
+dist_tests_libpakfire_path_SOURCES = \
+ tests/libpakfire/path.c
+
+tests_libpakfire_path_CPPFLAGS = \
+ $(TESTSUITE_CPPFLAGS)
+
+tests_libpakfire_path_CFLAGS = \
+ $(TESTSUITE_CFLAGS)
+
+tests_libpakfire_path_LDADD = \
+ $(TESTSUITE_LDADD)
+
dist_tests_libpakfire_repo_SOURCES = \
tests/libpakfire/repo.c
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2013 Pakfire development team #
+# #
+# 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/>. #
+# #
+#############################################################################*/
+
+#ifndef PAKFIRE_PATH_H
+#define PAKFIRE_PATH_H
+
+#include <stddef.h>
+
+#define pakfire_path_normalize(path) \
+ __pakfire_path_normalize(path, sizeof(path))
+int __pakfire_path_normalize(char* p, const size_t length);
+
+#endif /* PAKFIRE_PATH_H */
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2019 Pakfire development team #
+# #
+# 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 <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pakfire/path.h>
+#include <pakfire/string.h>
+
+struct pakfire_path {
+ int is_absolute;
+
+ unsigned int num_segments;
+ char** segments;
+};
+
+static void pakfire_path_free(struct pakfire_path* path) {
+ if (path->segments) {
+ for (unsigned int i = 0; i < path->num_segments; i++)
+ free(path->segments[i]);
+
+ free(path->segments);
+ }
+
+ free(path);
+}
+
+static int pakfire_path_append(struct pakfire_path* path, const char* segment) {
+ char* s = NULL;
+
+ // Make space
+ path->segments = reallocarray(path->segments, sizeof(*path->segments),
+ path->num_segments + 2);
+
+ // Fail if we could not make space
+ if (!path->segments)
+ return -errno;
+
+ // Copy the segment to the heap
+ s = strdup(segment);
+ if (!s)
+ return -errno;
+
+ // Store the segment in the array
+ path->segments[path->num_segments++] = s;
+
+ return 0;
+}
+
+static int pakfire_path_remove_segment(struct pakfire_path* path, unsigned int idx) {
+ if (idx >= path->num_segments)
+ return -EINVAL;
+
+ // Free the value
+ if (path->segments[idx])
+ free(path->segments[idx]);
+
+ // Move the other segments
+ for (unsigned int i = idx + 1; i < path->num_segments; i++)
+ path->segments[i - 1] = path->segments[i];
+
+ // Decrement the number of segments
+ path->num_segments--;
+
+ return 0;
+}
+
+static int pakfire_path_import_segments(struct pakfire_path* path, const char* s) {
+ char buffer[PATH_MAX];
+ char* p = NULL;
+ int r;
+
+ // Is the path absolute?
+ if (*s == '/')
+ path->is_absolute = 1;
+
+ // Copy path into buffer
+ r = pakfire_string_set(buffer, s);
+ if (r)
+ return r;
+
+ const char* segment = strtok_r(buffer, "/", &p);
+
+ // Append all segments
+ while (segment) {
+ r = pakfire_path_append(path, segment);
+ if (r)
+ break;
+
+ segment = strtok_r(NULL, "/", &p);
+ }
+
+ return r;
+}
+
+static int pakfire_path_parse(struct pakfire_path** path, const char* s) {
+ struct pakfire_path* p = NULL;
+ int r;
+
+ // Check input
+ if (!s || !*s)
+ return -EINVAL;
+
+ // Allocate object
+ p = calloc(1, sizeof(*p));
+ if (!p)
+ return -errno;
+
+ // Import the path
+ r = pakfire_path_import_segments(p, s);
+ if (r)
+ goto ERROR;
+
+ // Success
+ *path = p;
+ return 0;
+
+ERROR:
+ if (p)
+ pakfire_path_free(p);
+
+ return r;
+}
+
+static size_t pakfire_path_length(struct pakfire_path* path) {
+ size_t length = path->num_segments;
+
+ for (unsigned int i = 0; i < path->num_segments; i++)
+ length += strlen(path->segments[i]);
+
+ return length;
+}
+
+static int pakfire_path_to_string(struct pakfire_path* path, char* buffer, const size_t length) {
+ char* p = buffer;
+ char* s = NULL;
+
+ const size_t required_length = pakfire_path_length(path) + 1;
+
+ // Check if we have enough space
+ if (length < required_length)
+ return -ENOBUFS;
+
+ // Return / for empty paths
+ if (!path->num_segments)
+ return __pakfire_string_set(buffer, length, "/");
+
+ for (unsigned int i = 0; i < path->num_segments; i++) {
+ s = path->segments[i];
+
+ // Add separator
+ *p++ = '/';
+
+ // Add the segment
+ while (*s)
+ *p++ = *s++;
+ }
+
+ // Terminate the string
+ *p = '\0';
+
+ return 0;
+}
+
+static int pakfire_path_do_normalize(struct pakfire_path* path) {
+ int r;
+
+ for (unsigned int i = 0; i < path->num_segments; i++) {
+ // Simply remove single dots
+ if (strcmp(path->segments[i], ".") == 0) {
+ r = pakfire_path_remove_segment(path, i);
+ if (r)
+ return r;
+
+ // Go back one step
+ i--;
+
+ // Remove double dots
+ } else if (strcmp(path->segments[i], "..") == 0) {
+ r = pakfire_path_remove_segment(path, i);
+ if (r)
+ return r;
+
+ // Remove the previous element if there is one
+ if (i > 0) {
+ r = pakfire_path_remove_segment(path, i - 1);
+ if (r)
+ return r;
+
+ i--;
+ }
+
+ // Go back one step
+ i--;
+ }
+ }
+
+ return 0;
+}
+
+int __pakfire_path_normalize(char* p, const size_t length) {
+ struct pakfire_path* path = NULL;
+ int r;
+
+ // Parse the path
+ r = pakfire_path_parse(&path, p);
+ if (r)
+ goto ERROR;
+
+ // Normalize the path
+ r = pakfire_path_do_normalize(path);
+ if (r)
+ goto ERROR;
+
+ // Write back the path
+ r = pakfire_path_to_string(path, p, length);
+ if (r)
+ goto ERROR;
+
+ERROR:
+ if (path)
+ pakfire_path_free(path);
+
+ return r;
+}
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2019 Pakfire development team #
+# #
+# 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 <limits.h>
+
+#include <pakfire/path.h>
+#include <pakfire/string.h>
+
+#include "../testsuite.h"
+
+static int test_path_normalize(const struct test* t) {
+ char path[PATH_MAX];
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/bash"));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/usr/bin/bash");
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/"));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/");
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/.."));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/");
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/../.."));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/");
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/bash"));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/usr/bin/bash");
+
+ ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/sh/../bash"));
+ ASSERT_SUCCESS(pakfire_path_normalize(path));
+ ASSERT_STRING_EQUALS(path, "/usr/bin/bash");
+
+ return EXIT_SUCCESS;
+
+FAIL:
+ return EXIT_FAILURE;
+}
+
+int main(int argc, const char* argv[]) {
+ testsuite_add_test(test_path_normalize);
+
+ return testsuite_run(argc, argv);
+}