12#if defined(WIN32) || defined(_WIN32) || \
13 defined(__WIN32) && !defined(__CYGWIN__)
14static enum cwk_path_style path_style = CWK_STYLE_WINDOWS;
16static enum cwk_path_style path_style = CWK_STYLE_UNIX;
24static const char *separators[] = {[CWK_STYLE_WINDOWS] =
"\\/",
25 [CWK_STYLE_UNIX] =
"/"};
39static size_t cwk_path_output_sized(
char *buffer,
size_t buffer_size,
40 size_t position,
const char *str,
size_t length)
42 size_t amount_written;
48 if (buffer_size > position + length) {
49 amount_written = length;
50 }
else if (buffer_size > position) {
51 amount_written = buffer_size - position;
59 if (amount_written > 0) {
60 memmove(&buffer[position], str, amount_written);
68static size_t cwk_path_output_current(
char *buffer,
size_t buffer_size,
73 return cwk_path_output_sized(buffer, buffer_size, position,
".", 1);
76static size_t cwk_path_output_back(
char *buffer,
size_t buffer_size,
81 return cwk_path_output_sized(buffer, buffer_size, position,
"..", 2);
84static size_t cwk_path_output_separator(
char *buffer,
size_t buffer_size,
88 return cwk_path_output_sized(buffer, buffer_size, position,
89 separators[path_style], 1);
92static size_t cwk_path_output_dot(
char *buffer,
size_t buffer_size,
96 return cwk_path_output_sized(buffer, buffer_size, position,
".", 1);
99static size_t cwk_path_output(
char *buffer,
size_t buffer_size,
size_t position,
106 length = strlen(str);
107 return cwk_path_output_sized(buffer, buffer_size, position, str, length);
110static void cwk_path_terminate_output(
char *buffer,
size_t buffer_size,
113 if (buffer_size > 0) {
114 if (pos >= buffer_size) {
115 buffer[buffer_size - 1] =
'\0';
122static bool cwk_path_is_string_equal(
const char *first,
const char *second,
127 if (path_style == CWK_STYLE_UNIX) {
128 return strncmp(first, second, n) == 0;
134 while (*first && *second && n > 0) {
137 if (tolower(*first++) != tolower(*second++)) {
146 return n == 0 || (*first ==
'\0' && *second ==
'\0');
149static const char *cwk_path_find_next_stop(
const char *c)
153 while (*c !=
'\0' && !cwk_path_is_separator(c)) {
161static const char *cwk_path_find_previous_stop(
const char *begin,
const char *c)
165 while (c > begin && !cwk_path_is_separator(c)) {
171 if (cwk_path_is_separator(c)) {
178static bool cwk_path_get_first_segment_without_root(
const char *path,
183 segment->path = path;
184 segment->segments = segments;
188 if (*segments ==
'\0') {
195 while (cwk_path_is_separator(segments)) {
197 if (*segments ==
'\0') {
203 segment->begin = segments;
207 segments = cwk_path_find_next_stop(segments);
211 segment->size = segments - segment->begin;
212 segment->end = segments;
218static bool cwk_path_get_last_segment_without_root(
const char *path,
224 if (!cwk_path_get_first_segment_without_root(path, path, segment)) {
231 while (cwk_path_get_next_segment(segment)) {
238static bool cwk_path_get_first_segment_joined(
const char **paths,
252 while (paths[sj->path_index] != NULL &&
253 (result = cwk_path_get_first_segment(paths[sj->path_index],
254 &sj->segment)) ==
false) {
265 if (sj->paths[sj->path_index] == NULL) {
269 }
else if (cwk_path_get_next_segment(&sj->segment)) {
283 if (sj->paths[sj->path_index] == NULL) {
291 result = cwk_path_get_first_segment_without_root(sj->paths[sj->path_index],
292 sj->paths[sj->path_index], &sj->segment);
304 if (*sj->paths == NULL) {
309 }
else if (cwk_path_get_previous_segment(&sj->segment)) {
320 if (sj->path_index == 0) {
330 if (sj->path_index == 0) {
331 result = cwk_path_get_last_segment(sj->paths[sj->path_index],
334 result = cwk_path_get_last_segment_without_root(sj->paths[sj->path_index],
345 enum cwk_segment_type type;
359 while (cwk_path_get_previous_segment_joined(sj)) {
364 type = cwk_path_get_segment_type(&sj->segment);
365 if (type == CWK_NORMAL) {
373 }
else if (type == CWK_BACK) {
385static bool cwk_path_segment_normal_will_be_removed(
388 enum cwk_segment_type type;
398 while (cwk_path_get_next_segment_joined(sj)) {
403 type = cwk_path_get_segment_type(&sj->segment);
404 if (type == CWK_NORMAL) {
408 }
else if (type == CWK_BACK) {
426 enum cwk_segment_type type;
434 type = cwk_path_get_segment_type(&sj->segment);
435 if (type == CWK_CURRENT) {
437 }
else if (type == CWK_BACK && absolute) {
439 }
else if (type == CWK_BACK) {
440 return cwk_path_segment_back_will_be_removed(&sjc);
442 return cwk_path_segment_normal_will_be_removed(&sjc);
450 while (cwk_path_segment_will_be_removed(sj, absolute)) {
451 if (!cwk_path_get_next_segment_joined(sj)) {
459static void cwk_path_get_root_windows(
const char *path,
size_t *length)
466 is_device_path =
false;
478 if (cwk_path_is_separator(c)) {
483 if (!cwk_path_is_separator(c)) {
495 is_device_path = (*c ==
'?' || *c ==
'.') && cwk_path_is_separator(++c);
496 if (is_device_path) {
506 c = cwk_path_find_next_stop(c);
510 while (cwk_path_is_separator(c)) {
516 c = cwk_path_find_next_stop(c);
520 if (cwk_path_is_separator(c)) {
537 if (cwk_path_is_separator(++c)) {
543static void cwk_path_get_root_unix(
const char *path,
size_t *length)
547 if (cwk_path_is_separator(path)) {
554static bool cwk_path_is_root_absolute(
const char *path,
size_t length)
563 return cwk_path_is_separator(&path[length - 1]);
566static size_t cwk_path_join_and_normalize_multiple(
const char **paths,
567 char *buffer,
size_t buffer_size)
570 bool absolute, has_segment_output;
574 cwk_path_get_root(paths[0], &pos);
578 absolute = cwk_path_is_root_absolute(paths[0], pos);
581 cwk_path_output_sized(buffer, buffer_size, 0, paths[0], pos);
585 if (!cwk_path_get_first_segment_joined(paths, &sj)) {
591 has_segment_output =
false;
596 if (cwk_path_segment_will_be_removed(&sj, absolute)) {
603 has_segment_output =
true;
608 pos += cwk_path_output_sized(buffer, buffer_size, pos, sj.segment.begin,
610 pos += cwk_path_output_separator(buffer, buffer_size, pos);
611 }
while (cwk_path_get_next_segment_joined(&sj));
615 if (has_segment_output) {
617 }
else if (pos == 0) {
621 assert(absolute ==
false);
622 pos += cwk_path_output_current(buffer, buffer_size, pos);
628 cwk_path_terminate_output(buffer, buffer_size, pos);
635size_t cwk_path_get_absolute(
const char *base,
const char *path,
char *buffer,
639 const char *paths[4];
644 if (cwk_path_is_absolute(base)) {
651 if (cwk_path_is_absolute(path)) {
665 return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
670 bool *other_available)
679 *base_available = cwk_path_segment_joined_skip_invisible(bsj, absolute);
680 *other_available = cwk_path_segment_joined_skip_invisible(osj, absolute);
685 if (!*base_available || !*other_available) {
691 if (!cwk_path_is_string_equal(bsj->segment.begin, osj->segment.begin,
692 bsj->segment.size)) {
699 *base_available = cwk_path_get_next_segment_joined(bsj);
700 *other_available = cwk_path_get_next_segment_joined(osj);
701 }
while (*base_available && *other_available);
704size_t cwk_path_get_relative(
const char *base_directory,
const char *path,
705 char *buffer,
size_t buffer_size)
707 size_t pos, base_root_length, path_root_length;
708 bool absolute, base_available, other_available, has_output;
709 const char *base_paths[2], *other_paths[2];
717 cwk_path_get_root(base_directory, &base_root_length);
718 cwk_path_get_root(path, &path_root_length);
719 if (!cwk_path_is_string_equal(base_directory, path, base_root_length)) {
725 absolute = cwk_path_is_root_absolute(base_directory, base_root_length);
730 base_paths[0] = base_directory;
731 base_paths[1] = NULL;
732 other_paths[0] = path;
733 other_paths[1] = NULL;
734 cwk_path_get_first_segment_joined(base_paths, &bsj);
735 cwk_path_get_first_segment_joined(other_paths, &osj);
739 cwk_path_skip_segments_until_diverge(&bsj, &osj, absolute, &base_available,
749 if (base_available) {
753 if (!cwk_path_segment_joined_skip_invisible(&bsj, absolute)) {
763 pos += cwk_path_output_back(buffer, buffer_size, pos);
764 pos += cwk_path_output_separator(buffer, buffer_size, pos);
765 }
while (cwk_path_get_next_segment_joined(&bsj));
770 if (other_available) {
774 if (!cwk_path_segment_joined_skip_invisible(&osj, absolute)) {
784 pos += cwk_path_output_sized(buffer, buffer_size, pos, osj.segment.begin,
786 pos += cwk_path_output_separator(buffer, buffer_size, pos);
787 }
while (cwk_path_get_next_segment_joined(&osj));
798 pos += cwk_path_output_current(buffer, buffer_size, pos);
803 cwk_path_terminate_output(buffer, buffer_size, pos);
808size_t cwk_path_join(
const char *path_a,
const char *path_b,
char *buffer,
811 const char *paths[3];
821 return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
824size_t cwk_path_join_multiple(
const char **paths,
char *buffer,
829 return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
832void cwk_path_get_root(
const char *path,
size_t *length)
836 if (path_style == CWK_STYLE_WINDOWS) {
837 cwk_path_get_root_windows(path, length);
839 cwk_path_get_root_unix(path, length);
843size_t cwk_path_change_root(
const char *path,
const char *new_root,
844 char *buffer,
size_t buffer_size)
847 size_t root_length, path_length, tail_length, new_root_length, new_path_size;
851 cwk_path_get_root(path, &root_length);
855 new_root_length = strlen(new_root);
856 path_length = strlen(path);
859 tail = path + root_length;
860 tail_length = path_length - root_length;
865 cwk_path_output_sized(buffer, buffer_size, new_root_length, tail,
867 cwk_path_output_sized(buffer, buffer_size, 0, new_root, new_root_length);
871 new_path_size = tail_length + new_root_length;
872 cwk_path_terminate_output(buffer, buffer_size, new_path_size);
874 return new_path_size;
877bool cwk_path_is_absolute(
const char *path)
883 cwk_path_get_root(path, &length);
886 return cwk_path_is_root_absolute(path, length);
889bool cwk_path_is_relative(
const char *path)
892 return !cwk_path_is_absolute(path);
895void cwk_path_get_basename(
const char *path,
const char **basename,
903 if (!cwk_path_get_last_segment(path, &segment)) {
912 *basename = segment.begin;
913 *length = segment.size;
916size_t cwk_path_change_basename(
const char *path,
const char *new_basename,
917 char *buffer,
size_t buffer_size)
920 size_t pos, root_size, new_basename_size;
924 if (!cwk_path_get_last_segment(path, &segment)) {
928 cwk_path_get_root(path, &root_size);
929 pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size);
933 while (cwk_path_is_separator(new_basename)) {
939 new_basename_size = 0;
940 while (new_basename[new_basename_size]) {
946 while (new_basename_size > 0 &&
947 cwk_path_is_separator(&new_basename[new_basename_size - 1])) {
952 pos += cwk_path_output_sized(buffer, buffer_size, pos, new_basename,
956 cwk_path_terminate_output(buffer, buffer_size, pos);
962 return cwk_path_change_segment(&segment, new_basename, buffer, buffer_size);
965void cwk_path_get_dirname(
const char *path,
size_t *length)
972 if (!cwk_path_get_last_segment(path, &segment)) {
979 *length = segment.begin - path;
982bool cwk_path_get_extension(
const char *path,
const char **extension,
990 if (!cwk_path_get_last_segment(path, &segment)) {
997 for (c = segment.end; c >= segment.begin; --c) {
1001 *length = segment.end - c;
1010bool cwk_path_has_extension(
const char *path)
1012 const char *extension;
1016 return cwk_path_get_extension(path, &extension, &length);
1019size_t cwk_path_change_extension(
const char *path,
const char *new_extension,
1020 char *buffer,
size_t buffer_size)
1023 const char *c, *old_extension;
1024 size_t pos, root_size, trail_size, new_extension_size;
1028 if (!cwk_path_get_last_segment(path, &segment)) {
1033 cwk_path_get_root(path, &root_size);
1034 pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size);
1037 if (*new_extension !=
'.') {
1038 pos += cwk_path_output_dot(buffer, buffer_size, pos);
1042 pos += cwk_path_output(buffer, buffer_size, pos, new_extension);
1043 cwk_path_terminate_output(buffer, buffer_size, pos);
1050 old_extension = segment.end;
1051 for (c = segment.begin; c < segment.end; ++c) {
1057 pos = cwk_path_output_sized(buffer, buffer_size, 0, segment.path,
1058 old_extension - segment.path);
1063 if (*new_extension ==
'.') {
1071 new_extension_size = strlen(new_extension) + 1;
1072 trail_size = cwk_path_output(buffer, buffer_size, pos + new_extension_size,
1077 pos += cwk_path_output_dot(buffer, buffer_size, pos);
1078 pos += cwk_path_output(buffer, buffer_size, pos, new_extension);
1084 cwk_path_terminate_output(buffer, buffer_size, pos);
1090size_t cwk_path_normalize(
const char *path,
char *buffer,
size_t buffer_size)
1092 const char *paths[2];
1099 return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
1102size_t cwk_path_get_intersection(
const char *path_base,
const char *path_other)
1105 size_t base_root_length, other_root_length;
1107 const char *paths_base[2], *paths_other[2];
1113 cwk_path_get_root(path_base, &base_root_length);
1114 cwk_path_get_root(path_other, &other_root_length);
1115 if (!cwk_path_is_string_equal(path_base, path_other, base_root_length)) {
1120 paths_base[0] = path_base;
1121 paths_base[1] = NULL;
1122 paths_other[0] = path_other;
1123 paths_other[1] = NULL;
1127 if (!cwk_path_get_first_segment_joined(paths_base, &base) ||
1128 !cwk_path_get_first_segment_joined(paths_other, &other)) {
1129 return base_root_length;
1136 absolute = cwk_path_is_root_absolute(path_base, base_root_length);
1141 end = path_base + base_root_length;
1148 if (!cwk_path_segment_joined_skip_invisible(&base, absolute) ||
1149 !cwk_path_segment_joined_skip_invisible(&other, absolute)) {
1153 if (!cwk_path_is_string_equal(base.segment.begin, other.segment.begin,
1154 base.segment.size)) {
1157 return end - path_base;
1161 end = base.segment.end;
1162 }
while (cwk_path_get_next_segment_joined(&base) &&
1163 cwk_path_get_next_segment_joined(&other));
1167 return end - path_base;
1170bool cwk_path_get_first_segment(
const char *path,
struct cwk_segment *segment)
1173 const char *segments;
1177 cwk_path_get_root(path, &length);
1178 segments = path + length;
1182 return cwk_path_get_first_segment_without_root(path, segments, segment);
1185bool cwk_path_get_last_segment(
const char *path,
struct cwk_segment *segment)
1190 if (!cwk_path_get_first_segment(path, segment)) {
1197 while (cwk_path_get_next_segment(segment)) {
1204bool cwk_path_get_next_segment(
struct cwk_segment *segment)
1210 c = segment->begin + segment->size;
1217 assert(cwk_path_is_separator(c));
1220 }
while (cwk_path_is_separator(c));
1234 c = cwk_path_find_next_stop(c);
1236 segment->size = c - segment->begin;
1242bool cwk_path_get_previous_segment(
struct cwk_segment *segment)
1249 if (c <= segment->segments) {
1257 if (c <= segment->segments) {
1262 }
while (cwk_path_is_separator(c));
1266 segment->end = c + 1;
1267 segment->begin = cwk_path_find_previous_stop(segment->segments, c);
1268 segment->size = segment->end - segment->begin;
1273enum cwk_segment_type cwk_path_get_segment_type(
1278 if (strncmp(segment->begin,
".", segment->size) == 0) {
1280 }
else if (strncmp(segment->begin,
"..", segment->size) == 0) {
1287bool cwk_path_is_separator(
const char *str)
1292 c = separators[path_style];
1306size_t cwk_path_change_segment(
struct cwk_segment *segment,
const char *value,
1307 char *buffer,
size_t buffer_size)
1309 size_t pos, value_size, tail_size;
1313 pos = cwk_path_output_sized(buffer, buffer_size, 0, segment->path,
1314 segment->begin - segment->path);
1318 while (cwk_path_is_separator(value)) {
1325 while (value[value_size]) {
1332 while (value_size > 0 && cwk_path_is_separator(&value[value_size - 1])) {
1338 tail_size = strlen(segment->end);
1343 cwk_path_output_sized(buffer, buffer_size, pos + value_size, segment->end,
1348 pos += cwk_path_output_sized(buffer, buffer_size, pos, value, value_size);
1353 cwk_path_terminate_output(buffer, buffer_size, pos);
1359enum cwk_path_style cwk_path_guess_style(
const char *path)
1368 cwk_path_get_root_windows(path, &root_length);
1369 if (root_length > 1) {
1370 return CWK_STYLE_WINDOWS;
1376 for (c = path; *c; ++c) {
1377 if (*c == *separators[CWK_STYLE_UNIX]) {
1378 return CWK_STYLE_UNIX;
1379 }
else if (*c == *separators[CWK_STYLE_WINDOWS]) {
1380 return CWK_STYLE_WINDOWS;
1388 if (!cwk_path_get_last_segment(path, &segment)) {
1391 return CWK_STYLE_UNIX;
1394 if (*segment.begin ==
'.') {
1395 return CWK_STYLE_UNIX;
1401 for (c = segment.begin; *c; ++c) {
1403 return CWK_STYLE_WINDOWS;
1409 return CWK_STYLE_UNIX;
1412void cwk_path_set_style(
enum cwk_path_style style)
1416 assert(style == CWK_STYLE_UNIX || style == CWK_STYLE_WINDOWS);
1420enum cwk_path_style cwk_path_get_style(
void)
A joined path represents multiple path strings which are concatenated, but not (necessarily) stored i...
A segment represents a single component of a path.