ObjFW  Check-in [a7ce7bb441]

Overview
Comment:Treat the leading slash of a path as a component
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a7ce7bb44102d696b6ab0051d13dcf8d766e0ba5012d5396efaec35194a9ad55
User & Date: js on 2018-03-13 23:05:56
Other Links: manifest | tags
Context
2018-03-17
21:53
Add +[OFSystemInfo operatingSystem{Name,Version}] check-in: c6ed29d881 user: js tags: trunk
2018-03-13
23:05
Treat the leading slash of a path as a component check-in: a7ce7bb441 user: js tags: trunk
2018-03-11
23:11
OFHTTPClient: Throw if socket got closed too early check-in: 69c2ca803a user: js tags: trunk
Changes

Modified src/OFString+PathAdditions_UNIX.m from [223d3e3c81] to [800379d99e].

23
24
25
26
27
28
29













30





31
32
33
34
35
36
37
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







+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+







#import "OFOutOfRangeException.h"

int _OFString_PathAdditions_reference;

@implementation OFString (PathAdditions)
+ (OFString *)pathWithComponents: (OFArray *)components
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret;

	if ([[components firstObject] isEqual: @"/"]) {
		OFMutableArray OF_GENERIC(OFString *) *mutableComponents =
		    [[components mutableCopy] autorelease];

		[mutableComponents replaceObjectAtIndex: 0
					     withObject: @""];

		components = mutableComponents;
	}

	return [components componentsJoinedByString: @"/"];
	ret = [components componentsJoinedByString: @"/"];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (bool)isAbsolutePath
{
	return [self hasPrefix: @"/"];
}

56
57
58
59
60
61
62




63
64
65
66
67
68
69
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90







+
+
+
+







			    [OFString stringWithUTF8String: cString + last
						    length: i - last]];
			last = i + 1;
		}
	}
	[ret addObject: [OFString stringWithUTF8String: cString + last
						length: i - last]];

	if ([[ret firstObject] isEqual: @""])
		[ret replaceObjectAtIndex: 0
			       withObject: @"/"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}
208
209
210
211
212
213
214
215

216
217
218


219










220




221
222

223
224
225
226
227
228
229
230
231
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







-
+

-
-
+
+

+
+
+
+
+
+
+
+
+
+

+
+
+
+

-
+

-







	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (OFString *)stringByStandardizingPath
{
	void *pool = objc_autoreleasePoolPush();
	OFArray OF_GENERIC(OFString *) *components = [self pathComponents];
	OFArray OF_GENERIC(OFString *) *components;
	OFMutableArray OF_GENERIC(OFString *) *array;
	OFString *ret;
	bool done = false, startsWithEmpty, endsWithEmpty;
	OFString *firstComponent, *ret;
	bool done = false, startsWithSlash, endsWithEmpty;

	if ([self length] == 0)
		return @"";

	components = [self pathComponents];

	if ([components count] == 1) {
		objc_autoreleasePoolPop(pool);
		return self;
	}

	array = [[components mutableCopy] autorelease];
	firstComponent = [array firstObject];
	startsWithSlash =
	    ([firstComponent isEqual: @"/"] || [firstComponent length] == 0);
	endsWithEmpty = ([[array lastObject] length] == 0);

	if ((startsWithEmpty = [[array firstObject] isEqual: @""]))
	if (startsWithSlash)
		[array removeObjectAtIndex: 0];
	endsWithEmpty = [[array lastObject] isEqual: @""];

	while (!done) {
		size_t length = [array count];

		done = true;

		for (size_t i = 0; i < length; i++) {
248
249
250
251
252
253
254
255

256
257

258
259
260
261
262
263
264
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296
297
298
299







-
+


+








				done = false;
				break;
			}
		}
	}

	if (startsWithEmpty)
	if (startsWithSlash)
		[array insertObject: @""
			    atIndex: 0];

	if (endsWithEmpty)
		[array addObject: @""];

	ret = [[array componentsJoinedByString: @"/"] retain];

	objc_autoreleasePoolPop(pool);

Modified src/OFURL.m from [6014ddabe8] to [c9e9081959].

962
963
964
965
966
967
968
969
970


971
972
973
974
975
976
977
962
963
964
965
966
967
968


969
970
971
972
973
974
975
976
977







-
-
+
+








	if ([path hasSuffix: @"/"])
		path = [path substringWithRange:
		    of_range(0, [path length] - 1)];

#if defined(OF_WINDOWS) || defined(OF_MSDOS)
	path = [path substringWithRange: of_range(1, [path length] - 1)];
	path = [OFString pathWithComponents:
	    [path componentsSeparatedByString: @"/"]];
	path = [path stringByReplacingOccurrencesOfString: @"/"
					       withString: @"\\"];
#endif

	[path retain];

	objc_autoreleasePoolPop(pool);

	return [path autorelease];

Modified tests/OFStringTests.m from [067d094bf5] to [fa776f7eaf].

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
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







-
-
+
+
+
+
+
+

-
-

-
-
+
+
-
-
-
-
-
+
+
+
+
+

+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
+



-
+














+

-
+







-
+







-
+







-
+









-
+









-
+







				options: OF_STRING_SKIP_EMPTY]) &&
	    [a count] == 3 &&
	    [[a objectAtIndex: i++] isEqual: @"foo"] &&
	    [[a objectAtIndex: i++] isEqual: @"bar"] &&
	    [[a objectAtIndex: i++] isEqual: @"baz"])

#ifdef OF_HAVE_FILES
# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
#  define EXPECTED @"foo/bar/baz"
# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	TEST(@"+[pathWithComponents:]",
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", nil]] isEqual: @"foo"])
# else
#  define EXPECTED @"foo\\bar\\baz"
# endif
	TEST(@"+[pathWithComponents:]",
	    (is = [stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]]) &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"/", @"foo", @"bar", @"baz", nil]] isEqual: @"/foo/bar/baz"] &&
	    [is isEqual: EXPECTED] &&
	    (is = [stringClass pathWithComponents:
	    [OFArray arrayWithObjects: @"foo", nil]]) &&
	    [is isEqual: @"foo"])
# undef EXPECTED
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", nil]] isEqual: @"foo"])
# endif

# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	TEST(@"-[pathComponents]",
	    /* /tmp */
	    (a = [C(@"c:/tmp") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* /tmp/ */
	    (a = [C(@"c:\\tmp\\") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* / */
	    (a = [C(@"c:/") pathComponents]) && [a count] == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    /* foo/bar */
	    (a = [C(@"foo\\bar") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (a = [C(@"foo\\bar/baz") pathComponents]) && [a count] == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (a = [C(@"foo\\/") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @""] &&
	    [[C(@"") pathComponents] count] == 0)
# else
	TEST(@"-[pathComponents]",
	    /* /tmp */
	    (a = [C(@"/tmp") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @""] &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* /tmp/ */
	    (a = [C(@"/tmp/") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @""] &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* / */
	    (a = [C(@"/") pathComponents]) && [a count] == 1 &&
	    [[a objectAtIndex: 0] isEqual: @""] &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    /* foo/bar */
	    (a = [C(@"foo/bar") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (a = [C(@"foo/bar/baz") pathComponents]) && [a count] == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (a = [C(@"foo//") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @""] &&
	    [[C(@"") pathComponents] count] == 0)
# endif

#if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
	TEST(@"-[lastPathComponent]",
	    [[C(@"/tmp") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"/tmp/") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"/") lastPathComponent] isEqual: @"/"] &&
	    [[C(@"foo") lastPathComponent] isEqual: @"foo"] &&
	    [[C(@"foo/bar") lastPathComponent] isEqual: @"bar"] &&
	    [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"])
#else
# else
	TEST(@"-[lastPathComponent]",
	    [[C(@"c:/tmp") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"c:\\tmp\\") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"\\") lastPathComponent] isEqual: @""] &&
	    [[C(@"foo") lastPathComponent] isEqual: @"foo"] &&
	    [[C(@"foo\\bar") lastPathComponent] isEqual: @"bar"] &&
	    [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"])
#endif
# endif

	TEST(@"-[pathExtension]",
	    [[C(@"foo.bar") pathExtension] isEqual: @"bar"] &&
	    [[C(@"foo/.bar") pathExtension] isEqual: @""] &&
	    [[C(@"foo/.bar.baz") pathExtension] isEqual: @"baz"] &&
	    [[C(@"foo/bar.baz/") pathExtension] isEqual: @"baz"])

#if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
	TEST(@"-[stringByDeletingLastPathComponent]",
	    [[C(@"/tmp") stringByDeletingLastPathComponent] isEqual: @"/"] &&
	    [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @"/"] &&
	    [[C(@"/tmp/foo/") stringByDeletingLastPathComponent]
	    isEqual: @"/tmp"] &&
	    [[C(@"foo/bar") stringByDeletingLastPathComponent]
	    isEqual: @"foo"] &&
	    [[C(@"/") stringByDeletingLastPathComponent] isEqual: @"/"] &&
	    [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @"."])
#else
# else
	TEST(@"-[stringByDeletingLastPathComponent]",
	    [[C(@"\\tmp") stringByDeletingLastPathComponent] isEqual: @""] &&
	    [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @""] &&
	    [[C(@"c:\\tmp/foo/") stringByDeletingLastPathComponent]
	    isEqual: @"c:\\tmp"] &&
	    [[C(@"foo\\bar") stringByDeletingLastPathComponent]
	    isEqual: @"foo"] &&
	    [[C(@"\\") stringByDeletingLastPathComponent] isEqual: @""] &&
	    [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @"."])
#endif
# endif

# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
#  define EXPECTED @"/foo./bar"
# else
#  define EXPECTED @"\\foo.\\bar"
# endif
	TEST(@"-[stringByDeletingPathExtension]",