ObjFW  Check-in [6dff0f5922]

Overview
Comment:Always use "." for the decimal point

This is achieved by replacing the locale's decimal point with "." after
formatting and replacing "." with the locale's decimal point before
parsing.

To still use the decimal point from the locale for formatting, the new
flag "," is introduced to formats. This is useful for just printing a
string to the user that is not saved to a file or sent via a network.

While this is an ugly hack, there is no better way to do this other than
implementing the functionality of printf and strtod myself, as POSIX
does not specify versions of these that take a locale as an argument.
While this is a lot of work and error-prone, I will most likely end up
doing this eventually.

This commit also enables the locale in OFApplication to notice when
things break. As a nice side effect, ofhttp now uses the locale's
decimal point in its user interface.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6dff0f59220b3915dfe2e699b4278eaa20ae05ba2780621ef5f9409a54dec868
User & Date: js on 2017-01-07 02:34:39
Other Links: manifest | tags
Context
2017-01-07
03:26
Use strtof_l, strtod_l and asprintf_l if available check-in: 0ad678f125 user: js tags: trunk
02:34
Always use "." for the decimal point check-in: 6dff0f5922 user: js tags: trunk
00:37
Add of_ascii_{to{upper,lower},is{alpha,alnum}} check-in: d9eb7b50b3 user: js tags: trunk
Changes

Modified src/OFApplication.m from [e942c20c56] to [669a08ac5c].

16
17
18
19
20
21
22

23
24
25
26
27
28
29

#include "config.h"

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


#include <signal.h>

#import "OFApplication.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFSystemInfo.h"







>







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

#include "config.h"

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

#include <locale.h>
#include <signal.h>

#import "OFApplication.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFSystemInfo.h"
96
97
98
99
100
101
102


103
104
105
106
107
108
109
of_application_main(int *argc, char **argv[], Class cls)
{
	id <OFApplicationDelegate> delegate;
#ifdef OF_WINDOWS
	wchar_t **wargv, **wenvp;
	int wargc, si = 0;
#endif



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







>
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
of_application_main(int *argc, char **argv[], Class cls)
{
	id <OFApplicationDelegate> delegate;
#ifdef OF_WINDOWS
	wchar_t **wargv, **wenvp;
	int wargc, si = 0;
#endif

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

Modified src/OFNumber.m from [424f2af76e] to [114905a94e].

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <inttypes.h>
#include <locale.h>
#include <math.h>

#import "OFNumber.h"
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFXMLAttribute.h"
#import "OFDataArray.h"







<







13
14
15
16
17
18
19

20
21
22
23
24
25
26
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <inttypes.h>

#include <math.h>

#import "OFNumber.h"
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFXMLAttribute.h"
#import "OFDataArray.h"
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
	case OF_NUMBER_TYPE_INTMAX:
	case OF_NUMBER_TYPE_PTRDIFF:
	case OF_NUMBER_TYPE_INTPTR:
		return [OFString stringWithFormat: @"%jd", [self intMaxValue]];
	case OF_NUMBER_TYPE_FLOAT:
		ret = [OFMutableString stringWithFormat: @"%g", _value.float_];

		{
			void *pool = objc_autoreleasePoolPush();
			OFString *decimalPoint = [OFString stringWithUTF8String:
			    localeconv()->decimal_point];

			if (![ret containsString: decimalPoint])
			       [ret appendFormat: @"%@0", decimalPoint];

			objc_autoreleasePoolPop(pool);
		}

		[ret makeImmutable];

		return ret;
	case OF_NUMBER_TYPE_DOUBLE:
		ret = [OFMutableString stringWithFormat: @"%g", _value.double_];

		{
			void *pool = objc_autoreleasePoolPush();
			OFString *decimalPoint = [OFString stringWithUTF8String:
			    localeconv()->decimal_point];

			if (![ret containsString: decimalPoint])
			       [ret appendFormat: @"%@0", decimalPoint];

			objc_autoreleasePoolPop(pool);
		}

		[ret makeImmutable];

		return ret;
	default:
		@throw [OFInvalidFormatException exception];
	}







<
<
<
<
<
|
|
<
<
<







<
<
<
<
<
|
|
<
<
<







902
903
904
905
906
907
908





909
910



911
912
913
914
915
916
917





918
919



920
921
922
923
924
925
926
	case OF_NUMBER_TYPE_INTMAX:
	case OF_NUMBER_TYPE_PTRDIFF:
	case OF_NUMBER_TYPE_INTPTR:
		return [OFString stringWithFormat: @"%jd", [self intMaxValue]];
	case OF_NUMBER_TYPE_FLOAT:
		ret = [OFMutableString stringWithFormat: @"%g", _value.float_];






		if (![ret containsString: @"."])
			[ret appendString: @".0"];




		[ret makeImmutable];

		return ret;
	case OF_NUMBER_TYPE_DOUBLE:
		ret = [OFMutableString stringWithFormat: @"%g", _value.double_];






		if (![ret containsString: @"."])
			[ret appendString: @".0"];




		[ret makeImmutable];

		return ret;
	default:
		@throw [OFInvalidFormatException exception];
	}

Modified src/OFString.m from [4bcce6446e] to [2a34be912f].

26
27
28
29
30
31
32

33
34
35
36
37
38
39

#import "OFString.h"
#import "OFString_UTF8.h"
#import "OFString_UTF8+Private.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFDataArray.h"

#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFURL.h"
#ifdef OF_HAVE_SOCKETS
# import "OFHTTPClient.h"
# import "OFHTTPRequest.h"







>







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

#import "OFString.h"
#import "OFString_UTF8.h"
#import "OFString_UTF8+Private.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFDataArray.h"
#import "OFSystemInfo.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFURL.h"
#ifdef OF_HAVE_SOCKETS
# import "OFHTTPClient.h"
# import "OFHTTPRequest.h"
2331
2332
2333
2334
2335
2336
2337

2338


2339
2340
2341
2342
2343
2344
2345

	return value;
}

- (float)floatValue
{
	void *pool = objc_autoreleasePoolPush();

	const char *UTF8String = [self UTF8String];


	char *endPointer = NULL;
	float value;

	while (*UTF8String == ' ' || *UTF8String == '\t' ||
	    *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f')
		UTF8String++;








>
|
>
>







2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349

	return value;
}

- (float)floatValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *decimalPoint = [OFSystemInfo decimalPoint];
	const char *UTF8String = [[self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint] UTF8String];
	char *endPointer = NULL;
	float value;

	while (*UTF8String == ' ' || *UTF8String == '\t' ||
	    *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f')
		UTF8String++;

2357
2358
2359
2360
2361
2362
2363

2364


2365
2366
2367
2368
2369
2370
2371

	return value;
}

- (double)doubleValue
{
	void *pool = objc_autoreleasePoolPush();

	const char *UTF8String = [self UTF8String];


	char *endPointer = NULL;
	double value;

	while (*UTF8String == ' ' || *UTF8String == '\t' ||
	    *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f')
		UTF8String++;








>
|
>
>







2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378

	return value;
}

- (double)doubleValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *decimalPoint = [OFSystemInfo decimalPoint];
	const char *UTF8String = [[self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint] UTF8String];
	char *endPointer = NULL;
	double value;

	while (*UTF8String == ' ' || *UTF8String == '\t' ||
	    *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f')
		UTF8String++;

Modified src/OFSystemInfo.h from [b6d210b0c4] to [0c8db08aa1].

47
48
49
50
51
52
53







54
55
56
57
58
59
60
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * @return The native 8-bit string encoding of the operating system
 */
+ (of_string_encoding_t)native8BitEncoding;








/*!
 * @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.







>
>
>
>
>
>
>







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 * This is useful to encode strings correctly for passing them to operating
 * system calls.
 *
 * @return The native 8-bit string encoding of the operating system
 */
+ (of_string_encoding_t)native8BitEncoding;

/*!
 * @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 [7a2395920f] to [c6d392a88f].

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 <unistd.h>

#include "platform.h"

#ifdef OF_MAC_OS_X
# include <sys/sysctl.h>
#endif







>







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

/* 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
144
145
146
147
148
149
150





151
152
153
154
155
156
157
}

+ (of_string_encoding_t)native8BitEncoding
{
	/* FIXME */
	return OF_STRING_ENCODING_UTF_8;
}






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







>
>
>
>
>







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
}

+ (of_string_encoding_t)native8BitEncoding
{
	/* FIXME */
	return OF_STRING_ENCODING_UTF_8;
}

+ (OFString*)decimalPoint
{
	return [OFString stringWithUTF8String: localeconv()->decimal_point];
}

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

Modified src/of_asprintf.m from [bff5bd9808] to [91cd8b629a].

22
23
24
25
26
27
28

29
30
31
32
33
34
35
#include <stdarg.h>
#include <stdbool.h>
#include <wchar.h>

#include <sys/types.h>

#import "OFString.h"


#define MAX_SUBFORMAT_LEN 64

#ifndef HAVE_ASPRINTF
/*
 * (v)asprintf might be declared, but HAVE_ASPRINTF not defined because
 * configure determined it is broken. In this case, we must make sure there is







>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdarg.h>
#include <stdbool.h>
#include <wchar.h>

#include <sys/types.h>

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

#define MAX_SUBFORMAT_LEN 64

#ifndef HAVE_ASPRINTF
/*
 * (v)asprintf might be declared, but HAVE_ASPRINTF not defined because
 * configure determined it is broken. In this case, we must make sure there is
62
63
64
65
66
67
68

69
70
71
72
73
74
75
		LENGTH_MODIFIER_L,
		LENGTH_MODIFIER_LL,
		LENGTH_MODIFIER_J,
		LENGTH_MODIFIER_Z,
		LENGTH_MODIFIER_T,
		LENGTH_MODIFIER_CAPITAL_L
	} lengthModifier;

};

#ifndef HAVE_ASPRINTF
static int
vasprintf(char **string, const char *format, va_list arguments)
{
	int length;







>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
		LENGTH_MODIFIER_L,
		LENGTH_MODIFIER_LL,
		LENGTH_MODIFIER_J,
		LENGTH_MODIFIER_Z,
		LENGTH_MODIFIER_T,
		LENGTH_MODIFIER_CAPITAL_L
	} lengthModifier;
	bool useLocale;
};

#ifndef HAVE_ASPRINTF
static int
vasprintf(char **string, const char *format, va_list arguments)
{
	int length;
161
162
163
164
165
166
167




168
169
170
171
172
173
174
	case ' ':
	case '#':
	case '0':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		break;




	default:
		ctx->state = STATE_FORMAT_FIELD_WIDTH;
		ctx->i--;

		break;
	}








>
>
>
>







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
	case ' ':
	case '#':
	case '0':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		break;
	case ',':
		/* ObjFW extension: Use decimal point from locale */
		ctx->useLocale = true;
		break;
	default:
		ctx->state = STATE_FORMAT_FIELD_WIDTH;
		ctx->i--;

		break;
	}

497
498
499
500
501
502
503





























504
505
506
507
508
509
510
		case LENGTH_MODIFIER_CAPITAL_L:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long double));
			break;
		default:
			return false;
		}






























		break;
	case 'c':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
		case LENGTH_MODIFIER_CAPITAL_L:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long double));
			break;
		default:
			return false;
		}

		/*
		 * Ugly hack to undo locale, as there is nothing such as
		 * asprintf_l in POSIX.
		 */
		if (!ctx->useLocale) {
			void *pool = objc_autoreleasePoolPush();
			char *tmp2;

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

			tmp = tmp2;
		}

		break;
	case 'c':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
608
609
610
611
612
613
614

615
616
617
618
619
620
621

		free(tmp);
	}

	memset(ctx->subformat, 0, MAX_SUBFORMAT_LEN);
	ctx->subformatLen = 0;
	ctx->lengthModifier = LENGTH_MODIFIER_NONE;


	ctx->last = ctx->i + 1;
	ctx->state = STATE_STRING;

	return true;
}








>







643
644
645
646
647
648
649
650
651
652
653
654
655
656
657

		free(tmp);
	}

	memset(ctx->subformat, 0, MAX_SUBFORMAT_LEN);
	ctx->subformatLen = 0;
	ctx->lengthModifier = LENGTH_MODIFIER_NONE;
	ctx->useLocale = false;

	ctx->last = ctx->i + 1;
	ctx->state = STATE_STRING;

	return true;
}

637
638
639
640
641
642
643

644
645
646
647
648
649
650
	memset(ctx.subformat, 0, MAX_SUBFORMAT_LEN + 1);
	ctx.subformatLen = 0;
	va_copy(ctx.arguments, arguments);
	ctx.bufferLen = 0;
	ctx.last = 0;
	ctx.state = STATE_STRING;
	ctx.lengthModifier = LENGTH_MODIFIER_NONE;


	if ((ctx.buffer = malloc(1)) == NULL)
		return -1;

	for (ctx.i = 0; ctx.i < ctx.formatLen; ctx.i++) {
		if (!states[ctx.state](&ctx)) {
			free(ctx.buffer);







>







673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
	memset(ctx.subformat, 0, MAX_SUBFORMAT_LEN + 1);
	ctx.subformatLen = 0;
	va_copy(ctx.arguments, arguments);
	ctx.bufferLen = 0;
	ctx.last = 0;
	ctx.state = STATE_STRING;
	ctx.lengthModifier = LENGTH_MODIFIER_NONE;
	ctx.useLocale = false;

	if ((ctx.buffer = malloc(1)) == NULL)
		return -1;

	for (ctx.i = 0; ctx.i < ctx.formatLen; ctx.i++) {
		if (!states[ctx.state](&ctx)) {
			free(ctx.buffer);

Modified tests/OFStringTests.m from [461bffb20c] to [cd4f1dfd47].

520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
	    OFInvalidFormatException, [@"0x" hexadecimalValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[hexadecimalValue] #3",
	    OFInvalidFormatException, [@"$" hexadecimalValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[hexadecimalValue] #4",
	    OFInvalidFormatException, [@"$ " hexadecimalValue])

	EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #1",
	    OFInvalidFormatException, [@"0,0" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #2",
	    OFInvalidFormatException, [@"0.0a" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #3",
	    OFInvalidFormatException, [@"0 0" floatValue])

	EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #1",
	    OFInvalidFormatException, [@"0,0" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #2",
	    OFInvalidFormatException, [@"0.0a" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #3",
	    OFInvalidFormatException, [@"0 0" floatValue])

	EXPECT_EXCEPTION(@"Detect out of range in -[decimalValue]",
	    OFOutOfRangeException,
	    [@"12345678901234567890123456789012345678901234567890"
	     @"12345678901234567890123456789012345678901234567890"
	    decimalValue])







<
<

|



<
<

|







520
521
522
523
524
525
526


527
528
529
530
531


532
533
534
535
536
537
538
539
540
	    OFInvalidFormatException, [@"0x" hexadecimalValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[hexadecimalValue] #3",
	    OFInvalidFormatException, [@"$" hexadecimalValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[hexadecimalValue] #4",
	    OFInvalidFormatException, [@"$ " hexadecimalValue])

	EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #1",


	    OFInvalidFormatException, [@"0.0a" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #2",
	    OFInvalidFormatException, [@"0 0" floatValue])

	EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #1",


	    OFInvalidFormatException, [@"0.0a" floatValue])
	EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #2",
	    OFInvalidFormatException, [@"0 0" floatValue])

	EXPECT_EXCEPTION(@"Detect out of range in -[decimalValue]",
	    OFOutOfRangeException,
	    [@"12345678901234567890123456789012345678901234567890"
	     @"12345678901234567890123456789012345678901234567890"
	    decimalValue])

Modified utils/ofhttp/OFHTTP.m from [676c6715db] to [3ef2bcb62b].

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
	if (!_quiet) {
		if (type == nil)
			type = @"unknown";

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








|



|



|







709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
	if (!_quiet) {
		if (type == nil)
			type = @"unknown";

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

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

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
		else
			[of_stdout writeString: @" "];

		for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++)
			[of_stdout writeString: @" "];
	}

	[of_stdout writeFormat: @"▏ %6.2f%% ", percent];

	if (percent == 100) {
		double timeInterval = -[_startDate timeIntervalSinceNow];

		_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
		[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];
}

- (void)_drawReceived
{
	if (_resumedFrom + _received >= GIBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %7.2f GiB ",
		    (float)(_resumedFrom + _received) / GIBIBYTE];
	else if (_resumedFrom + _received >= MEBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %7.2f MiB ",
		    (float)(_resumedFrom + _received) / MEBIBYTE];
	else if (_resumedFrom + _received >= KIBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %7.2f KiB ",
		    (float)(_resumedFrom + _received) / KIBIBYTE];
	else
		[of_stdout writeFormat:
		    @"\r  %jd bytes ", _resumedFrom + _received];

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

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







|











|






|

|

|

|






|



|



|










|

|

|

|







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
		else
			[of_stdout writeString: @" "];

		for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++)
			[of_stdout writeString: @" "];
	}

	[of_stdout writeFormat: @"▏ %,6.2f%% ", percent];

	if (percent == 100) {
		double timeInterval = -[_startDate timeIntervalSinceNow];

		_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
		[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];
}

- (void)_drawReceived
{
	if (_resumedFrom + _received >= GIBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %,7.2f GiB ",
		    (float)(_resumedFrom + _received) / GIBIBYTE];
	else if (_resumedFrom + _received >= MEBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %,7.2f MiB ",
		    (float)(_resumedFrom + _received) / MEBIBYTE];
	else if (_resumedFrom + _received >= KIBIBYTE)
		[of_stdout writeFormat:
		    @"\r  %,7.2f KiB ",
		    (float)(_resumedFrom + _received) / KIBIBYTE];
	else
		[of_stdout writeFormat:
		    @"\r  %jd bytes ", _resumedFrom + _received];

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

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