1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
|
#include <assert.h>
#include <ctype.h>
#include <cwalk.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/**
* We try to default to a different path style depending on the operating
* system. So this should detect whether we should use windows or unix paths.
*/
#if defined(WIN32) || defined(_WIN32) || \
defined(__WIN32) && !defined(__CYGWIN__)
static enum cwk_path_style path_style = CWK_STYLE_WINDOWS;
#else
static enum cwk_path_style path_style = CWK_STYLE_UNIX;
#endif
/**
* This is a list of separators used in different styles. Windows can read
* multiple separators, but it generally outputs just a backslash. The output
* will always use the first character for the output.
*/
static const char *separators[] = {
"\\/", // CWK_STYLE_WINDOWS
"/" // CWK_STYLE_UNIX
};
/**
* A joined path represents multiple path strings which are concatenated, but
* not (necessarily) stored in contiguous memory. The joined path allows to
* iterate over the segments as if it was one piece of path.
*/
struct cwk_segment_joined
{
struct cwk_segment segment;
const char **paths;
size_t path_index;
};
static size_t cwk_path_output_sized(char *buffer, size_t buffer_size,
size_t position, const char *str, size_t length)
{
size_t amount_written;
// First we determine the amount which we can write to the buffer. There are
// three cases. In the first case we have enough to store the whole string in
// it. In the second one we can only store a part of it, and in the third we
// have no space left.
if (buffer_size > position + length) {
amount_written = length;
} else if (buffer_size > position) {
amount_written = buffer_size - position;
} else {
amount_written = 0;
}
// If we actually want to write out something we will do that here. We will
// always append a '\0', this way we are guaranteed to have a valid string at
// all times.
if (amount_written > 0) {
memmove(&buffer[position], str, amount_written);
}
// Return the theoretical length which would have been written when everything
// would have fit in the buffer.
return length;
}
static size_t cwk_path_output_current(char *buffer, size_t buffer_size,
size_t position)
{
// We output a "current" directory, which is a single character. This
// character is currently not style dependant.
return cwk_path_output_sized(buffer, buffer_size, position, ".", 1);
}
static size_t cwk_path_output_back(char *buffer, size_t buffer_size,
size_t position)
{
// We output a "back" directory, which ahs two characters. This
// character is currently not style dependant.
return cwk_path_output_sized(buffer, buffer_size, position, "..", 2);
}
static size_t cwk_path_output_separator(char *buffer, size_t buffer_size,
size_t position)
{
// We output a separator, which is a single character.
return cwk_path_output_sized(buffer, buffer_size, position,
separators[path_style], 1);
}
static size_t cwk_path_output_dot(char *buffer, size_t buffer_size,
size_t position)
{
// We output a dot, which is a single character. This is used for extensions.
return cwk_path_output_sized(buffer, buffer_size, position, ".", 1);
}
static size_t cwk_path_output(char *buffer, size_t buffer_size, size_t position,
const char *str)
{
size_t length;
// This just does a sized output internally, but first measuring the
// null-terminated string.
length = strlen(str);
return cwk_path_output_sized(buffer, buffer_size, position, str, length);
}
static void cwk_path_terminate_output(char *buffer, size_t buffer_size,
size_t pos)
{
if (buffer_size > 0) {
if (pos >= buffer_size) {
buffer[buffer_size - 1] = '\0';
} else {
buffer[pos] = '\0';
}
}
}
static bool cwk_path_is_string_equal(const char *first, const char *second,
size_t first_size, size_t second_size)
{
bool are_both_separators;
// The two strings are not equal if the sizes are not equal.
if (first_size != second_size) {
return false;
}
// If the path style is UNIX, we will compare case sensitively. This can be
// done easily using strncmp.
if (path_style == CWK_STYLE_UNIX) {
return strncmp(first, second, first_size) == 0;
}
// However, if this is windows we will have to compare case insensitively.
// Since there is no standard method to do that we will have to do it on our
// own.
while (*first && *second && first_size > 0) {
// We can consider the string to be not equal if the two lowercase
// characters are not equal. The two chars may also be separators, which
// means they would be equal.
are_both_separators = strchr(separators[path_style], *first) != NULL &&
strchr(separators[path_style], *second) != NULL;
if (tolower(*first) != tolower(*second) && !are_both_separators) {
return false;
}
first++;
second++;
--first_size;
}
// The string must be equal since they both have the same length and all the
// characters are the same.
return true;
}
static const char *cwk_path_find_next_stop(const char *c)
{
// We just move forward until we find a '\0' or a separator, which will be our
// next "stop".
while (*c != '\0' && !cwk_path_is_separator(c)) {
++c;
}
// Return the pointer of the next stop.
return c;
}
static const char *cwk_path_find_previous_stop(const char *begin, const char *c)
{
// We just move back until we find a separator or reach the beginning of the
// path, which will be our previous "stop".
while (c > begin && !cwk_path_is_separator(c)) {
--c;
}
// Return the pointer to the previous stop. We have to return the first
// character after the separator, not on the separator itself.
if (cwk_path_is_separator(c)) {
return c + 1;
} else {
return c;
}
}
static bool cwk_path_get_first_segment_without_root(const char *path,
const char *segments, struct cwk_segment *segment)
{
// Let's remember the path. We will move the path pointer afterwards, that's
// why this has to be done first.
segment->path = path;
segment->segments = segments;
segment->begin = segments;
segment->end = segments;
segment->size = 0;
// Now let's check whether this is an empty string. An empty string has no
// segment it could use.
if (*segments == '\0') {
return false;
}
// If the string starts with separators, we will jump over those. If there is
// only a slash and a '\0' after it, we can't determine the first segment
// since there is none.
while (cwk_path_is_separator(segments)) {
++segments;
if (*segments == '\0') {
return false;
}
}
// So this is the beginning of our segment.
segment->begin = segments;
// Now let's determine the end of the segment, which we do by moving the path
// pointer further until we find a separator.
segments = cwk_path_find_next_stop(segments);
// And finally, calculate the size of the segment by subtracting the position
// from the end.
segment->size = (size_t)(segments - segment->begin);
segment->end = segments;
// Tell the caller that we found a segment.
return true;
}
static bool cwk_path_get_last_segment_without_root(const char *path,
struct cwk_segment *segment)
{
// Now this is fairly similar to the normal algorithm, however, it will assume
// that there is no root in the path. So we grab the first segment at this
// position, assuming there is no root.
if (!cwk_path_get_first_segment_without_root(path, path, segment)) {
return false;
}
// Now we find our last segment. The segment struct of the caller
// will contain the last segment, since the function we call here will not
// change the segment struct when it reaches the end.
while (cwk_path_get_next_segment(segment)) {
// We just loop until there is no other segment left.
}
return true;
}
static bool cwk_path_get_first_segment_joined(const char **paths,
struct cwk_segment_joined *sj)
{
bool result;
// Prepare the first segment. We position the joined segment on the first path
// and assign the path array to the struct.
sj->path_index = 0;
sj->paths = paths;
// We loop through all paths until we find one which has a segment. The result
// is stored in a variable, so we can let the caller know whether we found one
// or not.
result = false;
while (paths[sj->path_index] != NULL &&
(result = cwk_path_get_first_segment(paths[sj->path_index],
&sj->segment)) == false) {
++sj->path_index;
}
return result;
}
static bool cwk_path_get_next_segment_joined(struct cwk_segment_joined *sj)
{
bool result;
if (sj->paths[sj->path_index] == NULL) {
// We reached already the end of all paths, so there is no other segment
// left.
return false;
} else if (cwk_path_get_next_segment(&sj->segment)) {
// There was another segment on the current path, so we are good to
// continue.
return true;
}
// We try to move to the next path which has a segment available. We must at
// least move one further since the current path reached the end.
result = false;
do {
++sj->path_index;
// And we obviously have to stop this loop if there are no more paths left.
if (sj->paths[sj->path_index] == NULL) {
break;
}
// Grab the first segment of the next path and determine whether this path
// has anything useful in it. There is one more thing we have to consider
// here - for the first time we do this we want to skip the root, but
// afterwards we will consider that to be part of the segments.
result = cwk_path_get_first_segment_without_root(sj->paths[sj->path_index],
sj->paths[sj->path_index], &sj->segment);
} while (!result);
// Finally, report the result back to the caller.
return result;
}
static bool cwk_path_get_previous_segment_joined(struct cwk_segment_joined *sj)
{
bool result;
if (*sj->paths == NULL) {
// It's possible that there is no initialized segment available in the
// struct since there are no paths. In that case we can return false, since
// there is no previous segment.
return false;
} else if (cwk_path_get_previous_segment(&sj->segment)) {
// Now we try to get the previous segment from the current path. If we can
// do that successfully, we can let the caller know that we found one.
return true;
}
result = false;
do {
// We are done once we reached index 0. In that case there are no more
// segments left.
if (sj->path_index == 0) {
break;
}
// There is another path which we have to inspect. So we decrease the path
// index.
--sj->path_index;
// If this is the first path we will have to consider that this path might
// include a root, otherwise we just treat is as a segment.
if (sj->path_index == 0) {
result = cwk_path_get_last_segment(sj->paths[sj->path_index],
&sj->segment);
} else {
result = cwk_path_get_last_segment_without_root(sj->paths[sj->path_index],
&sj->segment);
}
} while (!result);
return result;
}
static bool cwk_path_segment_back_will_be_removed(struct cwk_segment_joined *sj)
{
enum cwk_segment_type type;
int counter;
// We are handling back segments here. We must verify how many back segments
// and how many normal segments come before this one to decide whether we keep
// or remove it.
// The counter determines how many normal segments are our current segment,
// which will popped off before us. If the counter goes above zero it means
// that our segment will be popped as well.
counter = 0;
// We loop over all previous segments until we either reach the beginning,
// which means our segment will not be dropped or the counter goes above zero.
while (cwk_path_get_previous_segment_joined(sj)) {
// Now grab the type. The type determines whether we will increase or
// decrease the counter. We don't handle a CWK_CURRENT frame here since it
// has no influence.
type = cwk_path_get_segment_type(&sj->segment);
if (type == CWK_NORMAL) {
// This is a normal segment. The normal segment will increase the counter
// since it neutralizes one back segment. If we go above zero we can
// return immediately.
++counter;
if (counter > 0) {
return true;
}
} else if (type == CWK_BACK) {
// A CWK_BACK segment will reduce the counter by one. We can not remove a
// back segment as long we are not above zero since we don't have the
// opposite normal segment which we would remove.
--counter;
}
}
// We never got a count larger than zero, so we will keep this segment alive.
return false;
}
static bool cwk_path_segment_normal_will_be_removed(
struct cwk_segment_joined *sj)
{
enum cwk_segment_type type;
int counter;
// The counter determines how many segments are above our current segment,
// which will popped off before us. If the counter goes below zero it means
// that our segment will be popped as well.
counter = 0;
// We loop over all following segments until we either reach the end, which
// means our segment will not be dropped or the counter goes below zero.
while (cwk_path_get_next_segment_joined(sj)) {
// First, grab the type. The type determines whether we will increase or
// decrease the counter. We don't handle a CWK_CURRENT frame here since it
// has no influence.
type = cwk_path_get_segment_type(&sj->segment);
if (type == CWK_NORMAL) {
// This is a normal segment. The normal segment will increase the counter
// since it will be removed by a "../" before us.
++counter;
} else if (type == CWK_BACK) {
// A CWK_BACK segment will reduce the counter by one. If we are below zero
// we can return immediately.
--counter;
if (counter < 0) {
return true;
}
}
}
// We never got a negative count, so we will keep this segment alive.
return false;
}
static bool
cwk_path_segment_will_be_removed(const struct cwk_segment_joined *sj,
bool absolute)
{
enum cwk_segment_type type;
struct cwk_segment_joined sjc;
// We copy the joined path so we don't need to modify it.
sjc = *sj;
// First we check whether this is a CWK_CURRENT or CWK_BACK segment, since
// those will always be dropped.
type = cwk_path_get_segment_type(&sj->segment);
if (type == CWK_CURRENT || (type == CWK_BACK && absolute)) {
return true;
} else if (type == CWK_BACK) {
return cwk_path_segment_back_will_be_removed(&sjc);
} else {
return cwk_path_segment_normal_will_be_removed(&sjc);
}
}
static bool
cwk_path_segment_joined_skip_invisible(struct cwk_segment_joined *sj,
bool absolute)
{
while (cwk_path_segment_will_be_removed(sj, absolute)) {
if (!cwk_path_get_next_segment_joined(sj)) {
return false;
}
}
return true;
}
static void cwk_path_get_root_windows(const char *path, size_t *length)
{
const char *c;
bool is_device_path;
// We can not determine the root if this is an empty string. So we set the
// root to NULL and the length to zero and cancel the whole thing.
c = path;
*length = 0;
if (!*c) {
return;
}
// Now we have to verify whether this is a windows network path (UNC), which
// we will consider our root.
if (cwk_path_is_separator(c)) {
++c;
// Check whether the path starts with a single backslash, which means this
// is not a network path - just a normal path starting with a backslash.
if (!cwk_path_is_separator(c)) {
// Okay, this is not a network path but we still use the backslash as a
// root.
++(*length);
return;
}
// A device path is a path which starts with "\\." or "\\?". A device path
// can be a UNC path as well, in which case it will take up one more
// segment. So, this is a network or device path. Skip the previous
// separator. Now we need to determine whether this is a device path. We
// might advance one character here if the server name starts with a '?' or
// a '.', but that's fine since we will search for a separator afterwards
// anyway.
++c;
is_device_path = (*c == '?' || *c == '.') && cwk_path_is_separator(++c);
if (is_device_path) {
// That's a device path, and the root must be either "\\.\" or "\\?\"
// which is 4 characters long. (at least that's how Windows
// GetFullPathName behaves.)
*length = 4;
return;
}
// We will grab anything up to the next stop. The next stop might be a '\0'
// or another separator. That will be the server name.
c = cwk_path_find_next_stop(c);
// If this is a separator and not the end of a string we wil have to include
// it. However, if this is a '\0' we must not skip it.
while (cwk_path_is_separator(c)) {
++c;
}
// We are now skipping the shared folder name, which will end after the
// next stop.
c = cwk_path_find_next_stop(c);
// Then there might be a separator at the end. We will include that as well,
// it will mark the path as absolute.
if (cwk_path_is_separator(c)) {
++c;
}
// Finally, calculate the size of the root.
*length = (size_t)(c - path);
return;
}
// Move to the next and check whether this is a colon.
if (*++c == ':') {
*length = 2;
// Now check whether this is a backslash (or slash). If it is not, we could
// assume that the next character is a '\0' if it is a valid path. However,
// we will not assume that - since ':' is not valid in a path it must be a
// mistake by the caller than. We will try to understand it anyway.
if (cwk_path_is_separator(++c)) {
*length = 3;
}
}
}
static void cwk_path_get_root_unix(const char *path, size_t *length)
{
// The slash of the unix path represents the root. There is no root if there
// is no slash.
if (cwk_path_is_separator(path)) {
*length = 1;
} else {
*length = 0;
}
}
static bool cwk_path_is_root_absolute(const char *path, size_t length)
{
// This is definitely not absolute if there is no root.
if (length == 0) {
return false;
}
// If there is a separator at the end of the root, we can safely consider this
// to be an absolute path.
return cwk_path_is_separator(&path[length - 1]);
}
static void cwk_path_fix_root(char *buffer, size_t buffer_size, size_t length)
{
size_t i;
// This only affects windows.
if (path_style != CWK_STYLE_WINDOWS) {
return;
}
// Make sure we are not writing further than we are actually allowed to.
if (length > buffer_size) {
length = buffer_size;
}
// Replace all forward slashes with backwards slashes. Since this is windows
// we can't have any forward slashes in the root.
for (i = 0; i < length; ++i) {
if (cwk_path_is_separator(&buffer[i])) {
buffer[i] = *separators[CWK_STYLE_WINDOWS];
}
}
}
static size_t cwk_path_join_and_normalize_multiple(const char **paths,
char *buffer, size_t buffer_size)
{
size_t pos;
bool absolute, has_segment_output;
struct cwk_segment_joined sj;
// We initialize the position after the root, which should get us started.
cwk_path_get_root(paths[0], &pos);
// Determine whether the path is absolute or not. We need that to determine
// later on whether we can remove superfluous "../" or not.
absolute = cwk_path_is_root_absolute(paths[0], pos);
// First copy the root to the output. After copying, we will normalize the
// root.
cwk_path_output_sized(buffer, buffer_size, 0, paths[0], pos);
cwk_path_fix_root(buffer, buffer_size, pos);
// So we just grab the first segment. If there is no segment we will always
// output a "/", since we currently only support absolute paths here.
if (!cwk_path_get_first_segment_joined(paths, &sj)) {
goto done;
}
// Let's assume that we don't have any segment output for now. We will toggle
// this flag once there is some output.
has_segment_output = false;
do {
// Check whether we have to drop this segment because of resolving a
// relative path or because it is a CWK_CURRENT segment.
if (cwk_path_segment_will_be_removed(&sj, absolute)) {
continue;
}
// We add a separator if we previously wrote a segment. The last segment
// must not have a trailing separator. This must happen before the segment
// output, since we would override the null terminating character with
// reused buffers if this was done afterwards.
if (has_segment_output) {
pos += cwk_path_output_separator(buffer, buffer_size, pos);
}
// Remember that we have segment output, so we can handle the trailing slash
// later on. This is necessary since we might have segments but they are all
// removed.
has_segment_output = true;
// Write out the segment but keep in mind that we need to follow the
// buffer size limitations. That's why we use the path output functions
// here.
pos += cwk_path_output_sized(buffer, buffer_size, pos, sj.segment.begin,
sj.segment.size);
} while (cwk_path_get_next_segment_joined(&sj));
// Remove the trailing slash, but only if we have segment output. We don't
// want to remove anything from the root.
if (!has_segment_output && pos == 0) {
// This may happen if the path is absolute and all segments have been
// removed. We can not have an empty output - and empty output means we stay
// in the current directory. So we will output a ".".
assert(absolute == false);
pos += cwk_path_output_current(buffer, buffer_size, pos);
}
// We must append a '\0' in any case, unless the buffer size is zero. If the
// buffer size is zero, which means we can not.
done:
cwk_path_terminate_output(buffer, buffer_size, pos);
// And finally let our caller know about the total size of the normalized
// path.
return pos;
}
size_t cwk_path_get_absolute(const char *base, const char *path, char *buffer,
size_t buffer_size)
{
size_t i;
const char *paths[4];
// The basename should be an absolute path if the caller is using the API
// correctly. However, he might not and in that case we will append a fake
// root at the beginning.
if (cwk_path_is_absolute(base)) {
i = 0;
} else if (path_style == CWK_STYLE_WINDOWS) {
paths[0] = "\\";
i = 1;
} else {
paths[0] = "/";
i = 1;
}
if (cwk_path_is_absolute(path)) {
// If the submitted path is not relative the base path becomes irrelevant.
// We will only normalize the submitted path instead.
paths[i++] = path;
paths[i] = NULL;
} else {
// Otherwise we append the relative path to the base path and normalize it.
// The result will be a new absolute path.
paths[i++] = base;
paths[i++] = path;
paths[i] = NULL;
}
// Finally join everything together and normalize it.
return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
}
static void cwk_path_skip_segments_until_diverge(struct cwk_segment_joined *bsj,
struct cwk_segment_joined *osj, bool absolute, bool *base_available,
bool *other_available)
{
// Now looping over all segments until they start to diverge. A path may
// diverge if two segments are not equal or if one path reaches the end.
do {
// Check whether there is anything available after we skip everything which
// is invisible. We do that for both paths, since we want to let the caller
// know which path has some trailing segments after they diverge.
*base_available = cwk_path_segment_joined_skip_invisible(bsj, absolute);
*other_available = cwk_path_segment_joined_skip_invisible(osj, absolute);
// We are done if one or both of those paths reached the end. They either
// diverge or both reached the end - but in both cases we can not continue
// here.
if (!*base_available || !*other_available) {
break;
}
// Compare the content of both segments. We are done if they are not equal,
// since they diverge.
if (!cwk_path_is_string_equal(bsj->segment.begin, osj->segment.begin,
bsj->segment.size, osj->segment.size)) {
break;
}
// We keep going until one of those segments reached the end. The next
// segment might be invisible, but we will check for that in the beginning
// of the loop once again.
*base_available = cwk_path_get_next_segment_joined(bsj);
*other_available = cwk_path_get_next_segment_joined(osj);
} while (*base_available && *other_available);
}
size_t cwk_path_get_relative(const char *base_directory, const char *path,
char *buffer, size_t buffer_size)
{
size_t pos, base_root_length, path_root_length;
bool absolute, base_available, other_available, has_output;
const char *base_paths[2], *other_paths[2];
struct cwk_segment_joined bsj, osj;
pos = 0;
// First we compare the roots of those two paths. If the roots are not equal
// we can't continue, since there is no way to get a relative path from
// different roots.
cwk_path_get_root(base_directory, &base_root_length);
cwk_path_get_root(path, &path_root_length);
if (base_root_length != path_root_length ||
!cwk_path_is_string_equal(base_directory, path, base_root_length,
path_root_length)) {
cwk_path_terminate_output(buffer, buffer_size, pos);
return pos;
}
// Verify whether this is an absolute path. We need to know that since we can
// remove all back-segments if it is.
absolute = cwk_path_is_root_absolute(base_directory, base_root_length);
// Initialize our joined segments. This will allow us to use the internal
// functions to skip until diverge and invisible. We only have one path in
// them though.
base_paths[0] = base_directory;
base_paths[1] = NULL;
other_paths[0] = path;
other_paths[1] = NULL;
cwk_path_get_first_segment_joined(base_paths, &bsj);
cwk_path_get_first_segment_joined(other_paths, &osj);
// Okay, now we skip until the segments diverge. We don't have anything to do
// with the segments which are equal.
cwk_path_skip_segments_until_diverge(&bsj, &osj, absolute, &base_available,
&other_available);
// Assume there is no output until we have got some. We will need this
// information later on to remove trailing slashes or alternatively output a
// current-segment.
has_output = false;
// So if we still have some segments left in the base path we will now output
// a back segment for all of them.
if (base_available) {
do {
// Skip any invisible segment. We don't care about those and we don't need
// to navigate back because of them.
if (!cwk_path_segment_joined_skip_invisible(&bsj, absolute)) {
break;
}
// Toggle the flag if we have output. We need to remember that, since we
// want to remove the trailing slash.
has_output = true;
// Output the back segment and a separator. No need to worry about the
// superfluous segment since it will be removed later on.
pos += cwk_path_output_back(buffer, buffer_size, pos);
pos += cwk_path_output_separator(buffer, buffer_size, pos);
} while (cwk_path_get_next_segment_joined(&bsj));
}
// And if we have some segments available of the target path we will output
// all of those.
if (other_available) {
do {
// Again, skip any invisible segments since we don't need to navigate into
// them.
if (!cwk_path_segment_joined_skip_invisible(&osj, absolute)) {
break;
}
// Toggle the flag if we have output. We need to remember that, since we
// want to remove the trailing slash.
has_output = true;
// Output the current segment and a separator. No need to worry about the
// superfluous segment since it will be removed later on.
pos += cwk_path_output_sized(buffer, buffer_size, pos, osj.segment.begin,
osj.segment.size);
pos += cwk_path_output_separator(buffer, buffer_size, pos);
} while (cwk_path_get_next_segment_joined(&osj));
}
// If we have some output by now we will have to remove the trailing slash. We
// simply do that by moving back one character. The terminate output function
// will then place the '\0' on this position. Otherwise, if there is no
// output, we will have to output a "current directory", since the target path
// points to the base path.
if (has_output) {
--pos;
} else {
pos += cwk_path_output_current(buffer, buffer_size, pos);
}
// Finally, we can terminate the output - which means we place a '\0' at the
// current position or at the end of the buffer.
cwk_path_terminate_output(buffer, buffer_size, pos);
return pos;
}
size_t cwk_path_join(const char *path_a, const char *path_b, char *buffer,
size_t buffer_size)
{
const char *paths[3];
// This is simple. We will just create an array with the two paths which we
// wish to join.
paths[0] = path_a;
paths[1] = path_b;
paths[2] = NULL;
// And then call the join and normalize function which will do the hard work
// for us.
return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
}
size_t cwk_path_join_multiple(const char **paths, char *buffer,
size_t buffer_size)
{
// We can just call the internal join and normalize function for this one,
// since it will handle everything.
return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
}
void cwk_path_get_root(const char *path, size_t *length)
{
// We use a different implementation here based on the configuration of the
// library.
if (path_style == CWK_STYLE_WINDOWS) {
cwk_path_get_root_windows(path, length);
} else {
cwk_path_get_root_unix(path, length);
}
}
size_t cwk_path_change_root(const char *path, const char *new_root,
char *buffer, size_t buffer_size)
{
const char *tail;
size_t root_length, path_length, tail_length, new_root_length, new_path_size;
// First we need to determine the actual size of the root which we will
// change.
cwk_path_get_root(path, &root_length);
// Now we determine the sizes of the new root and the path. We need that to
// determine the size of the part after the root (the tail).
new_root_length = strlen(new_root);
path_length = strlen(path);
// Okay, now we calculate the position of the tail and the length of it.
tail = path + root_length;
tail_length = path_length - root_length;
// We first output the tail and then the new root, that's because the source
// path and the buffer may be overlapping. This way the root will not
// overwrite the tail.
cwk_path_output_sized(buffer, buffer_size, new_root_length, tail,
tail_length);
cwk_path_output_sized(buffer, buffer_size, 0, new_root, new_root_length);
// Finally we calculate the size o the new path and terminate the output with
// a '\0'.
new_path_size = tail_length + new_root_length;
cwk_path_terminate_output(buffer, buffer_size, new_path_size);
return new_path_size;
}
bool cwk_path_is_absolute(const char *path)
{
size_t length;
// We grab the root of the path. This root does not include the first
// separator of a path.
cwk_path_get_root(path, &length);
// Now we can determine whether the root is absolute or not.
return cwk_path_is_root_absolute(path, length);
}
bool cwk_path_is_relative(const char *path)
{
// The path is relative if it is not absolute.
return !cwk_path_is_absolute(path);
}
void cwk_path_get_basename(const char *path, const char **basename,
size_t *length)
{
struct cwk_segment segment;
// We get the last segment of the path. The last segment will contain the
// basename if there is any. If there are no segments we will set the basename
// to NULL and the length to 0.
if (!cwk_path_get_last_segment(path, &segment)) {
*basename = NULL;
if (length) {
*length = 0;
}
return;
}
// Now we can just output the segment contents, since that's our basename.
// There might be trailing separators after the basename, but the size does
// not include those.
*basename = segment.begin;
if (length) {
*length = segment.size;
}
}
size_t cwk_path_change_basename(const char *path, const char *new_basename,
char *buffer, size_t buffer_size)
{
struct cwk_segment segment;
size_t pos, root_size, new_basename_size;
// First we try to get the last segment. We may only have a root without any
// segments, in which case we will create one.
if (!cwk_path_get_last_segment(path, &segment)) {
// So there is no segment in this path. First we grab the root and output
// that. We are not going to modify the root in any way.
cwk_path_get_root(path, &root_size);
pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size);
// We have to trim the separators from the beginning of the new basename.
// This is quite easy to do.
while (cwk_path_is_separator(new_basename)) {
++new_basename;
}
// Now we measure the length of the new basename, this is a two step
// process. First we find the '\0' character at the end of the string.
new_basename_size = 0;
while (new_basename[new_basename_size]) {
++new_basename_size;
}
// And then we trim the separators at the end of the basename until we reach
// the first valid character.
while (new_basename_size > 0 &&
cwk_path_is_separator(&new_basename[new_basename_size - 1])) {
--new_basename_size;
}
// Now we will output the new basename after the root.
pos += cwk_path_output_sized(buffer, buffer_size, pos, new_basename,
new_basename_size);
// And finally terminate the output and return the total size of the path.
cwk_path_terminate_output(buffer, buffer_size, pos);
return pos;
}
// If there is a last segment we can just forward this call, which is fairly
// easy.
return cwk_path_change_segment(&segment, new_basename, buffer, buffer_size);
}
void cwk_path_get_dirname(const char *path, size_t *length)
{
struct cwk_segment segment;
// We get the last segment of the path. The last segment will contain the
// basename if there is any. If there are no segments we will set the length
// to 0.
if (!cwk_path_get_last_segment(path, &segment)) {
*length = 0;
return;
}
// We can now return the length from the beginning of the string up to the
// beginning of the last segment.
*length = (size_t)(segment.begin - path);
}
bool cwk_path_get_extension(const char *path, const char **extension,
size_t *length)
{
struct cwk_segment segment;
const char *c;
// We get the last segment of the path. The last segment will contain the
// extension if there is any.
if (!cwk_path_get_last_segment(path, &segment)) {
return false;
}
// Now we search for a dot within the segment. If there is a dot, we consider
// the rest of the segment the extension. We do this from the end towards the
// beginning, since we want to find the last dot.
for (c = segment.end; c >= segment.begin; --c) {
if (*c == '.') {
// Okay, we found an extension. We can stop looking now.
*extension = c;
*length = (size_t)(segment.end - c);
return true;
}
}
// We couldn't find any extension.
return false;
}
bool cwk_path_has_extension(const char *path)
{
const char *extension;
size_t length;
// We just wrap the get_extension call which will then do the work for us.
return cwk_path_get_extension(path, &extension, &length);
}
size_t cwk_path_change_extension(const char *path, const char *new_extension,
char *buffer, size_t buffer_size)
{
struct cwk_segment segment;
const char *c, *old_extension;
size_t pos, root_size, trail_size, new_extension_size;
// First we try to get the last segment. We may only have a root without any
// segments, in which case we will create one.
if (!cwk_path_get_last_segment(path, &segment)) {
// So there is no segment in this path. First we grab the root and output
// that. We are not going to modify the root in any way. If there is no
// root, this will end up with a root size 0, and nothing will be written.
cwk_path_get_root(path, &root_size);
pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size);
// Add a dot if the submitted value doesn't have any.
if (*new_extension != '.') {
pos += cwk_path_output_dot(buffer, buffer_size, pos);
}
// And finally terminate the output and return the total size of the path.
pos += cwk_path_output(buffer, buffer_size, pos, new_extension);
cwk_path_terminate_output(buffer, buffer_size, pos);
return pos;
}
// Now we seek the old extension in the last segment, which we will replace
// with the new one. If there is no old extension, it will point to the end of
// the segment.
old_extension = segment.end;
for (c = segment.begin; c < segment.end; ++c) {
if (*c == '.') {
old_extension = c;
}
}
pos = cwk_path_output_sized(buffer, buffer_size, 0, segment.path,
(size_t)(old_extension - segment.path));
// If the new extension starts with a dot, we will skip that dot. We always
// output exactly one dot before the extension. If the extension contains
// multiple dots, we will output those as part of the extension.
if (*new_extension == '.') {
++new_extension;
}
// We calculate the size of the new extension, including the dot, in order to
// output the trail - which is any part of the path coming after the
// extension. We must output this first, since the buffer may overlap with the
// submitted path - and it would be overridden by longer extensions.
new_extension_size = strlen(new_extension) + 1;
trail_size = cwk_path_output(buffer, buffer_size, pos + new_extension_size,
segment.end);
// Finally we output the dot and the new extension. The new extension itself
// doesn't contain the dot anymore, so we must output that first.
pos += cwk_path_output_dot(buffer, buffer_size, pos);
pos += cwk_path_output(buffer, buffer_size, pos, new_extension);
// Now we terminate the output with a null-terminating character, but before
// we do that we must add the size of the trail to the position which we
// output before.
pos += trail_size;
cwk_path_terminate_output(buffer, buffer_size, pos);
// And the position is our output size now.
return pos;
}
size_t cwk_path_normalize(const char *path, char *buffer, size_t buffer_size)
{
const char *paths[2];
// Now we initialize the paths which we will normalize. Since this function
// only supports submitting a single path, we will only add that one.
paths[0] = path;
paths[1] = NULL;
return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size);
}
size_t cwk_path_get_intersection(const char *path_base, const char *path_other)
{
bool absolute;
size_t base_root_length, other_root_length;
const char *end;
const char *paths_base[2], *paths_other[2];
struct cwk_segment_joined base, other;
// We first compare the two roots. We just return zero if they are not equal.
// This will also happen to return zero if the paths are mixed relative and
// absolute.
cwk_path_get_root(path_base, &base_root_length);
cwk_path_get_root(path_other, &other_root_length);
if (!cwk_path_is_string_equal(path_base, path_other, base_root_length,
other_root_length)) {
return 0;
}
// Configure our paths. We just have a single path in here for now.
paths_base[0] = path_base;
paths_base[1] = NULL;
paths_other[0] = path_other;
paths_other[1] = NULL;
// So we get the first segment of both paths. If one of those paths don't have
// any segment, we will return 0.
if (!cwk_path_get_first_segment_joined(paths_base, &base) ||
!cwk_path_get_first_segment_joined(paths_other, &other)) {
return base_root_length;
}
// We now determine whether the path is absolute or not. This is required
// because if will ignore removed segments, and this behaves differently if
// the path is absolute. However, we only need to check the base path because
// we are guaranteed that both paths are either relative or absolute.
absolute = cwk_path_is_root_absolute(path_base, base_root_length);
// We must keep track of the end of the previous segment. Initially, this is
// set to the beginning of the path. This means that 0 is returned if the
// first segment is not equal.
end = path_base + base_root_length;
// Now we loop over both segments until one of them reaches the end or their
// contents are not equal.
do {
// We skip all segments which will be removed in each path, since we want to
// know about the true path.
if (!cwk_path_segment_joined_skip_invisible(&base, absolute) ||
!cwk_path_segment_joined_skip_invisible(&other, absolute)) {
break;
}
if (!cwk_path_is_string_equal(base.segment.begin, other.segment.begin,
base.segment.size, other.segment.size)) {
// So the content of those two segments are not equal. We will return the
// size up to the beginning.
return (size_t)(end - path_base);
}
// Remember the end of the previous segment before we go to the next one.
end = base.segment.end;
} while (cwk_path_get_next_segment_joined(&base) &&
cwk_path_get_next_segment_joined(&other));
// Now we calculate the length up to the last point where our paths pointed to
// the same place.
return (size_t)(end - path_base);
}
bool cwk_path_get_first_segment(const char *path, struct cwk_segment *segment)
{
size_t length;
const char *segments;
// We skip the root since that's not part of the first segment. The root is
// treated as a separate entity.
cwk_path_get_root(path, &length);
segments = path + length;
// Now, after we skipped the root we can continue and find the actual segment
// content.
return cwk_path_get_first_segment_without_root(path, segments, segment);
}
bool cwk_path_get_last_segment(const char *path, struct cwk_segment *segment)
{
// We first grab the first segment. This might be our last segment as well,
// but we don't know yet. There is no last segment if there is no first
// segment, so we return false in that case.
if (!cwk_path_get_first_segment(path, segment)) {
return false;
}
// Now we find our last segment. The segment struct of the caller
// will contain the last segment, since the function we call here will not
// change the segment struct when it reaches the end.
while (cwk_path_get_next_segment(segment)) {
// We just loop until there is no other segment left.
}
return true;
}
bool cwk_path_get_next_segment(struct cwk_segment *segment)
{
const char *c;
// First we jump to the end of the previous segment. The first character must
// be either a '\0' or a separator.
c = segment->begin + segment->size;
if (*c == '\0') {
return false;
}
// Now we skip all separator until we reach something else. We are not yet
// guaranteed to have a segment, since the string could just end afterwards.
assert(cwk_path_is_separator(c));
do {
++c;
} while (cwk_path_is_separator(c));
// If the string ends here, we can safely assume that there is no other
// segment after this one.
if (*c == '\0') {
return false;
}
// Now we are safe to assume there is a segment. We store the beginning of
// this segment in the segment struct of the caller.
segment->begin = c;
// And now determine the size of this segment, and store it in the struct of
// the caller as well.
c = cwk_path_find_next_stop(c);
segment->end = c;
segment->size = (size_t)(c - segment->begin);
// Tell the caller that we found a segment.
return true;
}
bool cwk_path_get_previous_segment(struct cwk_segment *segment)
{
const char *c;
// The current position might point to the first character of the path, which
// means there are no previous segments available.
c = segment->begin;
if (c <= segment->segments) {
return false;
}
// We move towards the beginning of the path until we either reached the
// beginning or the character is no separator anymore.
do {
--c;
if (c < segment->segments) {
// So we reached the beginning here and there is no segment. So we return
// false and don't change the segment structure submitted by the caller.
return false;
}
} while (cwk_path_is_separator(c));
// We are guaranteed now that there is another segment, since we moved before
// the previous separator and did not reach the segment path beginning.
segment->end = c + 1;
segment->begin = cwk_path_find_previous_stop(segment->segments, c);
segment->size = (size_t)(segment->end - segment->begin);
return true;
}
enum cwk_segment_type cwk_path_get_segment_type(
const struct cwk_segment *segment)
{
// We just make a string comparison with the segment contents and return the
// appropriate type.
if (strncmp(segment->begin, ".", segment->size) == 0) {
return CWK_CURRENT;
} else if (strncmp(segment->begin, "..", segment->size) == 0) {
return CWK_BACK;
}
return CWK_NORMAL;
}
bool cwk_path_is_separator(const char *str)
{
const char *c;
// We loop over all characters in the read symbols.
c = separators[path_style];
while (*c) {
if (*c == *str) {
return true;
}
++c;
}
return false;
}
size_t cwk_path_change_segment(struct cwk_segment *segment, const char *value,
char *buffer, size_t buffer_size)
{
size_t pos, value_size, tail_size;
// First we have to output the head, which is the whole string up to the
// beginning of the segment. This part of the path will just stay the same.
pos = cwk_path_output_sized(buffer, buffer_size, 0, segment->path,
(size_t)(segment->begin - segment->path));
// In order to trip the submitted value, we will skip any separator at the
// beginning of it and behave as if it was never there.
while (cwk_path_is_separator(value)) {
++value;
}
// Now we determine the length of the value. In order to do that we first
// locate the '\0'.
value_size = 0;
while (value[value_size]) {
++value_size;
}
// Since we trim separators at the beginning and in the end of the value we
// have to subtract from the size until there are either no more characters
// left or the last character is no separator.
while (value_size > 0 && cwk_path_is_separator(&value[value_size - 1])) {
--value_size;
}
// We also have to determine the tail size, which is the part of the string
// following the current segment. This part will not change.
tail_size = strlen(segment->end);
// Now we output the tail. We have to do that, because if the buffer and the
// source are overlapping we would override the tail if the value is
// increasing in length.
cwk_path_output_sized(buffer, buffer_size, pos + value_size, segment->end,
tail_size);
// Finally we can output the value in the middle of the head and the tail,
// where we have enough space to fit the whole trimmed value.
pos += cwk_path_output_sized(buffer, buffer_size, pos, value, value_size);
// Now we add the tail size to the current position and terminate the output -
// basically, ensure that there is a '\0' at the end of the buffer.
pos += tail_size;
cwk_path_terminate_output(buffer, buffer_size, pos);
// And now tell the caller how long the whole path would be.
return pos;
}
enum cwk_path_style cwk_path_guess_style(const char *path)
{
const char *c;
size_t root_length;
struct cwk_segment segment;
// First we determine the root. Only windows roots can be longer than a single
// slash, so if we can determine that it starts with something like "C:", we
// know that this is a windows path.
cwk_path_get_root_windows(path, &root_length);
if (root_length > 1) {
return CWK_STYLE_WINDOWS;
}
// Next we check for slashes. Windows uses backslashes, while unix uses
// forward slashes. Windows actually supports both, but our best guess is to
// assume windows with backslashes and unix with forward slashes.
for (c = path; *c; ++c) {
if (*c == *separators[CWK_STYLE_UNIX]) {
return CWK_STYLE_UNIX;
} else if (*c == *separators[CWK_STYLE_WINDOWS]) {
return CWK_STYLE_WINDOWS;
}
}
// This path does not have any slashes. We grab the last segment (which
// actually must be the first one), and determine whether the segment starts
// with a dot. A dot is a hidden folder or file in the UNIX world, in that
// case we assume the path to have UNIX style.
if (!cwk_path_get_last_segment(path, &segment)) {
// We couldn't find any segments, so we default to a UNIX path style since
// there is no way to make any assumptions.
return CWK_STYLE_UNIX;
}
if (*segment.begin == '.') {
return CWK_STYLE_UNIX;
}
// And finally we check whether the last segment contains a dot. If it
// contains a dot, that might be an extension. Windows is more likely to have
// file names with extensions, so our guess would be windows.
for (c = segment.begin; *c; ++c) {
if (*c == '.') {
return CWK_STYLE_WINDOWS;
}
}
// All our checks failed, so we will return a default value which is currently
// UNIX.
return CWK_STYLE_UNIX;
}
void cwk_path_set_style(enum cwk_path_style style)
{
// We can just set the global path style variable and then the behaviour for
// all functions will change accordingly.
assert(style == CWK_STYLE_UNIX || style == CWK_STYLE_WINDOWS);
path_style = style;
}
enum cwk_path_style cwk_path_get_style(void)
{
// Simply return the path style which we store in a global variable.
return path_style;
}
|