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}