Artifact 669a08ac5c5fda8c95f6ca331a20e19aa70eb01f767acb308662451e0b87f9fa:
- File
src/OFApplication.m
— part of check-in
[6dff0f5922]
at
2017-01-07 02:34:39
on branch trunk
— 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. (user: js, size: 10856) [annotate] [blame] [check-ins using]
/* * 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 <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" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFThread.h" #import "OFThread+Private.h" #if defined(OF_MAC_OS_X) # include <crt_externs.h> #elif defined(OF_WINDOWS) # include <windows.h> extern int _CRT_glob; extern void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*); #elif !defined(OF_IOS) extern char **environ; #endif #ifdef OF_PSP # include <pspkerneltypes.h> # include <psploadexec.h> #endif #ifdef OF_NINTENDO_DS # define asm __asm__ # include <nds.h> # undef asm #endif @interface OFApplication () - (void)OF_setArgumentCount: (int*)argc andArgumentValues: (char**[])argv; #ifdef OF_WINDOWS - (void)OF_setArgumentCount: (int)argc andWideArgumentValues: (wchar_t*[])argv; #endif - (void)OF_run; @end static OFApplication *app = nil; static void atexitHandler(void) { id <OFApplicationDelegate> delegate = [app delegate]; if ([delegate respondsToSelector: @selector(applicationWillTerminate)]) [delegate applicationWillTerminate]; [delegate release]; } #define SIGNAL_HANDLER(sig) \ static void \ handle##sig(int signal) \ { \ app->_##sig##Handler(app->_delegate, \ @selector(applicationDidReceive##sig)); \ } SIGNAL_HANDLER(SIGINT) #ifndef OF_WINDOWS SIGNAL_HANDLER(SIGHUP) SIGNAL_HANDLER(SIGUSR1) SIGNAL_HANDLER(SIGUSR2) #endif #undef SIGNAL_HANDLER int 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", class_getName(cls), class_getName(cls)); exit(1); } app = [[OFApplication alloc] init]; [app OF_setArgumentCount: argc andArgumentValues: argv]; #ifdef OF_WINDOWS __wgetmainargs(&wargc, &wargv, &wenvp, _CRT_glob, &si); [app OF_setArgumentCount: wargc andWideArgumentValues: wargv]; #endif delegate = [[cls alloc] init]; [app setDelegate: delegate]; [app OF_run]; [delegate release]; return 0; } @implementation OFApplication @synthesize programName = _programName, arguments = _arguments; @synthesize environment = _environment; + (OFApplication*)sharedApplication { return app; } + (OFString*)programName { return [app programName]; } + (OFArray*)arguments { return [app arguments]; } + (OFDictionary*)environment { return [app environment]; } + (void)terminate { [self terminateWithStatus: EXIT_SUCCESS]; OF_UNREACHABLE } + (void)terminateWithStatus: (int)status { #ifndef OF_PSP exit(status); #else sceKernelExitGame(); OF_UNREACHABLE #endif } - init { self = [super init]; @try { void *pool; OFMutableDictionary *environment; #if defined(OF_MAC_OS_X) char **env = *_NSGetEnviron(); #elif defined(OF_WINDOWS) of_char16_t *env, *env0; #elif !defined(OF_IOS) char **env = environ; #else char *env; #endif environment = [[OFMutableDictionary alloc] init]; atexit(atexitHandler); #if defined(OF_WINDOWS) env = env0 = GetEnvironmentStringsW(); while (*env != 0) { OFString *tmp, *key, *value; size_t length, pos; pool = objc_autoreleasePoolPush(); length = of_string_utf16_length(env); tmp = [OFString stringWithUTF16String: env length: length]; env += length + 1; /* * cmd.exe seems to add some special variables which * start with a "=", even though variable names are not * allowed to contain a "=". */ if ([tmp hasPrefix: @"="]) { objc_autoreleasePoolPop(pool); continue; } pos = [tmp rangeOfString: @"="].location; if (pos == OF_NOT_FOUND) { fprintf(stderr, "Warning: Invalid environment " "variable: %s\n", [tmp UTF8String]); continue; } key = [tmp substringWithRange: of_range(0, pos)]; value = [tmp substringWithRange: of_range(pos + 1, [tmp length] - pos - 1)]; [environment setObject: value forKey: key]; objc_autoreleasePoolPop(pool); } FreeEnvironmentStringsW(env0); #elif !defined(OF_IOS) if (env != NULL) { 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; } key = [OFString stringWithCString: *env encoding: encoding length: sep - *env]; value = [OFString stringWithCString: sep + 1 encoding: encoding]; [environment setObject: value forKey: key]; objc_autoreleasePoolPop(pool); } } #else /* * iOS does not provide environ and Apple does not allow using * _NSGetEnviron on iOS. Therefore, we just get a few common * variables from the environment which applications might * expect. */ pool = objc_autoreleasePoolPush(); if ((env = getenv("HOME")) != NULL) { OFString *home = [[[OFString alloc] initWithUTF8StringNoCopy: env freeWhenDone: false] autorelease]; [environment setObject: home forKey: @"HOME"]; } if ((env = getenv("PATH")) != NULL) { OFString *path = [[[OFString alloc] initWithUTF8StringNoCopy: env freeWhenDone: false] autorelease]; [environment setObject: path forKey: @"PATH"]; } if ((env = getenv("SHELL")) != NULL) { OFString *shell = [[[OFString alloc] initWithUTF8StringNoCopy: env freeWhenDone: false] autorelease]; [environment setObject: shell forKey: @"SHELL"]; } if ((env = getenv("TMPDIR")) != NULL) { OFString *tmpdir = [[[OFString alloc] initWithUTF8StringNoCopy: env freeWhenDone: false] autorelease]; [environment setObject: tmpdir forKey: @"TMPDIR"]; } if ((env = getenv("USER")) != NULL) { OFString *user = [[[OFString alloc] initWithUTF8StringNoCopy: env freeWhenDone: false] autorelease]; [environment setObject: user forKey: @"USER"]; } objc_autoreleasePoolPop(pool); #endif [environment makeImmutable]; _environment = environment; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_arguments release]; [_environment release]; [super dealloc]; } - (void)OF_setArgumentCount: (int*)argc andArgumentValues: (char***)argv { #ifndef OF_WINDOWS void *pool = objc_autoreleasePoolPush(); OFMutableArray *arguments; of_string_encoding_t encoding; _argc = argc; _argv = argv; encoding = [OFSystemInfo native8BitEncoding]; # ifndef OF_NINTENDO_DS if (*argc > 0) { # else if (__system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc > 0) { # endif _programName = [[OFString alloc] initWithCString: (*argv)[0] encoding: encoding]; arguments = [[OFMutableArray alloc] init]; _arguments = arguments; for (int i = 1; i < *argc; i++) [arguments addObject: [OFString stringWithCString: (*argv)[i] encoding: encoding]]; [arguments makeImmutable]; } objc_autoreleasePoolPop(pool); #else _argc = argc; _argv = argv; #endif } #ifdef OF_WINDOWS - (void)OF_setArgumentCount: (int)argc andWideArgumentValues: (wchar_t**)argv { void *pool = objc_autoreleasePoolPush(); OFMutableArray *arguments; if (argc > 0) { _programName = [[OFString alloc] initWithUTF16String: argv[0]]; arguments = [[OFMutableArray alloc] init]; for (int i = 1; i < argc; i++) [arguments addObject: [OFString stringWithUTF16String: argv[i]]]; [arguments makeImmutable]; _arguments = arguments; } objc_autoreleasePoolPop(pool); } #endif - (void)getArgumentCount: (int**)argc andArgumentValues: (char****)argv { *argc = _argc; *argv = _argv; } - (id <OFApplicationDelegate>)delegate { return _delegate; } - (void)setDelegate: (id <OFApplicationDelegate>)delegate { #ifdef HAVE_SIGACTION struct sigaction sa = { .sa_flags = SA_RESTART }; sigemptyset(&sa.sa_mask); # define REGISTER_SIGNAL(sig) \ if ([delegate respondsToSelector: \ @selector(applicationDidReceive##sig)]) { \ _##sig##Handler = (void(*)(id, SEL))[(id)delegate \ methodForSelector: \ @selector(applicationDidReceive##sig)]; \ \ sa.sa_handler = handle##sig; \ } else \ sa.sa_handler = SIG_DFL; \ \ OF_ENSURE(sigaction(sig, &sa, NULL) == 0); #else # define REGISTER_SIGNAL(sig) \ if ([delegate respondsToSelector: \ @selector(applicationDidReceive##sig)]) { \ _##sig##Handler = (void(*)(id, SEL))[(id)delegate \ methodForSelector: \ @selector(applicationDidReceive##sig)]; \ signal(sig, handle##sig); \ } else \ signal(sig, SIG_DFL); #endif _delegate = delegate; REGISTER_SIGNAL(SIGINT) #ifndef OF_WINDOWS REGISTER_SIGNAL(SIGHUP) REGISTER_SIGNAL(SIGUSR1) REGISTER_SIGNAL(SIGUSR2) #endif #undef REGISTER_SIGNAL } - (void)OF_run { void *pool = objc_autoreleasePoolPush(); OFRunLoop *runLoop; #ifdef OF_HAVE_THREADS [OFThread OF_createMainThread]; runLoop = [OFRunLoop currentRunLoop]; #else runLoop = [[[OFRunLoop alloc] init] autorelease]; #endif [OFRunLoop OF_setMainRunLoop: runLoop]; objc_autoreleasePoolPop(pool); /* * Note: runLoop is still valid after the release of the pool, as * OF_setMainRunLoop: retained it. However, we only have a weak * reference to it now, whereas we had a strong reference before. */ pool = objc_autoreleasePoolPush(); [_delegate applicationDidFinishLaunching]; objc_autoreleasePoolPop(pool); [runLoop run]; } - (void)terminate { [[self class] terminate]; OF_UNREACHABLE } - (void)terminateWithStatus: (int)status { [[self class] terminateWithStatus: status]; OF_UNREACHABLE } @end