ObjFW  Check-in [69148d4e5e]

Overview
Comment:Ignore empty and slash-only path components
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 69148d4e5ea7a36ae67bd3eaf046975a9d40091697a735443c55a9e29700c320
User & Date: js on 2018-03-19 23:42:15
Other Links: manifest | tags
Context
2018-04-07
14:13
Add OFSecureData check-in: d4d9072480 user: js tags: trunk
2018-03-19
23:42
Ignore empty and slash-only path components check-in: 69148d4e5e user: js tags: trunk
2018-03-18
06:05
Fix missing #ifdef OF_HAVE_FILES check-in: f059c39b00 user: js tags: trunk
Changes

Modified src/OFString+PathAdditions_DOS.m from [466313d925] to [d05bf752db].

23
24
25
26
27
28
29












30









31
32
33
34
35
36
37
#import "OFOutOfRangeException.h"

int _OFString_PathAdditions_reference;

@implementation OFString (PathAdditions)
+ (OFString *)pathWithComponents: (OFArray *)components
{












	return [components componentsJoinedByString: @"\\"];









}

- (bool)isAbsolutePath
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = [self UTF8String];
	size_t UTF8StringLength = [self UTF8StringLength];







>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>







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
#import "OFOutOfRangeException.h"

int _OFString_PathAdditions_reference;

@implementation OFString (PathAdditions)
+ (OFString *)pathWithComponents: (OFArray *)components
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	bool first = true;

	for (OFString *component in components) {
		if ([component length] == 0)
			continue;

		if ([component isEqual: @"\\"] || [component isEqual: @"/"])
			continue;

		if (!first && ![ret hasSuffix: @"\\"] && ![ret hasSuffix: @"/"])
			[ret appendString: @"\\"];

		[ret appendString: component];

		first = false;
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)isAbsolutePath
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = [self UTF8String];
	size_t UTF8StringLength = [self UTF8StringLength];
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
	size_t i, last = 0, pathCStringLength = [self UTF8StringLength];

	if (pathCStringLength == 0) {
		objc_autoreleasePoolPop(pool);
		return ret;
	}

	if (cString[pathCStringLength - 1] == '\\' ||
	    cString[pathCStringLength - 1] == '/')
		pathCStringLength--;

	for (i = 0; i < pathCStringLength; i++) {
		if (cString[i] == '\\' || cString[i] == '/') {

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

			last = i + 1;
		}
	}

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

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}







<
<
<
<


>
|
|
|
>



>
|
|







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
	size_t i, last = 0, pathCStringLength = [self UTF8StringLength];

	if (pathCStringLength == 0) {
		objc_autoreleasePoolPop(pool);
		return ret;
	}





	for (i = 0; i < pathCStringLength; i++) {
		if (cString[i] == '\\' || cString[i] == '/') {
			if (i - last != 0)
				[ret addObject: [OFString
				    stringWithUTF8String: cString + last
						  length: i - last]];

			last = i + 1;
		}
	}
	if (i - last != 0)
		[ret addObject: [OFString stringWithUTF8String: cString + last
							length: i - last]];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

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

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
#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;
	}


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

	[ret retain];
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

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

- (OFArray *)pathComponents
{
	OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	const char *cString = [self UTF8String];
	size_t i, last = 0, pathCStringLength = [self UTF8StringLength];

	if (pathCStringLength == 0) {
		objc_autoreleasePoolPop(pool);
		return ret;
	}

	if (cString[pathCStringLength - 1] == '/')
		pathCStringLength--;

	for (i = 0; i < pathCStringLength; i++) {
		if (cString[i] == '/') {



			[ret addObject:
			    [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;
}







>

|

|
<
|
>

|
>
|
>
>

|
|
>
|
<

<

>
|



















<
<
<


>
>
>
|
|
|
>



>
|
|
<
<
<
<







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
#import "OFOutOfRangeException.h"

int _OFString_PathAdditions_reference;

@implementation OFString (PathAdditions)
+ (OFString *)pathWithComponents: (OFArray *)components
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	bool first = true;

	for (OFString *component in components) {

		if ([component length] == 0)
			continue;

		if (!first && [component isEqual: @"/"])
			continue;

		if (!first && ![ret hasSuffix: @"/"])
			[ret appendString: @"/"];

		[ret appendString: component];

		first = false;
	}



	objc_autoreleasePoolPop(pool);

	return ret;
}

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

- (OFArray *)pathComponents
{
	OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	const char *cString = [self UTF8String];
	size_t i, last = 0, pathCStringLength = [self UTF8StringLength];

	if (pathCStringLength == 0) {
		objc_autoreleasePoolPop(pool);
		return ret;
	}




	for (i = 0; i < pathCStringLength; i++) {
		if (cString[i] == '/') {
			if (i == 0)
				[ret addObject: @"/"];
			else if (i - last != 0)
				[ret addObject: [OFString
				    stringWithUTF8String: cString + last
						  length: i - last]];

			last = i + 1;
		}
	}
	if (i - last != 0)
		[ret addObject: [OFString stringWithUTF8String: cString + last
							length: i - last]];





	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

Modified tests/OFStringTests.m from [fa776f7eaf] to [78bcb36f91].

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
	    [[a objectAtIndex: i++] isEqual: @"baz"])

#ifdef OF_HAVE_FILES
# 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
	TEST(@"+[pathWithComponents:]",
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"/", @"foo", @"bar", @"baz", nil]] isEqual: @"/foo/bar/baz"] &&
	    [[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: 1] isEqual: @"tmp"] &&







>
>
>








>
>
>






|



|



|


|



|




|
|

<







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
	    [[a objectAtIndex: i++] isEqual: @"baz"])

#ifdef OF_HAVE_FILES
# 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/", @"bar\\", @"", @"baz", @"\\", nil]]
	    isEqual: @"foo/bar\\baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", nil]] isEqual: @"foo"])
# else
	TEST(@"+[pathWithComponents:]",
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"/", @"foo", @"bar", @"baz", nil]] isEqual: @"/foo/bar/baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] &&
	    [[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]",
	    /* c:/tmp */
	    (a = [C(@"c:/tmp") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\tmp\ */
	    (a = [C(@"c:\\tmp\\") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:/ */
	    (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] == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&

	    [[C(@"") pathComponents] count] == 0)
# else
	TEST(@"-[pathComponents]",
	    /* /tmp */
	    (a = [C(@"/tmp") pathComponents]) && [a count] == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
	    [[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)
	TEST(@"-[lastPathComponent]",
	    [[C(@"/tmp") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"/tmp/") lastPathComponent] isEqual: @"tmp"] &&







|

<







688
689
690
691
692
693
694
695
696

697
698
699
700
701
702
703
	    [[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] == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&

	    [[C(@"") pathComponents] count] == 0)
# endif

# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
	TEST(@"-[lastPathComponent]",
	    [[C(@"/tmp") lastPathComponent] isEqual: @"tmp"] &&
	    [[C(@"/tmp/") lastPathComponent] isEqual: @"tmp"] &&
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

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

# if !defined(OF_WINDOWS) && !defined(OF_MSDOS)





#  define EXPECTED @"/foo./bar"




# else
#  define EXPECTED @"\\foo.\\bar"
# endif
	TEST(@"-[stringByDeletingPathExtension]",
	    [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] &&
	    [[C(@"/foo./bar") stringByDeletingPathExtension]
	    isEqual: @"/foo./bar"] &&
	    [[C(@"/foo./bar.baz") stringByDeletingPathExtension]
	    isEqual: EXPECTED] &&
	    [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] &&


	    [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"])
# undef EXPECTED
#endif

	TEST(@"-[decimalValue]",
	    [C(@"1234") decimalValue] == 1234 &&
	    [C(@"\r\n+123  ") decimalValue] == 123 &&
	    [C(@"-500\t") decimalValue] == -500 &&
	    [C(@"\t\t\r\n") decimalValue] == 0)







|
<
<
<
<
<
<
<
<
<
<









>
>
>
>
>
>
>
>
>
>


|
>
>
>
>
>
|
>
>
>
>

<
<






|


>
>

|







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

	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)










	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: @"."])
# else
	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: @"."])
# endif

# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	TEST(@"-[stringByDeletingPathExtension]",
	    [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] &&
	    [[C(@"c:/foo.\\bar") stringByDeletingPathExtension]
	    isEqual: @"c:/foo.\\bar"] &&
	    [[C(@"c:\\foo./bar.baz") stringByDeletingPathExtension]
	    isEqual: @"c:\\foo.\\bar"] &&
	    [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] &&
	    [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"])
# else


	TEST(@"-[stringByDeletingPathExtension]",
	    [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] &&
	    [[C(@"/foo./bar") stringByDeletingPathExtension]
	    isEqual: @"/foo./bar"] &&
	    [[C(@"/foo./bar.baz") stringByDeletingPathExtension]
	    isEqual: @"/foo./bar"] &&
	    [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] &&
	    [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] &&
	    [[C(@".foo\\bar") stringByDeletingPathExtension]
	    isEqual: @".foo\\bar"] &&
	    [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"])
# endif
#endif

	TEST(@"-[decimalValue]",
	    [C(@"1234") decimalValue] == 1234 &&
	    [C(@"\r\n+123  ") decimalValue] == 123 &&
	    [C(@"-500\t") decimalValue] == -500 &&
	    [C(@"\t\t\r\n") decimalValue] == 0)