ObjFW  Check-in [06bcb21fc7]

Overview
Comment:Add OFLocalization

This singleton gives access to all things locale, including the ability
to get localized strings.

This also adds the OF_LOCALIZED() macro. Its first argument is an ID for
the string to be localized and its second argument is the fallback
string to be used if it cannot retrieve the localized string. Following
that are variable name / value pairs to be replaced in the localized
string.

Getting translated strings is not implemented yet: Instead, it always
uses the fallback string.

This also switches ofhttp to localized strings.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 06bcb21fc76b4915588681cef8a6e5f455f78562f938bc3532e34fca30a00cbc
User & Date: js on 2017-01-09 06:26:04
Other Links: manifest | tags
Context
2017-01-09
17:36
Update copyright check-in: 44f45c2e35 user: js tags: trunk
06:26
Add OFLocalization check-in: 06bcb21fc7 user: js tags: trunk
2017-01-08
20:14
Clean up the dllexport mess a little check-in: f57765b5c6 user: js tags: trunk
Changes

Modified ObjFW.xcodeproj/project.pbxproj from [33b1d62e30] to [368861be8c].

675
676
677
678
679
680
681




682
683
684
685
686
687
688
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692







+
+
+
+







		4B3ED7C31AF62C30004C8FF1 /* OFGetOptionFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3ED7C11AF62C30004C8FF1 /* OFGetOptionFailedException.m */; };
		4B40EC1B189FE2650031E19E /* socket.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B40EC1A189FE2650031E19E /* socket.m */; };
		4B44836D1D0497DE005D12A7 /* OFUndefinedKeyException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B44836B1D0497DE005D12A7 /* OFUndefinedKeyException.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B44836E1D0497DE005D12A7 /* OFUndefinedKeyException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B44836C1D0497DE005D12A7 /* OFUndefinedKeyException.m */; };
		4B4483711D04989C005D12A7 /* RuntimeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B44836F1D049887005D12A7 /* RuntimeTests.m */; };
		4B45355313DCFE1E0037AB4D /* OFCountedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B45355413DCFE1E0037AB4D /* OFCountedSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */; };
		4B46E2211E235EA700121ED6 /* OFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B46E21F1E235EA700121ED6 /* OFLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B46E2221E235EA700121ED6 /* OFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B46E21F1E235EA700121ED6 /* OFLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B46E2231E235EA700121ED6 /* OFLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B46E2201E235EA700121ED6 /* OFLocalization.m */; };
		4B46E2241E235EA700121ED6 /* OFLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B46E2201E235EA700121ED6 /* OFLocalization.m */; };
		4B48B95414DC23B100546D39 /* OFXMLProcessingInstructions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B48B95214DC23B100546D39 /* OFXMLProcessingInstructions.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B48B95514DC23B100546D39 /* OFXMLProcessingInstructions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B48B95314DC23B100546D39 /* OFXMLProcessingInstructions.m */; };
		4B49EA66143B39CE0005BBC6 /* OFXMLNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B49EA65143B39CE0005BBC6 /* OFXMLNodeTests.m */; };
		4B49EA6D143B3A090005BBC6 /* OFXMLCDATA.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B49EA67143B3A090005BBC6 /* OFXMLCDATA.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B49EA6E143B3A090005BBC6 /* OFXMLCDATA.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B49EA68143B3A090005BBC6 /* OFXMLCDATA.m */; };
		4B49EA6F143B3A090005BBC6 /* OFXMLCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B49EA69143B3A090005BBC6 /* OFXMLCharacters.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4B49EA70143B3A090005BBC6 /* OFXMLCharacters.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B49EA6A143B3A090005BBC6 /* OFXMLCharacters.m */; };
1248
1249
1250
1251
1252
1253
1254


1255
1256
1257
1258
1259
1260
1261
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267







+
+







		4B3ED7C11AF62C30004C8FF1 /* OFGetOptionFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFGetOptionFailedException.m; path = src/exceptions/OFGetOptionFailedException.m; sourceTree = "<group>"; };
		4B40EC1A189FE2650031E19E /* socket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = socket.m; path = src/socket.m; sourceTree = "<group>"; };
		4B44836B1D0497DE005D12A7 /* OFUndefinedKeyException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFUndefinedKeyException.h; path = src/exceptions/OFUndefinedKeyException.h; sourceTree = "<group>"; };
		4B44836C1D0497DE005D12A7 /* OFUndefinedKeyException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFUndefinedKeyException.m; path = src/exceptions/OFUndefinedKeyException.m; sourceTree = "<group>"; };
		4B44836F1D049887005D12A7 /* RuntimeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RuntimeTests.m; path = tests/RuntimeTests.m; sourceTree = "<group>"; };
		4B45355113DCFE1E0037AB4D /* OFCountedSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCountedSet.h; path = src/OFCountedSet.h; sourceTree = "<group>"; };
		4B45355213DCFE1E0037AB4D /* OFCountedSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCountedSet.m; path = src/OFCountedSet.m; sourceTree = "<group>"; };
		4B46E21F1E235EA700121ED6 /* OFLocalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFLocalization.h; path = src/OFLocalization.h; sourceTree = "<group>"; };
		4B46E2201E235EA700121ED6 /* OFLocalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFLocalization.m; path = src/OFLocalization.m; sourceTree = "<group>"; };
		4B48B95214DC23B100546D39 /* OFXMLProcessingInstructions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLProcessingInstructions.h; path = src/OFXMLProcessingInstructions.h; sourceTree = "<group>"; };
		4B48B95314DC23B100546D39 /* OFXMLProcessingInstructions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLProcessingInstructions.m; path = src/OFXMLProcessingInstructions.m; sourceTree = "<group>"; };
		4B49EA65143B39CE0005BBC6 /* OFXMLNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLNodeTests.m; path = tests/OFXMLNodeTests.m; sourceTree = "<group>"; };
		4B49EA67143B3A090005BBC6 /* OFXMLCDATA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLCDATA.h; path = src/OFXMLCDATA.h; sourceTree = "<group>"; };
		4B49EA68143B3A090005BBC6 /* OFXMLCDATA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLCDATA.m; path = src/OFXMLCDATA.m; sourceTree = "<group>"; };
		4B49EA69143B3A090005BBC6 /* OFXMLCharacters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLCharacters.h; path = src/OFXMLCharacters.h; sourceTree = "<group>"; };
		4B49EA6A143B3A090005BBC6 /* OFXMLCharacters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLCharacters.m; path = src/OFXMLCharacters.m; sourceTree = "<group>"; };
1971
1972
1973
1974
1975
1976
1977


1978
1979
1980
1981
1982
1983
1984
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992







+
+







				4B0EA9161898690E00F573A4 /* OFKernelEventObserver_select.h */,
				4B0EA9171898690E00F573A4 /* OFKernelEventObserver_select.m */,
				4BC176231D04963000C32718 /* OFKeyValueCoding.h */,
				4BC176241D04963000C32718 /* OFObject+KeyValueCoding.h */,
				4BC176251D04963000C32718 /* OFObject+KeyValueCoding.m */,
				4B67996C1099E7C50041064A /* OFList.h */,
				4B67996D1099E7C50041064A /* OFList.m */,
				4B46E21F1E235EA700121ED6 /* OFLocalization.h */,
				4B46E2201E235EA700121ED6 /* OFLocalization.m */,
				4B6743F9163C395900EB1E59 /* OFLocking.h */,
				4B3B0796166978780044E634 /* OFMapTable.h */,
				4B3B0797166978780044E634 /* OFMapTable.m */,
				4BC1C3EA184B5EB200BBF50F /* OFMapTable+Private.h */,
				4BF1BCC211C9663F0025511F /* OFMD5Hash.h */,
				4BF1BCC311C9663F0025511F /* OFMD5Hash.m */,
				4BCAA9AD1772432E003EF859 /* OFMessagePackExtension.h */,
2341
2342
2343
2344
2345
2346
2347

2348
2349
2350
2351
2352
2353
2354
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363







+







				4B2C21F41DA292BE00735907 /* OFINICategory.h in Headers */,
				4B2C21F51DA292BE00735907 /* OFINIFile.h in Headers */,
				4B2C21F61DA292BE00735907 /* OFIntrospection.h in Headers */,
				4B2C21F71DA292BE00735907 /* OFJSONRepresentation.h in Headers */,
				4B2C21F81DA292BE00735907 /* OFKernelEventObserver.h in Headers */,
				4B2C21F91DA292BE00735907 /* OFKeyValueCoding.h in Headers */,
				4B2C21FA1DA292BE00735907 /* OFList.h in Headers */,
				4B46E2221E235EA700121ED6 /* OFLocalization.h in Headers */,
				4B2C21FB1DA292BE00735907 /* OFLocking.h in Headers */,
				4B2C21FC1DA292BE00735907 /* OFMapTable.h in Headers */,
				4B2C21FD1DA292BE00735907 /* OFMD5Hash.h in Headers */,
				4B2C21FE1DA292BE00735907 /* OFMessagePackExtension.h in Headers */,
				4B2C21FF1DA292BE00735907 /* OFMessagePackRepresentation.h in Headers */,
				4B2C22001DA292BE00735907 /* OFMutableArray.h in Headers */,
				4B2C22011DA292BE00735907 /* OFMutableDictionary.h in Headers */,
2558
2559
2560
2561
2562
2563
2564

2565
2566
2567
2568
2569
2570
2571
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581







+







				4B06855318B2AD3800FC731A /* OFINICategory.h in Headers */,
				4B5B02BE18B288A400CE6AE4 /* OFINIFile.h in Headers */,
				4BA49D9013DB113B00381CDB /* OFIntrospection.h in Headers */,
				4BA02BA115041F5900002F84 /* OFJSONRepresentation.h in Headers */,
				4B0EA9211898690E00F573A4 /* OFKernelEventObserver.h in Headers */,
				4BC1762F1D04963000C32718 /* OFKeyValueCoding.h in Headers */,
				4B3D23CB1337FCB000DD29B8 /* OFList.h in Headers */,
				4B46E2211E235EA700121ED6 /* OFLocalization.h in Headers */,
				4B674402163C395900EB1E59 /* OFLocking.h in Headers */,
				4B3B0798166978780044E634 /* OFMapTable.h in Headers */,
				4B3D23CC1337FCB000DD29B8 /* OFMD5Hash.h in Headers */,
				4BCAA9AF1772432F003EF859 /* OFMessagePackExtension.h in Headers */,
				4B879A8E177231F000EBCEA4 /* OFMessagePackRepresentation.h in Headers */,
				4B3D23CD1337FCB000DD29B8 /* OFMutableArray.h in Headers */,
				4B3D23CE1337FCB000DD29B8 /* OFMutableDictionary.h in Headers */,
3095
3096
3097
3098
3099
3100
3101

3102
3103
3104
3105
3106
3107
3108
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119







+







				4B2C213E1DA292BE00735907 /* OFIntrospection.m in Sources */,
				4B2C213F1DA292BE00735907 /* OFGZIPStream.m in Sources */,
				4B2C21401DA292BE00735907 /* OFKernelEventObserver.m in Sources */,
				4B2C21411DA292BE00735907 /* OFKernelEventObserver_kqueue.m in Sources */,
				4B2C21421DA292BE00735907 /* OFKernelEventObserver_poll.m in Sources */,
				4B2C21431DA292BE00735907 /* OFKernelEventObserver_select.m in Sources */,
				4B2C21441DA292BE00735907 /* OFList.m in Sources */,
				4B46E2241E235EA700121ED6 /* OFLocalization.m in Sources */,
				4B2C21451DA292BE00735907 /* OFMapTable.m in Sources */,
				4B2C21461DA292BE00735907 /* OFMD5Hash.m in Sources */,
				4B2C21471DA292BE00735907 /* OFMessagePackExtension.m in Sources */,
				4B2C21481DA292BE00735907 /* OFMutableArray.m in Sources */,
				4B2C21491DA292BE00735907 /* OFMutableArray_adjacent.m in Sources */,
				4B2C214A1DA292BE00735907 /* OFMutableDictionary.m in Sources */,
				4B2C214B1DA292BE00735907 /* OFMutableDictionary_hashtable.m in Sources */,
3282
3283
3284
3285
3286
3287
3288

3289
3290
3291
3292
3293
3294
3295
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307







+







				4BA49D9113DB113B00381CDB /* OFIntrospection.m in Sources */,
				4BD112611CCB73A60076FDB9 /* OFGZIPStream.m in Sources */,
				4B0EA9221898690E00F573A4 /* OFKernelEventObserver.m in Sources */,
				4B0EA91C1898690E00F573A4 /* OFKernelEventObserver_kqueue.m in Sources */,
				4B0EA91E1898690E00F573A4 /* OFKernelEventObserver_poll.m in Sources */,
				4B0EA9201898690E00F573A4 /* OFKernelEventObserver_select.m in Sources */,
				4B3D23991337FC0D00DD29B8 /* OFList.m in Sources */,
				4B46E2231E235EA700121ED6 /* OFLocalization.m in Sources */,
				4B3B0799166978780044E634 /* OFMapTable.m in Sources */,
				4B3D239A1337FC0D00DD29B8 /* OFMD5Hash.m in Sources */,
				4BCAA9B01772432F003EF859 /* OFMessagePackExtension.m in Sources */,
				4B3D239B1337FC0D00DD29B8 /* OFMutableArray.m in Sources */,
				4B2B3E82140D430500EC2F7C /* OFMutableArray_adjacent.m in Sources */,
				4B3D239C1337FC0D00DD29B8 /* OFMutableDictionary.m in Sources */,
				4B2B3E84140D430500EC2F7C /* OFMutableDictionary_hashtable.m in Sources */,

Modified src/Makefile from [dc8c07a997] to [143a1e3a3b].

24
25
26
27
28
29
30

31
32
33
34
35
36
37
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38







+







       OFDeflate64Stream.m		\
       OFDictionary.m			\
       OFEnumerator.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFIntrospection.m		\
       OFList.m				\
       OFLocalization.m			\
       OFMapTable.m			\
       OFMD5Hash.m			\
       OFMessagePackExtension.m		\
       OFMutableArray.m			\
       OFMutableDictionary.m		\
       OFMutableSet.m			\
       OFMutableString.m		\

Modified src/OFApplication.m from [4b696ca492] to [514600c380].

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
16
17
18
19
20
21
22



23
24
25
26
27
28
29

30
31
32
33
34
35
36
37







-
-
-







-
+








#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_LANGINFO_H
# include <langinfo.h>
#endif
#include <locale.h>
#include <signal.h>

#import "OFApplication.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFThread.h"
#import "OFThread+Private.h"

#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
65
66
67
68
69
70
71

72
73
74
75
76
77
78







-







- (void)OF_setArgumentCount: (int)argc
      andWideArgumentValues: (wchar_t*[])argv;
#endif
- (void)OF_run;
@end

static OFApplication *app = nil;
extern void of_system_info_parse_locale(char*);

static void
atexitHandler(void)
{
	id <OFApplicationDelegate> delegate = [app delegate];

	if ([delegate respondsToSelector: @selector(applicationWillTerminate)])
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115







-
+







{
	id <OFApplicationDelegate> delegate;
#ifdef OF_WINDOWS
	wchar_t **wargv, **wenvp;
	int wargc, si = 0;
#endif

	of_system_info_parse_locale(setlocale(LC_ALL, ""));
	[[OFLocalization alloc] initWithLocale: setlocale(LC_ALL, "")];

	if ([cls isSubclassOfClass: [OFApplication class]]) {
		fprintf(stderr, "FATAL ERROR:\n  Class %s is a subclass of "
		    "class OFApplication, but class\n  %s was specified as "
		    "application delegate!\n  Most likely, you wanted to "
		    "subclass OFObject instead or specified\n  the wrong class "
		    "with OF_APPLICATION_DELEGATE().\n",
244
245
246
247
248
249
250



251
252
253
254
255
256
257
258
259
260
261
262
240
241
242
243
244
245
246
247
248
249
250
251
252


253
254
255
256
257
258
259







+
+
+



-
-








			objc_autoreleasePoolPop(pool);
		}

		FreeEnvironmentStringsW(env0);
#elif !defined(OF_IOS)
		if (env != NULL) {
			const of_string_encoding_t encoding =
			    [OFLocalization encoding];

			for (; *env != NULL; env++) {
				OFString *key, *value;
				char *sep;
				const of_string_encoding_t encoding =
				    [OFSystemInfo native8BitEncoding];

				pool = objc_autoreleasePoolPush();

				if ((sep = strchr(*env, '=')) == NULL) {
					fprintf(stderr, "Warning: Invalid "
					    "environment variable: %s\n", *env);
					continue;
349
350
351
352
353
354
355
356

357
358
359
360
361
362
363
346
347
348
349
350
351
352

353
354
355
356
357
358
359
360







-
+







	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;
	of_string_encoding_t encoding;

	_argc = argc;
	_argv = argv;

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

# ifndef OF_NINTENDO_DS
	if (*argc > 0) {
# else
	if (__system_argv->argvMagic == ARGV_MAGIC &&
	    __system_argv->argc > 0) {
# endif

Modified src/OFFile.m from [70abf75d98] to [6d0086e491].

32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46







-
+







#ifdef OF_NINTENDO_DS
# include <stdbool.h>
# include <filesystem.h>
#endif

#import "OFFile.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSeekFailedException.h"
162
163
164
165
166
167
168
169
170


171
172
173


174
175
176
177
178
179
180
162
163
164
165
166
167
168


169
170
171


172
173
174
175
176
177
178
179
180







-
-
+
+

-
-
+
+








		flags |= O_CLOEXEC;

#if defined(OF_WINDOWS)
		if ((_fd = _wopen([path UTF16String], flags,
		    DEFAULT_MODE)) == -1)
#elif defined(OF_HAVE_OFF64_T)
		if ((_fd = open64([path cStringWithEncoding: [OFSystemInfo
		    native8BitEncoding]], flags, DEFAULT_MODE)) == -1)
		if ((_fd = open64([path cStringWithEncoding:
		    [OFLocalization encoding]], flags, DEFAULT_MODE)) == -1)
#else
		if ((_fd = open([path cStringWithEncoding: [OFSystemInfo
		    native8BitEncoding]], flags, DEFAULT_MODE)) == -1)
		if ((_fd = open([path cStringWithEncoding:
		    [OFLocalization encoding]], flags, DEFAULT_MODE)) == -1)
#endif
			@throw [OFOpenItemFailedException
			    exceptionWithPath: path
					 mode: mode
					errNo: errno];
	} @catch (id e) {
		[self release];

Modified src/OFFileManager.m from [bdb65f91bc] to [c2a222bc22].

30
31
32
33
34
35
36

37
38
39
40
41
42
43
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44







+








#import "OFFileManager.h"
#import "OFFile.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif

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







-
-
+
+

-
-
+
+










-
-
+
+

-
-
+
+



-
-
+
+

-
-
+
+








int
of_stat(OFString *path, of_stat_t *buffer)
{
#if defined(OF_WINDOWS)
	return _wstat64([path UTF16String], buffer);
#elif defined(OF_HAVE_OFF64_T)
	return stat64([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return stat64([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
#else
	return stat([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return stat([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
#endif
}

int
of_lstat(OFString *path, of_stat_t *buffer)
{
#if defined(OF_WINDOWS)
	return _wstat64([path UTF16String], buffer);
#elif defined(HAVE_LSTAT)
# ifdef OF_HAVE_OFF64_T
	return lstat64([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return lstat64([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
# else
	return lstat([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return lstat([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
# endif
#else
# ifdef OF_HAVE_OFF64_T
	return stat64([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return stat64([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
# else
	return stat([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], buffer);
	return stat([path cStringWithEncoding: [OFLocalization encoding]],
	    buffer);
# endif
#endif
}

@implementation OFFileManager
+ (void)initialize
{
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192
179
180
181
182
183
184
185

186
187
188
189
190
191
192
193







-
+







	wchar_t *buffer = _wgetcwd(NULL, 0);
#endif

	@try {
#ifndef OF_WINDOWS
		ret = [OFString
		    stringWithCString: buffer
			     encoding: [OFSystemInfo native8BitEncoding]];
			     encoding: [OFLocalization encoding]];
#else
		ret = [OFString stringWithUTF16String: buffer];
#endif
	} @finally {
		free(buffer);
	}

259
260
261
262
263
264
265
266

267
268
269
270
271
272
273
260
261
262
263
264
265
266

267
268
269
270
271
272
273
274







-
+








- (void)createDirectoryAtPath: (OFString*)path
{
	if (path == nil)
		@throw [OFInvalidArgumentException exception];

#ifndef OF_WINDOWS
	if (mkdir([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]],
	if (mkdir([path cStringWithEncoding: [OFLocalization encoding]],
	    DIR_MODE) != 0)
#else
	if (_wmkdir([path UTF16String]) != 0)
#endif
		@throw [OFCreateDirectoryFailedException
		    exceptionWithPath: path
				errNo: errno];
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333







-
+







		@throw [OFInvalidArgumentException exception];

	files = [OFMutableArray array];

#ifndef OF_WINDOWS
	DIR *dir;

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL)
		@throw [OFOpenItemFailedException exceptionWithPath: path
							      errNo: errno];

# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
	[readdirMutex lock];
429
430
431
432
433
434
435
436

437
438
439
440
441
442
443
444
430
431
432
433
434
435
436

437

438
439
440
441
442
443
444







-
+
-








- (void)changeCurrentDirectoryPath: (OFString*)path
{
	if (path == nil)
		@throw [OFInvalidArgumentException exception];

#ifndef OF_WINDOWS
	if (chdir([path cStringWithEncoding:
	if (chdir([path cStringWithEncoding: [OFLocalization encoding]]) != 0)
	    [OFSystemInfo native8BitEncoding]]) != 0)
#else
	if (_wchdir([path UTF16String]) != 0)
#endif
		@throw [OFChangeCurrentDirectoryPathFailedException
		    exceptionWithPath: path
				errNo: errno];
}
520
521
522
523
524
525
526
527

528
529
530
531
532
533
534
520
521
522
523
524
525
526

527
528
529
530
531
532
533
534







-
+







- (void)changePermissionsOfItemAtPath: (OFString*)path
			  permissions: (mode_t)permissions
{
	if (path == nil)
		@throw [OFInvalidArgumentException exception];

# ifndef OF_WINDOWS
	if (chmod([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]],
	if (chmod([path cStringWithEncoding: [OFLocalization encoding]],
	    permissions) != 0)
# else
	if (_wchmod([path UTF16String], permissions) != 0)
# endif
		@throw [OFChangePermissionsFailedException
		    exceptionWithPath: path
			  permissions: permissions
550
551
552
553
554
555
556
557

558
559
560
561
562
563
564
565
550
551
552
553
554
555
556

557

558
559
560
561
562
563
564







-
+
-







		@throw [OFStatItemFailedException exceptionWithPath: path
							      errNo: errno];

# ifdef OF_HAVE_THREADS
	[passwdMutex lock];
	@try {
# endif
		of_string_encoding_t encoding =
		of_string_encoding_t encoding = [OFLocalization encoding];
		    [OFSystemInfo native8BitEncoding];

		if (owner != NULL) {
			struct passwd *passwd = getpwuid(s.st_uid);

			*owner = [OFString stringWithCString: passwd->pw_name
						    encoding: encoding];
		}
584
585
586
587
588
589
590
591

592
593
594
595
596
597
598
583
584
585
586
587
588
589

590
591
592
593
594
595
596
597







-
+







	uid_t uid = -1;
	gid_t gid = -1;
	of_string_encoding_t encoding;

	if (path == nil || (owner == nil && group == nil))
		@throw [OFInvalidArgumentException exception];

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

# ifdef OF_HAVE_THREADS
	[passwdMutex lock];
	@try {
# endif
		if (owner != nil) {
			struct passwd *passwd;
796
797
798
799
800
801
802
803

804
805
806
807
808
809
810
795
796
797
798
799
800
801

802
803
804
805
806
807
808
809







-
+







	if (of_lstat(destination, &s) == 0)
		@throw [OFCopyItemFailedException
		    exceptionWithSourcePath: source
			    destinationPath: destination
				      errNo: EEXIST];

#ifndef OF_WINDOWS
	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	if (rename([source cStringWithEncoding: encoding],
	    [destination cStringWithEncoding: encoding]) != 0) {
#else
	if (_wrename([source UTF16String], [destination UTF16String]) != 0) {
#endif
		if (errno != EXDEV)
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
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







-
+









-
+







			    [path stringByAppendingPathComponent: item]];

			objc_autoreleasePoolPop(pool2);
		}

#ifndef OF_WINDOWS
		if (rmdir([path cStringWithEncoding:
		    [OFSystemInfo native8BitEncoding]]) != 0)
		    [OFLocalization encoding]]) != 0)
#else
		if (_wrmdir([path UTF16String]) != 0)
#endif
			@throw [OFRemoveItemFailedException
				exceptionWithPath: path
					    errNo: errno];
	} else {
#ifndef OF_WINDOWS
		if (unlink([path cStringWithEncoding:
		    [OFSystemInfo native8BitEncoding]]) != 0)
		    [OFLocalization encoding]]) != 0)
#else
		if (_wunlink([path UTF16String]) != 0)
#endif
			@throw [OFRemoveItemFailedException
			    exceptionWithPath: path
					errNo: errno];
	}
913
914
915
916
917
918
919
920

921
922
923
924
925
926
927
912
913
914
915
916
917
918

919
920
921
922
923
924
925
926







-
+







	void *pool;
	of_string_encoding_t encoding;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	if (link([source cStringWithEncoding: encoding],
	    [destination cStringWithEncoding: encoding]) != 0)
		@throw [OFLinkFailedException
		    exceptionWithSourcePath: source
			    destinationPath: destination
				      errNo: errno];
956
957
958
959
960
961
962
963

964
965
966
967
968
969
970
955
956
957
958
959
960
961

962
963
964
965
966
967
968
969







-
+







	void *pool;
	of_string_encoding_t encoding;

	if (source == nil || destination == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	if (symlink([source cStringWithEncoding: encoding],
	    [destination cStringWithEncoding: encoding]) != 0)
		@throw [OFCreateSymbolicLinkFailedException
		    exceptionWithSourcePath: source
			    destinationPath: destination
				      errNo: errno];
1002
1003
1004
1005
1006
1007
1008
1009

1010
1011
1012
1013
1014
1015
1016
1001
1002
1003
1004
1005
1006
1007

1008
1009
1010
1011
1012
1013
1014
1015







-
+







	char destination[PATH_MAX];
	ssize_t length;
	of_string_encoding_t encoding;

	if (path == nil)
		@throw [OFInvalidArgumentException exception];

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];
	length = readlink([path cStringWithEncoding: encoding],
	    destination, PATH_MAX);

	if (length < 0)
		@throw [OFStatItemFailedException exceptionWithPath: path
							      errNo: errno];

Added src/OFLocalization.h version [1add140a90].



















































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFObject.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

#define OF_LOCALIZED(ID, ...)				\
	[[OFLocalization sharedLocalization]		\
	    localizedStringForID: ID			\
			fallback: __VA_ARGS__, nil]

/*!
 * @class OFLocalization OFLocalization.h ObjFW/OFLocalization.h
 *
 * @brief A class for querying the locale and retrieving localized strings.
 */
@interface OFLocalization: OFObject
{
	OFString *_language;
	OFString *_territory;
	of_string_encoding_t _encoding;
	OFString *_decimalPoint;
}

/**
 * The language of the locale.
 *
 * If the language is unknown, it is `nil`.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy) OFString *language;

/*!
 * The territory of the locale.
 *
 * If the territory is unknown, it is `nil`.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy) OFString *territory;

/*!
 * The native 8-bit string encoding for the locale.
 *
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 */
@property (readonly) of_string_encoding_t encoding;

/*!
 * The decimal point of the system's locale.
 */
@property (readonly, copy) OFString *decimalPoint;

/*!
 * @brief Returns the shared OFLocalization instance.
 *
 * @warning If you don't use @ref OFApplication, this might be `nil`! In this
 *	    case, you need to manually allocate an instance and call
 *	    @ref initWithLocale: once, passing the locale used (as would be
 *	    returned by `setlocale()`).
 *
 * @return The shared OFLocalization instance
 */
+ (instancetype)sharedLocalization;

/**
 * @brief Returns the language of the locale.
 *
 * If the language is unknown, `nil` is returned.
 *
 * @return The language of the locale.
 */
+ (nullable OFString*)language;

/*!
 * @brief Returns the territory of the locale.
 *
 * If the territory is unknown, `nil` is returned.
 *
 * @return The territory of the locale.
 */
+ (nullable OFString*)territory;

/*!
 * @brief Returns the native 8-bit string encoding for the locale.
 *
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 *
 * @return The native 8-bit string encoding for the locale
 */
+ (of_string_encoding_t)encoding;

/*!
 * @brief Returns the decimal point of the system's locale.
 *
 * @return The decimal point of the system's locale
 */
+ (OFString*)decimalPoint;

/*!
 * @brief Initializes the OFLocalization singleton with the specified locale.
 *
 * @warning You should never call this yourself, except if you do not use
 *	    @ref OFApplication. In this case, you need to allocate exactly one
 *	    instance of OFLocalization, which will be come the singleton, and
 *	    call this method.
 *
 * @param locale The locale used, as returned from `setlocale()`
 */
- initWithLocale: (char*)locale;

/*!
 * @brief Returns the localized string for the specified ID, using the fallback
 *	  string if it cannot be looked up or is missing.
 *
 * @note This takes a variadic argument, terminated by `nil`, that consists of
 *	 pairs of variable names and variable values, which will be replaced
 *	 inside the localized string. For example, you can pass
 *	 `@"name", @"foo", nil`, causing `%[name]` to be replaced with `foo` in
 *	 the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 care of the `nil` sentinel automatically.
 *
 * @param ID The ID for the localized string
 * @param fallback The fallback to use in case the localized string cannot be
 *		   looked up or is missing
 * @return The localized string
 */
- (OFString*)localizedStringForID: (OFConstantString*)ID
			 fallback: (OFConstantString*)fallback, ... OF_SENTINEL;
/**
 * @brief Returns the localized string for the specified ID, using the fallback
 *	  string if it cannot be looked up or is missing.
 *
 * @note This takes a variadic argument, terminated by `nil` and passed as
 *	 va_list, that consists of pairs of variable names and variable values,
 *	 which will be replaced inside the localized string. For example, you
 *	 can pass `@"name", @"foo", nil`, causing `%[name]` to be replaced with
 *	 `foo` in the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 care of the `nil` sentinel automatically.
 *
 * @param ID The ID for the localized string
 * @param fallback The fallback to use in case the localized string cannot be
 *		   looked up or is missing
 * @param arguments A va_list of arguments, consisting of pairs of variable
 *		    names and values to replace in the localized string,
 *		    terminated with `nil`
 * @return The localized string
 */
- (OFString*)localizedStringForID: (OFConstantString*)ID
			 fallback: (OFConstantString*)fallback
			arguments: (va_list)arguments;
@end

OF_ASSUME_NONNULL_END

Added src/OFLocalization.m version [fbc5149bde].






































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <locale.h>

#import "OFLocalization.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

static OFLocalization *sharedLocalization = nil;

@implementation OFLocalization
@synthesize language = _language, territory = _territory, encoding = _encoding;
@synthesize decimalPoint = _decimalPoint;

+ (instancetype)sharedLocalization
{
	return sharedLocalization;
}

+ (OFString*)language
{
	return [sharedLocalization language];
}

+ (OFString*)territory
{
	return [sharedLocalization territory];
}

+ (of_string_encoding_t)encoding
{
	return [sharedLocalization encoding];
}

+ (OFString*)decimalPoint
{
	return [sharedLocalization decimalPoint];
}

- initWithLocale: (char*)locale
{
	self = [super init];

	if (locale == NULL) {
		_encoding = OF_STRING_ENCODING_UTF_8;
		_decimalPoint = @".";
		return self;
	}

	locale = of_strdup(locale);

	@try {
		char *tmp;

		/* We don't care for extras behind the @ */
		if ((tmp = strrchr(locale, '@')) != NULL)
			*tmp = '\0';

		/* Encoding */
		if ((tmp = strrchr(locale, '.')) != NULL) {
			size_t tmpLen;

			*tmp++ = '\0';

			tmpLen = strlen(tmp);
			for (size_t i = 0; i < tmpLen; i++)
				tmp[i] = of_ascii_tolower(tmp[i]);

			if (strcmp(tmp, "utf8") == 0 ||
			    strcmp(tmp, "utf-8") == 0)
				_encoding = OF_STRING_ENCODING_UTF_8;
			else if (strcmp(tmp, "ascii") == 0 ||
			    strcmp(tmp, "us-ascii") == 0)
				_encoding = OF_STRING_ENCODING_ASCII;
			else if (strcmp(tmp, "iso8859-1") == 0 ||
			    strcmp(tmp, "iso-8859-1") == 0)
				_encoding = OF_STRING_ENCODING_ISO_8859_1;
			else if (strcmp(tmp, "iso8859-15") == 0 ||
			    strcmp(tmp, "iso-8859-15") == 0)
				_encoding = OF_STRING_ENCODING_ISO_8859_15;
			/* Windows uses a codepage */
			else if (strcmp(tmp, "1252") == 0)
				_encoding = OF_STRING_ENCODING_WINDOWS_1252;
		}

		/* Territory */
		if ((tmp = strrchr(locale, '_')) != NULL) {
			*tmp++ = '\0';
			_territory = [[OFString alloc]
			    initWithCString: tmp
				   encoding: OF_STRING_ENCODING_ASCII];
		}

		_language = [[OFString alloc]
		    initWithCString: locale
			   encoding: OF_STRING_ENCODING_ASCII];

		_decimalPoint = [[OFString alloc]
		    initWithCString: localeconv()->decimal_point
			   encoding: _encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
		free(locale);
	}

	sharedLocalization = self;

	return self;
}

- (OFString*)localizedStringForID: (OFConstantString*)ID
			 fallback: (OFConstantString*)fallback, ...
{
	OFString *ret;
	va_list args;

	va_start(args, fallback);
	ret = [self localizedStringForID: ID
				fallback: fallback
			       arguments: args];
	va_end(args);

	return ret;
}

- (OFString*)localizedStringForID: (OFConstantString*)ID
			 fallback: (OFConstantString*)fallback
			arguments: (va_list)arguments
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = [fallback UTF8String];
	size_t UTF8StringLength = [fallback UTF8StringLength];
	size_t last = 0;
	int state = 0;

	for (size_t i = 0; i < UTF8StringLength; i++) {
		switch (state) {
		case 0:
			if (UTF8String[i] == '%') {
				[ret appendUTF8String: UTF8String + last
					       length: i - last];

				last = i + 1;
				state = 1;
			}
			break;
		case 1:
			if (UTF8String[i] == '[') {
				last = i + 1;
				state = 2;
			} else {
				[ret appendString: @"%"];
				state = 0;
			}
			break;
		case 2:
			if (UTF8String[i] == ']') {
				va_list argsCopy;
				OFConstantString *name;

				OFString *var = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				/*
				 * We loop, as most of the time, we only have
				 * one or maybe two variables, meaning looping
				 * is faster than constructing a dictionary.
				 */
				va_copy(argsCopy, arguments);
				while ((name = va_arg(argsCopy,
				    OFConstantString*)) != nil) {
					id value = va_arg(argsCopy, id);

					if (value == nil)
						@throw
						    [OFInvalidArgumentException
						    exception];

					if ([name isEqual: var]) {
						[ret appendString:
						    [value description]];
						break;
					}
				}

				last = i + 1;
				state = 0;
			}
			break;
		}
	}
	switch (state) {
	case 1:
		[ret appendString: @"%"];
		/* Explicit fall-through */
	case 0:
		[ret appendUTF8String: UTF8String + last
			       length: UTF8StringLength - last];
		break;
	}

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}
@end

Modified src/OFObject.m from [f0a708441f] to [f695ac4fbd].

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36







-
+








#include <assert.h>

#include <sys/time.h>

#import "OFObject.h"
#import "OFArray.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"
#import "OFTimer.h"
#import "OFRunLoop.h"
#import "OFThread.h"

#import "OFAllocFailedException.h"
#import "OFEnumerationMutationException.h"
#import "OFInitializationFailedException.h"
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114







-
+








#if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__)
static void
uncaughtExceptionHandler(id exception)
{
	OFString *description = [exception description];
	OFArray *backtrace = nil;
	of_string_encoding_t encoding = [OFSystemInfo native8BitEncoding];
	of_string_encoding_t encoding = [OFLocalization encoding];

	fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n",
	    [description cStringWithEncoding: encoding]);

	if ([exception respondsToSelector: @selector(backtrace)])
		backtrace = [exception backtrace];

196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210







-
+







	return instance;
}

const char*
_NSPrintForDebugger(id object)
{
	return [[object description]
	    cStringWithEncoding: [OFSystemInfo native8BitEncoding]];
	    cStringWithEncoding: [OFLocalization encoding]];
}

/* References for static linking */
void
_references_to_categories_of_OFObject(void)
{
	_OFObject_KeyValueCoding_reference = 1;

Modified src/OFPlugin.m from [50477690f4] to [58ad217163].

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







-
+









-
-
+
+








#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFPlugin.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#import "OFInitializationFailedException.h"

typedef OFPlugin* (*init_plugin_t)(void);

of_plugin_handle_t
of_dlopen(OFString *path, int flags)
{
#ifndef OF_WINDOWS
	return dlopen([path cStringWithEncoding:
	    [OFSystemInfo native8BitEncoding]], flags);
	return dlopen([path cStringWithEncoding: [OFLocalization encoding]],
	    flags);
#else
	if (path == nil)
		return GetModuleHandle(NULL);

	return LoadLibraryW([path UTF16String]);
#endif
}

Modified src/OFProcess.m from [273ab6b21b] to [b11627a172].

37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51







-
+







#endif

#import "OFProcess.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFDataArray.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#import "OFInitializationFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#ifdef OF_WINDOWS
145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
145
146
147
148
149
150
151

152

153
154
155
156
157
158
159







-
+
-







		const char *path;
		char **argv;

		if (pipe(_readPipe) != 0 || pipe(_writePipe) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: [self class]];

		path = [program cStringWithEncoding:
		path = [program cStringWithEncoding: [OFLocalization encoding]];
		    [OFSystemInfo native8BitEncoding]];
		[self OF_getArgV: &argv
		  forProgramName: programName
		    andArguments: arguments];

		@try {
			char **env = [self
			    OF_environmentForDictionary: environment];
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
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







-
+




















-
+







	OFString *const *objects = [arguments objects];
	size_t i, count = [arguments count];
	of_string_encoding_t encoding;

	*argv = [self allocMemoryWithSize: sizeof(char*)
				    count: count + 2];

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	(*argv)[0] = (char*)[programName cStringWithEncoding: encoding];

	for (i = 0; i < count; i++)
		(*argv)[i + 1] =
		    (char*)[objects[i] cStringWithEncoding: encoding];

	(*argv)[i + 1] = NULL;
}

- (char**)OF_environmentForDictionary: (OFDictionary*)environment
{
	OFEnumerator *keyEnumerator, *objectEnumerator;
	char **envp;
	size_t i, count;
	of_string_encoding_t encoding;

	if (environment == nil)
		return NULL;

	encoding = [OFSystemInfo native8BitEncoding];
	encoding = [OFLocalization encoding];

	count = [environment count];
	envp = [self allocMemoryWithSize: sizeof(char*)
				   count: count + 1];

	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];

Modified src/OFString.h from [898af43561] to [0fabfb3f71].

44
45
46
47
48
49
50




51
52
53
54
55
56
57
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61







+
+
+
+







#endif
typedef of_char32_t of_unichar_t;

/*!
 * @brief The encoding of a string.
 */
typedef enum of_string_encoding_t {
	/*
	 * UTF-8 *has* to be 0, so that if the @ref OFLocalization singleton is
	 * `nil`, `[OFLocalization encoding]` returns UTF-8.
	 */
	/*! UTF-8 */
	OF_STRING_ENCODING_UTF_8,
	/*! ASCII */
	OF_STRING_ENCODING_ASCII,
	/*! ISO 8859-1 */
	OF_STRING_ENCODING_ISO_8859_1,
	/*! ISO 8859-15 */

Modified src/OFString.m from [011cdaca0e] to [451da8b217].

33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47







-
+








#import "OFString.h"
#import "OFString_UTF8.h"
#import "OFString_UTF8+Private.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFDataArray.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFURL.h"
#ifdef OF_HAVE_SOCKETS
# import "OFHTTPClient.h"
# import "OFHTTPRequest.h"
2357
2358
2359
2360
2361
2362
2363
2364

2365
2366
2367
2368
2369
2370
2371
2357
2358
2359
2360
2361
2362
2363

2364
2365
2366
2367
2368
2369
2370
2371







-
+







#ifdef HAVE_STRTOF_L
	const char *UTF8String = [self UTF8String];
#else
	/*
	 * If we have no strtof_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFSystemInfo decimalPoint];
	OFString *decimalPoint = [OFLocalization decimalPoint];
	const char *UTF8String = [[self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint] UTF8String];
#endif
	char *endPointer = NULL;
	float value;

2398
2399
2400
2401
2402
2403
2404
2405

2406
2407
2408
2409
2410
2411
2412
2398
2399
2400
2401
2402
2403
2404

2405
2406
2407
2408
2409
2410
2411
2412







-
+







#ifdef HAVE_STRTOD_L
	const char *UTF8String = [self UTF8String];
#else
	/*
	 * If we have no strtod_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFSystemInfo decimalPoint];
	OFString *decimalPoint = [OFLocalization decimalPoint];
	const char *UTF8String = [[self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint] UTF8String];
#endif
	char *endPointer = NULL;
	double value;

Modified src/OFSystemInfo.h from [c797094656] to [cbabc25539].

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
37
38
39
40
41
42
43





































44
45
46
47
48
49
50







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







 *
 * A CPU with multiple cores counts as multiple CPUs.
 *
 * @return The number of CPUs installed in the system
 */
+ (size_t)numberOfCPUs;

/*!
 * @brief Returns the native 8-bit string encoding of the operating system.
 *
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * If the native 8-bit encoding is unknown, UTF-8 is assumed.
 *
 * @return The native 8-bit string encoding of the operating system
 */
+ (of_string_encoding_t)native8BitEncoding;

/*!
 * @brief Returns the language of the locale.
 *
 * If the language is unknown, nil is returned.
 *
 * @return The language of the locale.
 */
+ (OFString*)language;

/*!
 * @brief Returns the territory of the locale.
 *
 * If the territory is unknown, nil is returned.
 *
 * @return The territory of the locale.
 */
+ (OFString*)territory;

/*!
 * @brief Returns the decimal point in the system's locale.
 *
 * @return The decimal point in the system's locale
 */
+ (OFString*)decimalPoint;

/*!
 * @brief Returns the path where user data for the application can be stored.
 *
 * On Unix systems, this adheres to the XDG Base Directory specification.@n
 * On Mac OS X and iOS, it uses the `NSApplicationSupportDirectory` directory.@n
 * On Windows, it uses the `APPDATA` environment variable.@n
 * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.

Modified src/OFSystemInfo.m from [70cccb5f86] to [f489c81282].

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
20
21
22
23
24
25
26

27
28
29
30
31
32
33







-








/* Work around __block being used by glibc */
#include <stdlib.h>	/* include any libc header to get the libc defines */
#ifdef __GLIBC__
# undef __USE_XOPEN
#endif

#include <locale.h>
#include <unistd.h>

#include "platform.h"

#ifdef OF_MAC_OS_X
# include <sys/sysctl.h>
#endif
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
61
62
63
64
65
66
67





































































68
69
70
71
72
73
74







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







struct x86_regs {
	uint32_t eax, ebx, ecx, edx;
};
#endif

static size_t pageSize;
static size_t numberOfCPUs;
static of_string_encoding_t native8BitEncoding = OF_STRING_ENCODING_UTF_8;
static OFString *language = nil;
static OFString *territory = nil;
static OFString *decimalPoint = @".";

void
of_system_info_parse_locale(char *locale)
{
	if (locale == NULL)
		return;

	locale = of_strdup(locale);

	@try {
		char *tmp;

		/* We don't care for extras behind the @ */
		if ((tmp = strrchr(locale, '@')) != NULL)
			*tmp = '\0';

		/* Encoding */
		if ((tmp = strrchr(locale, '.')) != NULL) {
			size_t tmpLen;

			*tmp++ = '\0';

			tmpLen = strlen(tmp);
			for (size_t i = 0; i < tmpLen; i++)
				tmp[i] = of_ascii_tolower(tmp[i]);

			if (strcmp(tmp, "utf8") == 0 ||
			    strcmp(tmp, "utf-8") == 0)
				native8BitEncoding = OF_STRING_ENCODING_UTF_8;
			else if (strcmp(tmp, "ascii") == 0 ||
			    strcmp(tmp, "us-ascii") == 0)
				native8BitEncoding = OF_STRING_ENCODING_ASCII;
			else if (strcmp(tmp, "iso8859-1") == 0 ||
			    strcmp(tmp, "iso-8859-1") == 0)
				native8BitEncoding =
				    OF_STRING_ENCODING_ISO_8859_1;
			else if (strcmp(tmp, "iso8859-15") == 0 ||
			    strcmp(tmp, "iso-8859-15") == 0)
				native8BitEncoding =
				    OF_STRING_ENCODING_ISO_8859_15;
			/* Windows uses a codepage */
			else if (strcmp(tmp, "1252") == 0)
				native8BitEncoding =
				    OF_STRING_ENCODING_WINDOWS_1252;
		}

		/* Territory */
		if ((tmp = strrchr(locale, '_')) != NULL) {
			*tmp++ = '\0';
			territory = [[OFString alloc]
			    initWithCString: tmp
				   encoding: OF_STRING_ENCODING_ASCII];
		}

		language = [[OFString alloc]
		    initWithCString: locale
			   encoding: OF_STRING_ENCODING_ASCII];
	} @finally {
		free(locale);
	}

	decimalPoint = [[OFString alloc]
	    initWithCString: localeconv()->decimal_point
		   encoding: native8BitEncoding];
}

#if defined(OF_X86_64) || defined(OF_X86)
static OF_INLINE struct x86_regs OF_CONST_FUNC
x86_cpuid(uint32_t eax, uint32_t ecx)
{
	struct x86_regs regs;

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
139
140
141
142
143
144
145




















146
147
148
149
150
151
152







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







}

+ (size_t)numberOfCPUs
{
	return numberOfCPUs;
}

+ (of_string_encoding_t)native8BitEncoding
{
	return native8BitEncoding;
}

+ (OFString*)language
{
	return language;
}

+ (OFString*)territory
{
	return territory;
}

+ (OFString*)decimalPoint
{
	return decimalPoint;
}

+ (OFString*)userDataPath
{
#if defined(OF_MAC_OS_X) || defined(OF_IOS)
	void *pool = objc_autoreleasePoolPush();
	char pathC[PATH_MAX];
	OFMutableString *path;
	OFString *home;

Modified src/ObjFW.h from [9d37e8cfea] to [b467e259b3].

96
97
98
99
100
101
102
103
104


105
106
107
108
109
110
111
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112







-

+
+







#import "OFXMLProcessingInstructions.h"
#import "OFXMLParser.h"
#import "OFXMLElementBuilder.h"

#import "OFMessagePackExtension.h"

#import "OFApplication.h"
#import "OFOptionsParser.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"
#import "OFOptionsParser.h"
#import "OFTimer.h"
#import "OFRunLoop.h"

#import "OFAllocFailedException.h"
#import "OFException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFAcceptFailedException.h"

Modified src/exceptions/OFException.m from [a913b4b6a3] to [9a85916e30].

24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38







-
+







#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFException.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#import "OFInitializationFailedException.h"
#import "OFLockFailedException.h"
#import "OFUnlockFailedException.h"

#if !defined(HAVE_STRERROR_R) && defined(OF_HAVE_THREADS)
# import "threading.h"
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
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







-
+









-
+







#endif

#ifdef HAVE_STRERROR_R
	if (strerror_r(errNo, buffer, 256) != 0)
		return @"Unknown error (strerror_r failed)";

	ret = [OFString stringWithCString: buffer
				 encoding: [OFSystemInfo native8BitEncoding]];
				 encoding: [OFLocalization encoding]];
#else
# ifdef OF_HAVE_THREADS
	if (!of_mutex_lock(&mutex))
		@throw [OFLockFailedException exception];

	@try {
# endif
		ret = [OFString
		    stringWithCString: strerror(errNo)
			     encoding: [OFSystemInfo native8BitEncoding]];
			     encoding: [OFLocalization encoding]];
# ifdef OF_HAVE_THREADS
	} @finally {
		if (!of_mutex_unlock(&mutex))
			@throw [OFUnlockFailedException exception];
	}
# endif
#endif

Modified src/of_asprintf.m from [78991846e2] to [5de6ff2576].

29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43







-
+







#ifdef HAVE_XLOCALE_H
# include <xlocale.h>
#endif

#include <sys/types.h>

#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFLocalization.h"

#import "OFInitializationFailedException.h"

#define MAX_SUBFORMAT_LEN 64

#ifndef HAVE_ASPRINTF
/*
558
559
560
561
562
563
564
565

566
567
568
569
570
571
572
558
559
560
561
562
563
564

565
566
567
568
569
570
571
572







-
+







			char *tmp2;

			@try {
				OFMutableString *tmpStr = [OFMutableString
				    stringWithUTF8String: tmp
						  length: tmpLen];
				OFString *decimalPoint =
				    [OFSystemInfo decimalPoint];
				    [OFLocalization decimalPoint];
				[tmpStr replaceOccurrencesOfString: decimalPoint
							withString: @"."];
				if ([tmpStr UTF8StringLength] > INT_MAX)
					return false;
				tmpLen = (int)[tmpStr UTF8StringLength];
				tmp2 = malloc(tmpLen);
				memcpy(tmp2, [tmpStr UTF8String], tmpLen);

Modified utils/ofhttp/OFHTTP.m from [3ef2bcb62b] to [6d271e2d00].

26
27
28
29
30
31
32

33
34
35
36
37
38
39
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40







+







#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFOptionsParser.h"
#import "OFStdIOStream.h"
#import "OFSystemInfo.h"
#import "OFTCPSocket.h"
#import "OFURL.h"
#import "OFLocalization.h"

#import "OFAddressTranslationFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFOpenItemFailedException.h"
68
69
70
71
72
73
74
75
76
77




78
79
80

81
82
83
84
85
86
87
69
70
71
72
73
74
75



76
77
78
79
80
81

82
83
84
85
86
87
88
89







-
-
-
+
+
+
+


-
+







@end

OF_APPLICATION_DELEGATE(OFHTTP)

static void
help(OFStream *stream, bool full, int status)
{
	[of_stderr writeFormat:
	    @"Usage: %@ -[cehHmoOPqv] url1 [url2 ...]\n",
	    [OFApplication programName]];
	[of_stderr writeString:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]\n",
	    @"prog", [OFApplication programName])];

	if (full)
		[stream writeString:
		[stream writeString: OF_LOCALIZED(@"full_usage",
		    @"\nOptions:\n    "
		    @"-b  --body           "
		    @"  Specify the file to send as body\n    "
		    @"-c  --continue       "
		    @"  Continue download of existing file\n    "
		    @"-f  --force          "
		    @"  Force / overwrite existing file\n    "
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112







-
+







		    @"-O  --detect-filename"
		    @"  Do a HEAD request to detect the file name\n    "
		    @"-P  --proxy          "
		    @"  Specify SOCKS5 proxy\n    "
		    @"-q  --quiet          "
		    @"  Quiet mode (no output, except errors)\n    "
		    @"-v  --verbose        "
		    @"  Verbose mode (print headers)\n"];
		    @"  Verbose mode (print headers)\n")];

	[OFApplication terminateWithStatus: status];
}

@implementation OFHTTP
- init
{
131
132
133
134
135
136
137
138
139
140



141
142
143
144
145
146
147
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147
148
149







-
-
-
+
+
+








- (void)addHeader: (OFString*)header
{
	size_t pos = [header rangeOfString: @":"].location;
	OFString *name, *value;

	if (pos == OF_NOT_FOUND) {
		[of_stderr writeFormat: @"%@: Headers must to be in format "
					"name:value!\n",
					[OFApplication programName]];
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_header",
		    @"%[prog]: Headers must to be in format name:value!\n",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	name = [header substringWithRange: of_range(0, pos)];
	name = [name stringByDeletingEnclosingWhitespaces];

	value = [header substringWithRange:
181
182
183
184
185
186
187

188
189
190



191
192
193
194
195
196
197
183
184
185
186
187
188
189
190



191
192
193
194
195
196
197
198
199
200







+
-
-
-
+
+
+







	else if ([method isEqual: @"PUT"])
		_method = OF_HTTP_REQUEST_METHOD_PUT;
	else if ([method isEqual: @"DELETE"])
		_method = OF_HTTP_REQUEST_METHOD_DELETE;
	else if ([method isEqual: @"TRACE"])
		_method = OF_HTTP_REQUEST_METHOD_TRACE;
	else {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_method",
		[of_stderr writeFormat: @"%@: Invalid request method %@!\n",
					[OFApplication programName],
					method];
		    @"%[prog]: Invalid request method %[method]!\n",
		    @"prog", [OFApplication programName],
		    @"method", method)];
		[OFApplication terminateWithStatus: 1];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setProxy: (OFString*)proxy
212
213
214
215
216
217
218
219
220
221



222
223
224
225
226
227
228
215
216
217
218
219
220
221



222
223
224
225
226
227
228
229
230
231







-
-
-
+
+
+








		if (port > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		[OFTCPSocket setSOCKS5Host: host];
		[OFTCPSocket setSOCKS5Port: (uint16_t)port];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeFormat: @"%@: Proxy must to be in format "
					"host:port!\n",
					[OFApplication programName]];
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_proxy",
		    @"%[prog]: Proxy must to be in format host:port!\n",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}
}

- (void)applicationDidFinishLaunching
{
	OFString *outputPath;
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
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







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




-
-
-
-
+
+
+
+
+





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













+
-
-
-
+
+
+




-
-
-
-
+
+
+
+
+







			[self setMethod: [optionsParser argument]];
			break;
		case 'P':
			[self setProxy: [optionsParser argument]];
			break;
		case ':':
			if ([optionsParser lastLongOption] != nil)
				[of_stderr writeFormat:
				    @"%@: Argument for option --%@ missing\n",
				    [OFApplication programName],
				    [optionsParser lastLongOption]];
			else
				[of_stderr writeFormat:
				    @"%@: Argument for option -%C missing\n",
				    [OFApplication programName],
				    [optionsParser lastOption]];
				[of_stderr writeString:
				    OF_LOCALIZED(@"long_argument_missing",
				    @"%[prog]: Argument for option --%[opt] "
				    "missing\n"
				    @"prog", [OFApplication programName],
				    @"opt", [optionsParser lastLongOption])];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    [optionsParser lastOption]];
				[of_stderr writeString:
				    OF_LOCALIZED(@"argument_missing",
				    @"%[prog]: Argument for option -%[opt] "
				    "missing\n",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '=':
			[of_stderr writeFormat: @"%@: Option --%@ takes no "
						@"argument\n",
						[OFApplication programName],
						[optionsParser lastLongOption]];
			[of_stderr writeString:
			    OF_LOCALIZED(@"takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument\n",
			    @"prog", [OFApplication programName],
			    @"opt", [optionsParser lastLongOption])];

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if ([optionsParser lastLongOption] != nil)
				[of_stderr writeFormat:
				    @"%@: Unknown option: --%@\n",
				    [OFApplication programName],
				    [optionsParser lastLongOption]];
			else
				[of_stderr writeFormat:
				    @"%@: Unknown option: -%C\n",
				    [OFApplication programName],
				    [optionsParser lastOption]];
				[of_stderr writeString:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]\n",
				    @"prog", [OFApplication programName],
				    @"opt", [optionsParser lastLongOption])];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    [optionsParser lastOption]];
				[of_stderr writeString:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]\n",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	_outputPath = [outputPath copy];
	_URLs = [[optionsParser remainingArguments] retain];

	if ([_URLs count] < 1)
		help(of_stderr, false, 1);

	if (_quiet && _verbose) {
		[of_stderr writeString: OF_LOCALIZED(@"quiet_xor_verbose",
		[of_stderr writeFormat: @"%@: -q / --quiet and -v / --verbose "
					@"are mutually exclusive!\n",
					[OFApplication programName]];
		    @"%[prog]: -q / --quiet and -v / --verbose are mutually "
		    @"exclusive!\n",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && [_URLs count] > 1) {
		[of_stderr writeFormat: @"%@: Cannot use -o / --output when "
					@"more than one URL has been "
					@"specified!\n",
					[OFApplication programName]];
		[of_stderr writeString:
		    OF_LOCALIZED(@"output_only_with_one_url",
		    @"%[prog]: Cannot use -o / --output when more than one URL "
		    @"has been specified!\n",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

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







-
-
-
-
+
+
+
+
+
+
+




-
-
-
-
+
+
+
+
+
+
+




-
-
-
-
+
+
+
+
+
+




+
-
-
-
-
-
+
+
+
+
+

+
+
-
+





+
+
-
+

+
+
-
+

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





+
-
-
-
+
+
+








	@try {
		response = [_HTTPClient performRequest: request];
	} @catch (OFAddressTranslationFailedException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeFormat: @"%@: Failed to download <%@>!\n"
					@"  Address translation failed: %@\n",
					[OFApplication programName],
					[[request URL] string], e];
		[of_stderr writeString:
		    OF_LOCALIZED(@"download_failed_address_translation",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Address translation failed: %[exception]\n",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"exception", e)];
	} @catch (OFConnectionFailedException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeFormat: @"%@: Failed to download <%@>!\n"
					@"  Connection failed: %@\n",
					[OFApplication programName],
					[[request URL] string], e];
		[of_stderr writeString:
		    OF_LOCALIZED(@"download_failed_connection_failed",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Connection failed: %[exception]\n",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"exception", e)];
	} @catch (OFInvalidServerReplyException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeFormat: @"%@: Failed to download <%@>!\n"
					@"  Invalid server reply!\n",
					[OFApplication programName],
					[[request URL] string]];
		[of_stderr writeString:
		    OF_LOCALIZED(@"download_failed_invalid_server_reply",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Invalid server reply!\n",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string])];
	} @catch (OFUnsupportedProtocolException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeString: OF_LOCALIZED(@"no_ssl_library",
		[of_stderr writeFormat: @"%@: No SSL library loaded!\n"
					@"  In order to download via https, "
					@"you need to preload an SSL library "
					@"for ObjFW\n  such as ObjOpenSSL!\n",
					[OFApplication programName]];
		    @"%[prog]: No SSL library loaded!\n"
		    @"  In order to download via https, you need to preload an "
		    @"SSL library for ObjFW\n"
		    "such as ObjOpenSSL!\n",
		    @"prog", [OFApplication programName])];
	} @catch (OFReadOrWriteFailedException *e) {
		OFString *error = OF_LOCALIZED(
		    @"download_failed_read_or_write_failed_any",
		OFString *action = @"Read or write";
		    @"Read or write failed");

		if (!_quiet)
			[of_stdout writeString: @"\n"];

		if ([e isKindOfClass: [OFReadFailedException class]])
			error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_read",
			action = @"Read";
			    @"Read failed");
		else if ([e isKindOfClass: [OFWriteFailedException class]])
			error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_write",
			action = @"Write";
			    @"Write failed");

		[of_stderr writeFormat: @"%@: Failed to download <%@>!\n"
					@"  %@ failed: %@\n",
					[OFApplication programName],
					[[request URL] string], action, e];
		[of_stderr writeString:
		    OF_LOCALIZED(@"download_failed_read_or_write_failed",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[error]: %[exception]\n",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"error", error,
		    @"exception", e)];
	} @catch (OFHTTPRequestFailedException *e) {
		if (!_quiet)
			[of_stdout writeFormat: @" ➜ %d\n",
						[[e response] statusCode]];

		[of_stderr writeString: OF_LOCALIZED(@"download_failed",
		[of_stderr writeFormat: @"%@: Failed to download <%@>!\n",
					[OFApplication programName],
					[[request URL] string]];
		    @"%[prog]: Failed to download <%[url]>!\n",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string])];
	}

	if (!_quiet && response != nil)
		[of_stdout writeFormat: @" ➜ %d\n", [response statusCode]];

	return response;
}
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
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







-
-
+
+
+
+
+
+




















-
+
+







		[_progressBar release];
		_progressBar = nil;

		if (!_quiet)
			[of_stdout writeString: @"\n  Error!\n"];

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[of_stderr writeFormat: @"%@: Failed to download <%@>: %@\n",
					[OFApplication programName], URL, e];
		[of_stderr writeString:
		    OF_LOCALIZED(@"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>: %[exception]\n",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", e)];

		_errorCode = 1;
		goto next;
	}

	_received += length;

	[_output writeBuffer: buffer
		      length: length];

	[_progressBar setReceived: _received];

	if ([response isAtEndOfStream] ||
	    (_length >= 0 && _received >= _length)) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet)
			[of_stdout writeString: @"\n  Done!\n"];
			[of_stdout writeString:
			    OF_LOCALIZED(@"download_done", @"\n  Done!\n")];

		goto next;
	}

	return true;

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







+
-
-
-
+
+
+







+
-
-
-
+
+
+







	if (_URLIndex >= [_URLs count])
		[OFApplication terminateWithStatus: _errorCode];

	@try {
		URLString = [_URLs objectAtIndex: _URLIndex++];
		URL = [OFURL URLWithString: URLString];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_url",
		[of_stderr writeFormat: @"%@: Invalid URL: <%@>!\n",
					[OFApplication programName],
					URLString];
		    @"%[prog]: Invalid URL: <%[url]>!\n",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	if (![[URL scheme] isEqual: @"http"] &&
	    ![[URL scheme] isEqual: @"https"]) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_scheme",
		[of_stderr writeFormat: @"%@: Invalid scheme: <%@:>!\n",
					[OFApplication programName],
					URLString];
		    @"%[prog]: Invalid scheme: <%[scheme]:>!\n",
		    @"prog", [OFApplication programName],
		    @"scheme", URLString)];

		_errorCode = 1;
		goto next;
	}

	clientHeaders = [[_clientHeaders mutableCopy] autorelease];

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







-
+


-
+

-
+

+
+
+
-
+

-
+

+
+
+
-
+

-
+

+
+
+
-
+

-
+
+
+
+
+

-
-
+
+
-









+
+
+
+








-
-
+
+
+
+
+
+
+
+
+








-
-
-
+
+
+
+
+











-
-
-
+
+
+
+
+
+
+







	type = [headers objectForKey: @"Content-Type"];

	if (lengthString != nil)
		_length = [lengthString decimalValue];

	if (!_quiet) {
		if (type == nil)
			type = @"unknown";
			type = OF_LOCALIZED(@"type_unknown", @"unknown");

		if (_length >= 0) {
			if (_resumedFrom + _length >= GIBIBYTE)
			if (_resumedFrom + _length >= GIBIBYTE) {
				lengthString = [OFString stringWithFormat:
				    @"%,.2f GiB",
				    @"%,.2f",
				    (float)(_resumedFrom + _length) / GIBIBYTE];
				lengthString = OF_LOCALIZED(@"size_gib",
				    @"%[num] GiB",
				    @"num", lengthString);
			else if (_resumedFrom + _length >= MEBIBYTE)
			} else if (_resumedFrom + _length >= MEBIBYTE) {
				lengthString = [OFString stringWithFormat:
				    @"%,.2f MiB",
				    @"%,.2f",
				    (float)(_resumedFrom + _length) / MEBIBYTE];
				lengthString = OF_LOCALIZED(@"size_mib",
				    @"%[num] MiB",
				    @"num", lengthString);
			else if (_resumedFrom + _length >= KIBIBYTE)
			} else if (_resumedFrom + _length >= KIBIBYTE) {
				lengthString = [OFString stringWithFormat:
				    @"%,.2f KiB",
				    @"%,.2f",
				    (float)(_resumedFrom + _length) / KIBIBYTE];
				lengthString = OF_LOCALIZED(@"size_kib",
				    @"%[num] KiB",
				    @"num", lengthString);
			else
			} else {
				lengthString = [OFString stringWithFormat:
				    @"%jd bytes", _resumedFrom + _length];
				    @"%jd", _resumedFrom + _length];
				lengthString = OF_LOCALIZED(@"size_bytes",
				    @"%[num] bytes",
				    @"num", lengthString);
			}
		} else
			lengthString = @"unknown";

			lengthString =
			    OF_LOCALIZED(@"size_unknown", @"unknown");
		[of_stdout writeFormat: @"  Name: %@\n", fileName];

		if (_verbose) {
			void *pool = objc_autoreleasePoolPush();
			OFDictionary OF_GENERIC(OFString*, OFString*) *headers =
			    [response headers];
			OFEnumerator *keyEnumerator = [headers keyEnumerator];
			OFEnumerator *objectEnumerator =
			    [headers objectEnumerator];
			OFString *key, *object;

			[of_stdout writeString: OF_LOCALIZED(@"info_name_nopad",
			    @"  Name: %[name]\n",
			    @"name", fileName)];

			while ((key = [keyEnumerator nextObject]) != nil &&
			    (object = [objectEnumerator nextObject]) != nil)
				[of_stdout writeFormat: @"  %@: %@\n",
							key, object];

			objc_autoreleasePoolPop(pool);
		} else {
			[of_stdout writeFormat: @"  Type: %@\n", type];
			[of_stdout writeFormat: @"  Size: %@\n", lengthString];
			[of_stdout writeString: OF_LOCALIZED(@"info_name",
			    @"  Name: %[name]\n",
			    @"name", fileName)];
			[of_stdout writeString: OF_LOCALIZED(@"info_type",
			    @"  Type: %[type]\n",
			    @"type", type)];
			[of_stdout writeString: OF_LOCALIZED(@"info_size",
			    @"  Size: %[size]\n",
			    @"size", lengthString)];
		}
	}

	if ([_outputPath isEqual: @"-"])
		_output = of_stdout;
	else {
		if (!_continue && !_force &&
		    [fileManager fileExistsAtPath: fileName]) {
			[of_stderr writeFormat:
			    @"%@: File %@ already exists!\n",
			    [OFApplication programName], fileName];
			[of_stderr writeString:
			    OF_LOCALIZED(@"ouput_already_exists",
			    @"%[prog]: File %[filename] already exists!\n",
			    @"prog", [OFApplication programName],
			    @"filename", fileName)];

			_errorCode = 1;
			goto next;
		}

		@try {
			OFString *mode =
			    ([response statusCode] == 206 ? @"ab" : @"wb");
			_output = [[OFFile alloc] initWithPath: fileName
							  mode: mode];
		} @catch (OFOpenItemFailedException *e) {
			[of_stderr writeFormat:
			    @"%@: Failed to open file %@!\n",
			    [OFApplication programName], fileName];
			[of_stderr writeString:
			    OF_LOCALIZED(@"failed_to_open_output",
			    @"%[prog]: Failed to open file %[filename]: "
			    @"%[exception]\n",
			    @"prog", [OFApplication programName],
			    @"filename",fileName,
			    @"exception", e)];

			_errorCode = 1;
			goto next;
		}
	}

	if (!_quiet) {

Modified utils/ofhttp/ProgressBar.m from [b2906f802b] to [1f06b0db97].

17
18
19
20
21
22
23

24
25
26
27
28
29
30
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







+







#include "config.h"

#include <math.h>

#import "OFDate.h"
#import "OFStdIOStream.h"
#import "OFTimer.h"
#import "OFLocalization.h"

#import "ProgressBar.h"

#define GIBIBYTE (1024 * 1024 * 1024)
#define MEBIBYTE (1024 * 1024)
#define KIBIBYTE (1024)

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







-
-
-
+
+
+
+
+
+
+




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




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





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








		_BPS = (float)_received / (float)timeInterval;
		_ETA = timeInterval;
	}

	if (isinf(_ETA))
		[of_stdout writeString: @"--:--:-- "];
	else if (_ETA >= 99 * 3600)
		[of_stdout writeFormat: @"%,4.2f d ", _ETA / (24 * 3600)];
	else
	else if (_ETA >= 99 * 3600) {
		OFString *num = [OFString stringWithFormat:
		    @"%,4.2f", _ETA / (24 * 3600)];
		[of_stdout writeString: OF_LOCALIZED(@"eta_days",
		    @"%[num] d ",
		    @"num", num)];
	} else
		[of_stdout writeFormat: @"%2u:%02u:%02u ",
		    (uint8_t)(_ETA / 3600), (uint8_t)(_ETA / 60) % 60,
		    (uint8_t)_ETA % 60];

	if (_BPS >= GIBIBYTE)
		[of_stdout writeFormat: @"%,7.2f GiB/s", _BPS / GIBIBYTE];
	else if (_BPS >= MEBIBYTE)
		[of_stdout writeFormat: @"%,7.2f MiB/s", _BPS / MEBIBYTE];
	else if (_BPS >= KIBIBYTE)
		[of_stdout writeFormat: @"%,7.2f KiB/s", _BPS / KIBIBYTE];
	else
		[of_stdout writeFormat: @"%,7.2f B/s  ", _BPS];
	if (_BPS >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)_drawReceived
{
	if (_resumedFrom + _received >= GIBIBYTE)
		[of_stdout writeFormat:
	if (_resumedFrom + _received >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"\r  %,7.2f GiB ",
		    (float)(_resumedFrom + _received) / GIBIBYTE];
	else if (_resumedFrom + _received >= MEBIBYTE)
		[of_stdout writeFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gib",
		    @"\r  %[num] GiB ",
		    @"num", num)];
	} else if (_resumedFrom + _received >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"\r  %,7.2f MiB ",
		    (float)(_resumedFrom + _received) / MEBIBYTE];
	else if (_resumedFrom + _received >= KIBIBYTE)
		[of_stdout writeFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mib",
		    @"\r  %[num] MiB ",
		    @"num", num)];
	} else if (_resumedFrom + _received >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"\r  %,7.2f KiB ",
		    (float)(_resumedFrom + _received) / KIBIBYTE];
	else
		[of_stdout writeFormat:
		    @"\r  %jd bytes ", _resumedFrom + _received];
		    @"%,7.2f", (float)(_resumedFrom + _received) / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kib",
		    @"\r  %[num] KiB ",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%jd", _resumedFrom + _received];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bytes",
		    @"\r  %[num] bytes ",
		    @"num", num)];
	}

	if (_stopped)
		_BPS = (float)_received /
		    -(float)[_startDate timeIntervalSinceNow];

	if (_BPS >= GIBIBYTE)
		[of_stdout writeFormat: @"%,7.2f GiB/s", _BPS / GIBIBYTE];
	else if (_BPS >= MEBIBYTE)
		[of_stdout writeFormat: @"%,7.2f MiB/s", _BPS / MEBIBYTE];
	else if (_BPS >= KIBIBYTE)
		[of_stdout writeFormat: @"%,7.2f KiB/s", _BPS / KIBIBYTE];
	else
		[of_stdout writeFormat: @"%,7.2f B/s  ", _BPS];
	if (_BPS >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)draw
{
	if (_length > 0)
		[self _drawProgress];
	else