Artifact 514600c3807ac0678cf1c32393a46a0c2f37c46ba29d01eb054d5c750810b954:
- File
src/OFApplication.m
— part of check-in
[06bcb21fc7]
at
2017-01-09 06:26:04
on branch trunk
— 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. (user: js, size: 10952) [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 "OFLocalization.h" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFThread.h" #import "OFThread+Private.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.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 [[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", 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) { const of_string_encoding_t encoding = [OFLocalization encoding]; for (; *env != NULL; env++) { OFString *key, *value; char *sep; 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 = [OFLocalization encoding]; # 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