tron@2186: /* $Id$ */ tron@2186: belugas@6919: /** @file unix.cpp */ belugas@6919: truelight@0: #include "stdafx.h" Darkvater@1892: #include "openttd.h" tron@2159: #include "variables.h" rubidium@8603: #include "textbuf_gui.h" rubidium@8627: #include "functions.h" rubidium@8627: #include "core/random_func.hpp" truelight@0: rubidium@8760: #include "table/strings.h" rubidium@8760: truelight@0: #include truelight@0: #include truelight@0: #include truelight@0: #include tron@1509: #include truelight@0: tron@1850: #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__) tron@1597: #define HAS_STATVFS tron@1597: #endif tron@1597: tron@1597: #ifdef HAS_STATVFS truelight@0: #include truelight@0: #endif truelight@0: truelight@0: truelight@0: #ifdef __MORPHOS__ truelight@0: #include truelight@0: ULONG __stack = (1024*1024)*2; // maybe not that much is needed actually ;) tron@2356: belugas@6919: /* The system supplied definition of SIG_IGN does not match */ tron@2356: #undef SIG_IGN tron@2356: #define SIG_IGN (void (*)(int))1 truelight@0: #endif /* __MORPHOS__ */ truelight@0: bjarni@770: #ifdef __AMIGA__ bjarni@770: #warning add stack symbol to avoid that user needs to set stack manually (tokai) tron@915: // ULONG __stack = bjarni@770: #endif bjarni@770: bjarni@2188: #if defined(__APPLE__) bjarni@2217: #if defined(WITH_SDL) belugas@6919: /*the mac implementation needs this file included in the same file as main() */ bjarni@2217: #include bjarni@2217: #endif bjarni@2188: #endif truelight@0: Darkvater@4221: bool FiosIsRoot(const char *path) truelight@0: { Darkvater@4221: #if !defined(__MORPHOS__) && !defined(__AMIGAOS__) Darkvater@4221: return path[1] == '\0'; Darkvater@4221: #else Darkvater@4221: /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */ Darkvater@4221: const char *s = strchr(path, ':'); rubidium@7128: return s != NULL && s[1] == '\0'; Darkvater@4221: #endif Darkvater@4221: } Darkvater@4221: rubidium@6573: void FiosGetDrives() Darkvater@4221: { Darkvater@4221: return; Darkvater@4221: } Darkvater@4221: Darkvater@4222: bool FiosGetDiskFreeSpace(const char *path, uint32 *tot) Darkvater@4222: { Darkvater@4222: uint32 free = 0; Darkvater@4222: Darkvater@4222: #ifdef HAS_STATVFS bjarni@8481: # ifdef __APPLE__ bjarni@8481: /* OSX 10.3 lacks statvfs so don't try to use it even though later versions of OSX has it. */ bjarni@8481: if (MacOSVersionIsAtLeast(10, 4, 0)) bjarni@8481: # endif Darkvater@4222: { Darkvater@4222: struct statvfs s; Darkvater@4222: Darkvater@4222: if (statvfs(path, &s) != 0) return false; Darkvater@4222: free = (uint64)s.f_frsize * s.f_bavail >> 20; Darkvater@4222: } Darkvater@4222: #endif Darkvater@4222: if (tot != NULL) *tot = free; Darkvater@4222: return true; Darkvater@4222: } Darkvater@4222: Darkvater@4221: bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb) Darkvater@4221: { truelight@0: char filename[MAX_PATH]; truelight@0: Darkvater@4221: #if defined(__MORPHOS__) || defined(__AMIGAOS__) Darkvater@4221: /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */ Darkvater@4221: if (FiosIsRoot(path)) { Darkvater@4221: snprintf(filename, lengthof(filename), "%s:%s", path, ent->d_name); Darkvater@4221: } else // XXX - only next line! rubidium@7251: #else rubidium@7251: assert(path[strlen(path) - 1] == PATHSEPCHAR); rubidium@7251: if (strlen(path) > 2) assert(path[strlen(path) - 2] != PATHSEPCHAR); Darkvater@4221: #endif rubidium@7251: snprintf(filename, lengthof(filename), "%s%s", path, ent->d_name); truelight@0: rubidium@6566: return stat(filename, sb) == 0; rubidium@6566: } truelight@0: rubidium@6566: bool FiosIsHiddenFile(const struct dirent *ent) rubidium@6566: { rubidium@6566: return ent->d_name[0] == '.'; truelight@0: } truelight@0: truelight@0: void ShowInfo(const char *str) truelight@0: { Darkvater@5925: fprintf(stderr, "%s\n", str); truelight@0: } truelight@0: truelight@0: void ShowOSErrorBox(const char *buf) truelight@0: { bjarni@2731: #if defined(__APPLE__) belugas@6919: /* this creates an NSAlertPanel with the contents of 'buf' belugas@6919: * this is the native and nicest way to do this on OSX */ bjarni@2188: ShowMacDialog( buf, "See readme for more info\nMost likely you are missing files from the original TTD", "Quit" ); bjarni@529: #else belugas@6919: /* all systems, but OSX */ truelight@0: fprintf(stderr, "\033[1;31mError: %s\033[0;39m\n", buf); truelight@0: #endif truelight@0: } truelight@0: bjarni@2736: #ifdef WITH_COCOA rubidium@6573: void cocoaSetupAutoreleasePool(); rubidium@6573: void cocoaReleaseAutoreleasePool(); bjarni@2736: #endif bjarni@2736: truelight@0: int CDECL main(int argc, char* argv[]) truelight@0: { bjarni@2736: int ret; bjarni@2736: bjarni@2736: #ifdef WITH_COCOA bjarni@2736: cocoaSetupAutoreleasePool(); tron@4077: /* This is passed if we are launched by double-clicking */ tron@4077: if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) { bjarni@2736: argv[1] = NULL; bjarni@2736: argc = 1; bjarni@2736: } bjarni@2736: #endif bjarni@2736: skidd13@8431: SetRandomSeed(time(NULL)); truelight@0: tron@1509: signal(SIGPIPE, SIG_IGN); truelight@0: bjarni@2736: ret = ttd_main(argc, argv); bjarni@2736: bjarni@2736: #ifdef WITH_COCOA bjarni@2736: cocoaReleaseAutoreleasePool(); bjarni@2736: #endif bjarni@2736: bjarni@2736: return ret; truelight@0: } truelight@0: tron@1470: bool InsertTextBufferClipboard(Textbuf *tb) tron@1470: { tron@1470: return false; tron@1470: } Darkvater@1885: ludde@2073: belugas@6919: /* multi os compatible sleep function */ ludde@2073: ludde@2073: #ifdef __AMIGA__ belugas@6919: /* usleep() implementation */ ludde@2073: # include ludde@2073: # include ludde@2073: ludde@2073: extern struct Device *TimerBase = NULL; ludde@2073: extern struct MsgPort *TimerPort = NULL; ludde@2073: extern struct timerequest *TimerRequest = NULL; ludde@2073: #endif // __AMIGA__ ludde@2073: ludde@2073: void CSleep(int milliseconds) ludde@2073: { truelight@6348: #if defined(PSP) truelight@6348: sceKernelDelayThread(milliseconds * 1000); truelight@6348: #elif defined(__BEOS__) ludde@2073: snooze(milliseconds * 1000); truelight@6348: #elif defined(__AMIGA__) ludde@2073: { ludde@2073: ULONG signals; ludde@2073: ULONG TimerSigBit = 1 << TimerPort->mp_SigBit; ludde@2073: belugas@6919: /* send IORequest */ ludde@2073: TimerRequest->tr_node.io_Command = TR_ADDREQUEST; ludde@2073: TimerRequest->tr_time.tv_secs = (milliseconds * 1000) / 1000000; ludde@2073: TimerRequest->tr_time.tv_micro = (milliseconds * 1000) % 1000000; ludde@2073: SendIO((struct IORequest *)TimerRequest); ludde@2073: ludde@2073: if (!((signals = Wait(TimerSigBit | SIGBREAKF_CTRL_C)) & TimerSigBit) ) { ludde@2073: AbortIO((struct IORequest *)TimerRequest); ludde@2073: } ludde@2073: WaitIO((struct IORequest *)TimerRequest); ludde@2073: } truelight@6348: #else truelight@6348: usleep(milliseconds * 1000); truelight@6348: #endif ludde@2073: } bjarni@3260: Darkvater@3329: #ifdef WITH_ICONV Darkvater@3329: Darkvater@3332: #include Darkvater@3332: #include Darkvater@3332: #include "debug.h" rubidium@8711: #include "string_func.h" Darkvater@3332: peter1138@5108: #define INTERNALCODE "UTF-8" Darkvater@3329: Darkvater@3329: /** Try and try to decipher the current locale from environmental Darkvater@3329: * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable Darkvater@3329: * locale can be found, don't do any conversion "" */ rubidium@6573: static const char *GetLocalCode() bjarni@3260: { Darkvater@3408: #if defined(__APPLE__) Darkvater@3329: return "UTF-8-MAC"; Darkvater@3329: #else Darkvater@3329: /* Strip locale (eg en_US.UTF-8) to only have UTF-8 */ Darkvater@3329: const char *locale = GetCurrentLocale("LC_CTYPE"); Darkvater@3329: if (locale != NULL) locale = strchr(locale, '.'); bjarni@3260: Darkvater@3329: return (locale == NULL) ? "" : locale + 1; Darkvater@3329: #endif Darkvater@3329: } Darkvater@3329: Darkvater@3329: /** FYI: This is not thread-safe. Darkvater@3329: * convert between locales, which from and which to is set in the calling Darkvater@3329: * functions OTTD2FS() and FS2OTTD(). You should NOT use this function directly Darkvater@3329: * NOTE: iconv was added in OSX 10.3. 10.2.x will still have the invalid char Darkvater@3329: * issues. There aren't any easy fix for this */ Darkvater@3329: static const char *convert_tofrom_fs(iconv_t convd, const char *name) Darkvater@3329: { Darkvater@3329: static char buf[1024]; Darkvater@3329: /* Work around buggy iconv implementation where inbuf is wrongly typed as Darkvater@3329: * non-const. Correct implementation is at Darkvater@3329: * http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html */ egladil@8335: #ifdef HAVE_BROKEN_ICONV Darkvater@3329: char *inbuf = (char*)name; Darkvater@3329: #else Darkvater@3329: const char *inbuf = name; Darkvater@3329: #endif Darkvater@3329: Darkvater@3329: char *outbuf = buf; Darkvater@3329: size_t outlen = sizeof(buf) - 1; Darkvater@3329: size_t inlen = strlen(name); Darkvater@3329: Darkvater@3329: ttd_strlcpy(outbuf, name, sizeof(buf)); Darkvater@3329: Darkvater@3329: iconv(convd, NULL, NULL, NULL, NULL); Darkvater@3329: if (iconv(convd, &inbuf, &inlen, &outbuf, &outlen) == (size_t)(-1)) { Darkvater@5568: DEBUG(misc, 0, "[iconv] error converting '%s'. Errno %d", name, errno); bjarni@3260: } Darkvater@3329: Darkvater@3329: *outbuf = '\0'; belugas@6919: /* FIX: invalid characters will abort conversion, but they shouldn't occur? */ Darkvater@3329: return buf; bjarni@3260: } Darkvater@3329: Darkvater@3329: /** Convert from OpenTTD's encoding to that of the local environment Darkvater@3329: * @param name pointer to a valid string that will be converted Darkvater@3329: * @return pointer to a new stringbuffer that contains the converted string */ Darkvater@3329: const char *OTTD2FS(const char *name) Darkvater@3329: { Darkvater@3329: static iconv_t convd = (iconv_t)(-1); Darkvater@3329: Darkvater@3329: if (convd == (iconv_t)(-1)) { Darkvater@3329: const char *env = GetLocalCode(); Darkvater@3329: convd = iconv_open(env, INTERNALCODE); Darkvater@3329: if (convd == (iconv_t)(-1)) { Darkvater@5568: DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env); Darkvater@3329: return name; Darkvater@3329: } Darkvater@3329: } Darkvater@3329: Darkvater@3329: return convert_tofrom_fs(convd, name); Darkvater@3329: } Darkvater@3329: Darkvater@3329: /** Convert to OpenTTD's encoding from that of the local environment Darkvater@3329: * @param name pointer to a valid string that will be converted Darkvater@3329: * @return pointer to a new stringbuffer that contains the converted string */ Darkvater@3329: const char *FS2OTTD(const char *name) Darkvater@3329: { Darkvater@3329: static iconv_t convd = (iconv_t)(-1); Darkvater@3329: Darkvater@3329: if (convd == (iconv_t)(-1)) { Darkvater@3329: const char *env = GetLocalCode(); Darkvater@3329: convd = iconv_open(INTERNALCODE, env); Darkvater@3329: if (convd == (iconv_t)(-1)) { Darkvater@5568: DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE); Darkvater@3329: return name; Darkvater@3329: } Darkvater@3329: } Darkvater@3329: Darkvater@3329: return convert_tofrom_fs(convd, name); Darkvater@3329: } Darkvater@3329: Darkvater@3329: #else Darkvater@3329: const char *FS2OTTD(const char *name) {return name;} Darkvater@3329: const char *OTTD2FS(const char *name) {return name;} Darkvater@3329: #endif /* WITH_ICONV */