1#include "include/pw.h"
 2#include "include/pwlib/path.h"
 3
 4[[nodiscard]] bool pw_path_normalize(PwValuePtr path, PwValuePtr result)
 5{
 6    PwValue parts = PW_NULL;
 7    if (pw_is_array(path)) {
 8        parts = pw_clone(path);
 9    } else {
10        if (!pw_path_split(path, &parts)) {
11            return false;
12        }
13    }
14    PwValue normalized = PW_NULL;
15    if (!pw_create(PwTypeId_BasicArray, &normalized)) {
16        return false;
17    }
18    unsigned n = pw_array_length(&parts);
19    bool abspath = false;
20    for (unsigned i = 0; i < n; i++) {
21        PwValue item = PW_NULL;
22        if (!pw_array_item(&parts, i, &item)) {
23            return false;
24        }
25        pw_assert(pw_is_string(&item));
26
27        if (i == 0 && pw_equal(&item, "/")) {
28            abspath = true;
29            continue;
30        }
31        if (pw_strlen(&item) == 0) {
32            continue;
33        }
34        if (pw_equal(&item, ".")) {
35            continue;
36        }
37        if (pw_equal(&item, "..")) {
38            unsigned nn = pw_array_length(&normalized);
39            if (nn) {
40                if (!pw_array_del(&normalized, nn - 1, nn)) {
41                    return false;
42                }
43            }
44            continue;
45        }
46        if (!pw_array_append(&normalized, &item)) {
47            return false;
48        }
49    }
50    if (abspath) {
51        PwValue root = PW_STRING("/");
52        if (!pw_array_insert(&normalized, 0, &root)) {
53            return false;
54        }
55    }
56    // return same type as path
57    if (pw_is_array(path)) {
58        pw_move(result, &normalized);
59        return true;
60    } else {
61        return pw_path_join(&normalized, result);
62    }
63}