]> git.ipfire.org Git - pakfire.git/commitdiff
path: Add a simple path library to normalize paths
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 6 Oct 2023 13:22:46 +0000 (13:22 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 6 Oct 2023 13:22:46 +0000 (13:22 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
.gitignore
Makefile.am
src/libpakfire/include/pakfire/path.h [new file with mode: 0644]
src/libpakfire/path.c [new file with mode: 0644]
tests/libpakfire/path.c [new file with mode: 0644]

index 11f11b3e6a13c56bbbb71caa6e37f78a21cddfe4..b0c38bbafd1d25c04ba7da361556029bb41f7639 100644 (file)
@@ -32,6 +32,7 @@
 /tests/libpakfire/package
 /tests/libpakfire/packager
 /tests/libpakfire/parser
+/tests/libpakfire/path
 /tests/libpakfire/progressbar
 /tests/libpakfire/repo
 /tests/libpakfire/snapshot
index 3205afa93f967ce9374e05d2454db1db7fac5fcb..c164143201ca67c4bf0ecc6bcff14a112029c3a8 100644 (file)
@@ -235,6 +235,7 @@ libpakfire_la_SOURCES = \
        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 \
@@ -274,6 +275,7 @@ pkginclude_HEADERS += \
        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 \
@@ -507,6 +509,7 @@ check_PROGRAMS += \
        tests/libpakfire/package \
        tests/libpakfire/packager \
        tests/libpakfire/parser \
+       tests/libpakfire/path \
        tests/libpakfire/repo \
        tests/libpakfire/snapshot \
        tests/libpakfire/string \
@@ -728,6 +731,18 @@ tests_libpakfire_parser_CFLAGS = \
 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
 
diff --git a/src/libpakfire/include/pakfire/path.h b/src/libpakfire/include/pakfire/path.h
new file mode 100644 (file)
index 0000000..4390bec
--- /dev/null
@@ -0,0 +1,30 @@
+/*#############################################################################
+#                                                                             #
+# 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 */
diff --git a/src/libpakfire/path.c b/src/libpakfire/path.c
new file mode 100644 (file)
index 0000000..2fa08cf
--- /dev/null
@@ -0,0 +1,246 @@
+/*#############################################################################
+#                                                                             #
+# 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;
+}
diff --git a/tests/libpakfire/path.c b/tests/libpakfire/path.c
new file mode 100644 (file)
index 0000000..4c9218d
--- /dev/null
@@ -0,0 +1,65 @@
+/*#############################################################################
+#                                                                             #
+# 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);
+}