MegaGlest/mk/linux/mojosetup/mojosetup.c

1290 lines
36 KiB
C

/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*
Copyright (c) 2006-2010 Ryan C. Gordon and others.
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Ryan C. Gordon <icculus@icculus.org>
*
*/
#include <stdarg.h>
#include "universal.h"
#include "platform.h"
#include "gui.h"
#include "lua_glue.h"
#include "fileio.h"
#define TEST_LAUNCH_BROWSER_CODE 0
int MojoSetup_testLaunchBrowserCode(int argc, char **argv);
#define TEST_ARCHIVE_CODE 0
int MojoSetup_testArchiveCode(int argc, char **argv);
#define TEST_NETWORK_CODE 0
int MojoSetup_testNetworkCode(int argc, char **argv);
uint8 scratchbuf_128k[128 * 1024];
MojoSetupEntryPoints GEntryPoints =
{
xmalloc,
xrealloc,
xstrdup,
xstrncpy,
translate,
logWarning,
logError,
logInfo,
logDebug,
format,
numstr,
MojoPlatform_ticks,
utf8codepoint,
utf8len,
splitText,
isValidProductKey,
};
int GArgc = 0;
const char **GArgv = NULL;
static char *crashedmsg = NULL;
static char *termedmsg = NULL;
void MojoSetup_crashed(void)
{
if (crashedmsg == NULL)
panic("BUG: crash at startup");
else
fatal(crashedmsg);
} // MojoSetup_crash
void MojoSetup_terminated(void)
{
if (termedmsg == NULL) // no translation yet.
panic("The installer has been stopped by the system.");
else
fatal(termedmsg);
} // MojoSetup_crash
#if !SUPPORT_MULTIARCH
#define trySwitchBinaries()
#else
static void trySwitchBinary(MojoArchive *ar)
{
MojoInput *io = ar->openCurrentEntry(ar);
if (io != NULL)
{
const uint32 imglen = (uint32) io->length(io);
uint8 *img = (uint8 *) xmalloc(imglen);
const uint32 br = io->read(io, img, imglen);
io->close(io);
if (br == imglen)
{
logInfo("Switching binary with '%0'...", ar->prevEnum.filename);
MojoPlatform_switchBin(img, imglen); // no return on success.
logError("...Switch binary failed.");
} // if
free(img);
} // if
} // trySwitchBinary
static void trySwitchBinaries(void)
{
if (cmdlinestr("nobinswitch", "MOJOSETUP_NOBINSWITCH", NULL) != NULL)
return; // we are already switched or the user is preventing it.
setenv("MOJOSETUP_NOBINSWITCH", "1", 1);
setenv("MOJOSETUP_BASE", GBaseArchivePath, 1);
if (GBaseArchive->enumerate(GBaseArchive))
{
const MojoArchiveEntry *entinfo;
while ((entinfo = GBaseArchive->enumNext(GBaseArchive)) != NULL)
{
if (entinfo->type != MOJOARCHIVE_ENTRY_FILE)
continue;
if (strncmp(entinfo->filename, "arch/", 5) != 0)
continue;
trySwitchBinary(GBaseArchive);
} // while
} // if
} // trySwitchBinaries
#endif
static boolean trySpawnTerminalGui(void)
{
if (cmdlinestr("notermspawn", "MOJOSETUP_NOTERMSPAWN", NULL) != NULL)
return false; // we already spawned or the user is preventing it.
if (MojoPlatform_istty()) // maybe we can spawn a terminal for stdio?
return false; // We're a terminal already, no need to spawn one.
logInfo("No usable GUI found. Trying to spawn a terminal...");
if (!MojoPlatform_spawnTerminal())
{
logError("...Terminal spawning failed.");
return false;
} // if
assert(MojoPlatform_istty());
return (MojoGui_initGuiPlugin() != NULL);
} // trySpawnTerminalGui
static boolean initEverything(void)
{
MojoLog_initLogging();
logInfo("MojoSetup starting up...");
// We have to panic on errors until the GUI is ready. Try to make things
// "succeed" unless they are catastrophic, and report problems later.
// Start with the base archive work, since it might have GUI plugins.
// None of these panic() calls are localized, since localization isn't
// functional until MojoLua_initLua() succeeds.
if (!MojoArchive_initBaseArchive())
panic("Initial setup failed. Cannot continue.");
trySwitchBinaries(); // may not return.
if (!MojoGui_initGuiPlugin())
{
// This could terminate the process (and relaunch).
if (!trySpawnTerminalGui())
panic("Failed to start GUI. Is your download incomplete or corrupt?");
} // if
else if (!MojoLua_initLua())
{
// (...but if you're the developer: are your files in the wrong place?)
panic("Failed to start. Is your download incomplete or corrupt?");
} // else if
crashedmsg = xstrdup(_("The installer has crashed due to a bug."));
termedmsg = xstrdup(_("The installer has been stopped by the system."));
return true;
} // initEverything
static void deinitEverything(void)
{
char *tmp = NULL;
logInfo("MojoSetup shutting down...");
MojoLua_deinitLua();
MojoGui_deinitGuiPlugin();
MojoArchive_deinitBaseArchive();
MojoLog_deinitLogging();
tmp = crashedmsg;
crashedmsg = NULL;
free(tmp);
tmp = termedmsg;
termedmsg = NULL;
free(tmp);
} // deinitEverything
void MojoChecksum_init(MojoChecksumContext *ctx)
{
memset(ctx, '\0', sizeof (MojoChecksumContext));
#if SUPPORT_CRC32
MojoCrc32_init(&ctx->crc32);
#endif
#if SUPPORT_MD5
MojoMd5_init(&ctx->md5);
#endif
#if SUPPORT_SHA1
MojoSha1_init(&ctx->sha1);
#endif
} // MojoChecksum_init
void MojoChecksum_append(MojoChecksumContext *ctx, const uint8 *d, uint32 len)
{
#if SUPPORT_CRC32
MojoCrc32_append(&ctx->crc32, d, len);
#endif
#if SUPPORT_MD5
MojoMd5_append(&ctx->md5, d, len);
#endif
#if SUPPORT_SHA1
MojoSha1_append(&ctx->sha1, d, len);
#endif
} // MojoChecksum_append
void MojoChecksum_finish(MojoChecksumContext *ctx, MojoChecksums *sums)
{
memset(sums, '\0', sizeof (MojoChecksums));
#if SUPPORT_CRC32
MojoCrc32_finish(&ctx->crc32, &sums->crc32);
#endif
#if SUPPORT_MD5
MojoMd5_finish(&ctx->md5, sums->md5);
#endif
#if SUPPORT_SHA1
MojoSha1_finish(&ctx->sha1, sums->sha1);
#endif
} // MojoChecksum_finish
boolean cmdline(const char *arg)
{
int argc = GArgc;
const char **argv = GArgv;
int i;
if ((arg == NULL) || (argv == NULL))
return false;
while (*arg == '-') // Skip all '-' chars, so "--nosound" == "-nosound"
arg++;
for (i = 1; i < argc; i++)
{
const char *thisarg = argv[i];
if (*thisarg != '-')
continue; // no dash in the string, skip it.
while (*(++thisarg) == '-') { /* keep looping. */ }
if (strcmp(arg, thisarg) == 0)
return true;
} // for
return false;
} // cmdline
const char *cmdlinestr(const char *arg, const char *envr, const char *deflt)
{
uint32 len = 0;
int argc = GArgc;
const char **argv = GArgv;
int i;
if (envr != NULL)
{
const char *val = getenv(envr);
if (val != NULL)
return val;
} // if
if (arg == NULL)
return deflt;
while (*arg == '-') // Skip all '-' chars, so "--nosound" == "-nosound"
arg++;
len = strlen(arg);
for (i = 1; i < argc; i++)
{
const char *thisarg = argv[i];
if (*thisarg != '-')
continue; // no dash in the string, skip it.
while (*(++thisarg) == '-') { /* keep looping. */ }
if (strncmp(arg, thisarg, len) != 0)
continue; // not us.
thisarg += len; // skip ahead in string to end of match.
if (*thisarg == '=') // --a=b format.
return (thisarg + 1);
else if (*thisarg == '\0') // --a b format.
return ((argv[i+1] == NULL) ? deflt : argv[i+1]);
} // for
return deflt;
} // cmdlinestr
boolean wildcardMatch(const char *str, const char *pattern)
{
char sch = *(str++);
while (true)
{
const char pch = *(pattern++);
if (pch == '?')
{
if ((sch == '\0') || (sch == '/'))
return false;
sch = *(str++);
} // else if
else if (pch == '*')
{
char nextpch = *pattern;
if ((nextpch != '?') && (nextpch != '*'))
{
while ((sch != '\0') && (sch != nextpch))
sch = *(str++);
} // if
} // else if
else
{
if (pch != sch)
return false;
else if (pch == '\0')
break;
sch = *(str++);
} // else
} // while
return true;
} // wildcardMatch
const char *numstr(int val)
{
static int pos = 0;
char *ptr = ((char *) scratchbuf_128k) + (pos * 128);
snprintf(ptr, 128, "%d", val);
pos = (pos + 1) % 1000;
return ptr;
} // numstr
static char *format_internal(const char *fmt, va_list ap)
{
// This is kinda nasty. String manipulation in C always is.
char *retval = NULL;
const char *strs[10]; // 0 through 9.
const char *ptr = NULL;
char *wptr = NULL;
size_t len = 0;
int maxfmtid = -2;
int i;
// figure out what this format string contains...
for (ptr = fmt; *ptr; ptr++)
{
if (*ptr == '%')
{
const char ch = *(++ptr);
if (ch == '%') // a literal '%'
maxfmtid = (maxfmtid == -2) ? -1 : maxfmtid;
else if ((ch >= '0') && (ch <= '9'))
maxfmtid = ((maxfmtid > (ch - '0')) ? maxfmtid : (ch - '0'));
else
fatal(_("BUG: Invalid format() string"));
} // if
} // while
if (maxfmtid == -2) // no formatters present at all.
return xstrdup(fmt); // just copy it, we're done.
for (i = 0; i <= maxfmtid; i++) // referenced varargs --> linear array.
{
strs[i] = va_arg(ap, const char *);
if (strs[i] == NULL)
strs[i] = "(null)"; // just to match sprintf() behaviour...
} // for
// allocate the string we'll need in one shot, so we don't have to resize.
for (ptr = fmt; *ptr; ptr++)
{
if (*ptr != '%')
len++;
else
{
const char ch = *(++ptr);
if (ch == '%') // a literal '%'
len++; // just want '%' char.
else //if ((ch >= '0') && (ch <= '9'))
len += strlen(strs[ch - '0']);
} // else
} // while
// Now write the formatted string...
wptr = retval = (char *) xmalloc(len+1);
for (ptr = fmt; *ptr; ptr++)
{
const char strch = *ptr;
if (strch != '%')
*(wptr++) = strch;
else
{
const char ch = *(++ptr);
if (ch == '%') // a literal '%'
*(wptr++) = '%';
else //if ((ch >= '0') && (ch <= '9'))
{
const char *str = strs[ch - '0'];
strcpy(wptr, str);
wptr += strlen(str);
} // else
} // else
} // while
*wptr = '\0';
return retval;
} // format_internal
char *format(const char *fmt, ...)
{
char *retval = NULL;
va_list ap;
va_start(ap, fmt);
retval = format_internal(fmt, ap);
va_end(ap);
return retval;
} // format
#if ((defined _NDEBUG) || (defined NDEBUG))
#define DEFLOGLEV "info"
#else
#define DEFLOGLEV "everything"
#endif
MojoSetupLogLevel MojoLog_logLevel = MOJOSETUP_LOG_EVERYTHING;
static void *logFile = NULL;
void MojoLog_initLogging(void)
{
const char *level = cmdlinestr("loglevel","MOJOSETUP_LOGLEVEL", DEFLOGLEV);
const char *fname = cmdlinestr("log", "MOJOSETUP_LOG", NULL);
if (strcmp(level, "nothing") == 0)
MojoLog_logLevel = MOJOSETUP_LOG_NOTHING;
else if (strcmp(level, "errors") == 0)
MojoLog_logLevel = MOJOSETUP_LOG_ERRORS;
else if (strcmp(level, "warnings") == 0)
MojoLog_logLevel = MOJOSETUP_LOG_WARNINGS;
else if (strcmp(level, "info") == 0)
MojoLog_logLevel = MOJOSETUP_LOG_INFO;
else if (strcmp(level, "debug") == 0)
MojoLog_logLevel = MOJOSETUP_LOG_DEBUG;
else // Unknown string gets everything...that'll teach you.
MojoLog_logLevel = MOJOSETUP_LOG_EVERYTHING;
if ((fname != NULL) && (strcmp(fname, "-") == 0))
logFile = MojoPlatform_stdout();
else if (fname != NULL)
{
const uint32 flags = MOJOFILE_WRITE|MOJOFILE_CREATE|MOJOFILE_TRUNCATE;
const uint16 mode = MojoPlatform_defaultFilePerms();
logFile = MojoPlatform_open(fname, flags, mode);
} // if
} // MojoLog_initLogging
void MojoLog_deinitLogging(void)
{
if (logFile != NULL)
{
MojoPlatform_close(logFile);
logFile = NULL;
} // if
} // MojoLog_deinitLogging
static inline void addLog(MojoSetupLogLevel level, char levelchar,
const char *fmt, va_list ap)
{
if (level <= MojoLog_logLevel)
{
char *str = format_internal(fmt, ap);
//int len = vsnprintf(buf + 2, sizeof (buf) - 2, fmt, ap) + 2;
//buf[0] = levelchar;
//buf[1] = ' ';
int len = strlen(str);
while ( (--len >= 0) && ((str[len] == '\n') || (str[len] == '\r')) ) {}
str[len+1] = '\0'; // delete trailing newline crap.
MojoPlatform_log(str);
if (logFile != NULL)
{
const char *endl = MOJOPLATFORM_ENDLINE;
MojoPlatform_write(logFile, str, strlen(str));
MojoPlatform_write(logFile, endl, strlen(endl));
MojoPlatform_flush(logFile);
} // if
free(str);
} // if
} // addLog
void logWarning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
addLog(MOJOSETUP_LOG_WARNINGS, '-', fmt, ap);
va_end(ap);
} // logWarning
void logError(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
addLog(MOJOSETUP_LOG_ERRORS, '!', fmt, ap);
va_end(ap);
} // logError
void logInfo(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
addLog(MOJOSETUP_LOG_INFO, '*', fmt, ap);
va_end(ap);
} // logInfo
void logDebug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
addLog(MOJOSETUP_LOG_DEBUG, '#', fmt, ap);
va_end(ap);
} // logDebug
uint32 profile(const char *what, uint32 start_time)
{
uint32 retval = MojoPlatform_ticks() - start_time;
if (what != NULL)
logDebug("%0 took %1 ms.", what, numstr((int) retval));
return retval;
} // profile_start
int fatal(const char *fmt, ...)
{
static boolean in_fatal = false;
if (in_fatal)
return panic("BUG: fatal() called more than once!");
in_fatal = true;
// may not want to show a message, since we displayed one elsewhere, etc.
if (fmt != NULL)
{
char *buf = NULL;
va_list ap;
va_start(ap, fmt);
buf = format_internal(fmt, ap);
va_end(ap);
logError("FATAL: %0", buf);
if (GGui != NULL)
GGui->msgbox(_("Fatal error"), buf);
free(buf);
} // if
// Shouldn't call fatal() before app is initialized!
if ( (GGui == NULL) || (!MojoLua_initialized()) )
panic("fatal() called before app is initialized! Panicking...");
MojoLua_callProcedure("revertinstall");
deinitEverything();
exit(23);
return 0;
} // fatal
int panic(const char *err)
{
static int panic_runs = 0;
panic_runs++;
if (panic_runs == 1)
{
logError("PANIC: %0", err);
panic(err);
} // if
else if (panic_runs == 2)
{
boolean domsgbox = ((GGui != NULL) && (GGui->msgbox != NULL));
if (domsgbox)
GGui->msgbox(_("PANIC"), err);
if ((GGui != NULL) && (GGui->deinit != NULL))
GGui->deinit();
if (!domsgbox)
panic(err); /* no GUI plugin...double-panic. */
} // if
else if (panic_runs == 3) // no GUI or panic panicked...write to stderr...
fprintf(stderr, "\n\n\n%s\n %s\n\n\n", _("PANIC"), err);
else // panic is panicking in a loop, terminate without any cleanup...
MojoPlatform_die();
exit(22);
return 0; // shouldn't hit this.
} // panic
char *xstrncpy(char *dst, const char *src, size_t len)
{
snprintf(dst, len, "%s", src);
return dst;
} // xstrncpy
uint32 utf8codepoint(const char **_str)
{
const char *str = *_str;
uint32 retval = 0;
uint32 octet = (uint32) ((uint8) *str);
uint32 octet2, octet3, octet4;
if (octet == 0) // null terminator, end of string.
return 0;
else if (octet < 128) // one octet char: 0 to 127
{
(*_str)++; // skip to next possible start of codepoint.
return octet;
} // else if
else if ((octet > 127) && (octet < 192)) // bad (starts with 10xxxxxx).
{
// Apparently each of these is supposed to be flagged as a bogus
// char, instead of just resyncing to the next valid codepoint.
(*_str)++; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE;
} // else if
else if (octet < 224) // two octets
{
(*_str)++; // advance at least one byte in case of an error
octet -= (128+64);
octet2 = (uint32) ((uint8) *(++str));
if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 1; // skip to next possible start of codepoint.
retval = ((octet << 6) | (octet2 - 128));
if ((retval >= 0x80) && (retval <= 0x7FF))
return retval;
} // else if
else if (octet < 240) // three octets
{
(*_str)++; // advance at least one byte in case of an error
octet -= (128+64+32);
octet2 = (uint32) ((uint8) *(++str));
if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet3 = (uint32) ((uint8) *(++str));
if ((octet3 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 2; // skip to next possible start of codepoint.
retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) );
// There are seven "UTF-16 surrogates" that are illegal in UTF-8.
switch (retval)
{
case 0xD800:
case 0xDB7F:
case 0xDB80:
case 0xDBFF:
case 0xDC00:
case 0xDF80:
case 0xDFFF:
return UNICODE_BOGUS_CHAR_VALUE;
} // switch
// 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge.
if ((retval >= 0x800) && (retval <= 0xFFFD))
return retval;
} // else if
else if (octet < 248) // four octets
{
(*_str)++; // advance at least one byte in case of an error
octet -= (128+64+32+16);
octet2 = (uint32) ((uint8) *(++str));
if ((octet2 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet3 = (uint32) ((uint8) *(++str));
if ((octet3 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet4 = (uint32) ((uint8) *(++str));
if ((octet4 & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 3; // skip to next possible start of codepoint.
retval = ( ((octet << 18)) | ((octet2 - 128) << 12) |
((octet3 - 128) << 6) | ((octet4 - 128)) );
if ((retval >= 0x10000) && (retval <= 0x10FFFF))
return retval;
} // else if
// Five and six octet sequences became illegal in rfc3629.
// We throw the codepoint away, but parse them to make sure we move
// ahead the right number of bytes and don't overflow the buffer.
else if (octet < 252) // five octets
{
(*_str)++; // advance at least one byte in case of an error
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 4; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE;
} // else if
else // six octets
{
(*_str)++; // advance at least one byte in case of an error
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
octet = (uint32) ((uint8) *(++str));
if ((octet & (128+64)) != 128) // Format isn't 10xxxxxx?
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 5; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_VALUE;
} // else if
return UNICODE_BOGUS_CHAR_VALUE;
} // utf8codepoint
int utf8len(const char *str)
{
int retval = 0;
while (utf8codepoint(&str))
retval++;
return retval;
} // utf8len
static char *strfrombuf(const char *text, int len)
{
char *retval = xmalloc(len + 1);
memcpy(retval, text, len);
retval[len] = '\0';
return retval;
} // strfrombuf
char **splitText(const char *text, int scrw, int *_count, int *_w)
{
int i = 0;
int j = 0;
char **retval = NULL;
int count = 0;
int w = 0;
*_count = *_w = 0;
while (*text)
{
const char *utf8text = text;
uint32 ch = 0;
int pos = 0;
int furthest = 0;
for (i = 0; ((ch = utf8codepoint(&utf8text))) && (i < scrw); i++)
{
if ((ch == '\r') || (ch == '\n'))
{
const char nextbyte = *utf8text;
count++;
retval = (char **) xrealloc(retval, count * sizeof (char *));
retval[count-1] = strfrombuf(text, utf8text - text);
if ((ch == '\r') && (nextbyte == '\n')) // DOS endlines!
utf8text++; // skip it.
text = (char *) utf8text; // update to start of new line.
if (i > w)
w = i;
i = -1; // will be zero on next iteration...
} // if
else if ((ch == ' ') || (ch == '\t'))
{
if (i != 0) // trim spaces from start of line...
furthest = i;
else
{
text++;
i = -1; // it'll be zero on next iteration.
} // else
} // else if
} // for
// line overflow or end of stream...
pos = (ch) ? furthest : i;
if ((ch) && (furthest == 0)) // uhoh, no split at all...hack it.
{
pos = utf8len(text);
if (pos > scrw) // too big, have to chop a string in the middle.
pos = scrw;
} // if
if (pos > 0)
{
utf8text = text; // adjust pointer by redecoding from start...
for (j = 0; j < pos; j++)
utf8codepoint(&utf8text);
count++;
retval = (char **) xrealloc(retval, count * sizeof (char*));
retval[count-1] = strfrombuf(text, utf8text - text);
text = (char *) utf8text;
if (pos > w)
w = pos;
} // if
} // while
*_count = count;
*_w = w;
return retval;
} // splitText
static void outOfMemory(void)
{
// Try to translate "out of memory", but not if it causes recursion.
static boolean already_panicked = false;
const char *errstr = "out of memory";
if (!already_panicked)
{
already_panicked = true;
errstr = translate(errstr);
} // if
panic(errstr);
} // outOfMemory
#undef calloc
void *xmalloc(size_t bytes)
{
void *retval = calloc(1, bytes);
if (retval == NULL)
outOfMemory();
return retval;
} // xmalloc
#define calloc(x,y) DO_NOT_CALL_CALLOC__USE_XMALLOC_INSTEAD
#undef realloc
void *xrealloc(void *ptr, size_t bytes)
{
void *retval = realloc(ptr, bytes);
if (retval == NULL)
outOfMemory();
return retval;
} // xrealloc
#define realloc(x,y) DO_NOT_CALL_REALLOC__USE_XREALLOC_INSTEAD
char *xstrdup(const char *str)
{
char *retval = (char *) xmalloc(strlen(str) + 1);
strcpy(retval, str);
return retval;
} // xstrdup
// We have to supply this function under certain build types.
#if MOJOSETUP_INTERNAL_BZLIB && BZ_NO_STDIO
void bz_internal_error(int errcode)
{
fatal(_("bzlib triggered an internal error: %0"), numstr(errcode));
} // bz_internal_error
#endif
#if SUPPORT_STBIMAGE
unsigned char *stbi_load_from_memory(unsigned char *buffer, int len, int *x,
int *y, int *comp, int req_comp);
#endif
uint8 *decodeImage(const uint8 *data, uint32 size, uint32 *w, uint32 *h)
{
uint8 *retval = MojoPlatform_decodeImage(data, size, w, h);
#if SUPPORT_STBIMAGE
if (retval == NULL) // try our built-in routines.
{
const int siz = (int) size;
unsigned char *buf = (unsigned char *) data;
int x = 0, y = 0, comp = 0;
retval = (uint8 *) stbi_load_from_memory(buf, siz, &x, &y, &comp, 4);
*w = (uint32) x;
*h = (uint32) y;
} // if
#endif
if (retval == NULL)
*w = *h = 0;
return retval;
} // decodeImage
boolean isValidProductKey(const char *fmt, const char *key)
{
if (fmt == NULL)
return true;
else if (key == NULL)
return false;
while (*fmt)
{
const char fmtch = *(fmt++);
const char keych = *(key++);
switch (fmtch)
{
case '-':
case ' ':
if ((keych == ' ') || (keych == '-'))
break;
key--; // user didn't type this, roll back.
break;
case '#':
if ((keych >= '0') && (keych <= '9'))
break;
return false;
case 'X':
if ( ((keych >= 'A') && (keych <= 'Z')) ||
((keych >= 'a') && (keych <= 'z')) )
break;
return false;
case '?':
if ( ((keych >= 'A') && (keych <= 'Z')) ||
((keych >= 'a') && (keych <= 'z')) ||
((keych >= '0') && (keych <= '9')) )
break;
return false;
case '*':
break;
default:
// this should have been caught by schema sanitize.
assert(false && "Invalid product key format.");
return false;
} // switch
} // while
return (*key == '\0');
} // isValidProductKey
// This is called from main()/WinMain()/whatever.
int MojoSetup_main(int argc, char **argv)
{
GArgc = argc;
GArgv = (const char **) argv;
if (cmdline("buildver"))
{
printf("%s\n", GBuildVer);
return 0;
} // if
#if TEST_LAUNCH_BROWSER_CODE
return MojoSetup_testLaunchBrowserCode(argc, argv);
#endif
#if TEST_ARCHIVE_CODE
return MojoSetup_testArchiveCode(argc, argv);
#endif
#if TEST_NETWORK_CODE
return MojoSetup_testNetworkCode(argc, argv);
#endif
if (!initEverything())
return 1;
// Jump into Lua for the heavy lifting.
MojoLua_runFile("mojosetup_mainline");
deinitEverything();
return 0;
} // MojoSetup_main
#if TEST_LAUNCH_BROWSER_CODE
int MojoSetup_testLaunchBrowserCode(int argc, char **argv)
{
int i;
if (!MojoArchive_initBaseArchive()) // Maybe need for xdg-open script.
panic("Initial setup failed. Cannot continue.");
printf("Testing browser launching code...\n\n");
for (i = 1; i < argc; i++)
{
const boolean rc = MojoPlatform_launchBrowser(argv[i]);
printf("Launch '%s': %s\n", argv[i], rc ? "success" : "failure");
} // for
MojoArchive_deinitBaseArchive();
return 0;
} // MojoSetup_testLaunchBrowserCode
#endif
#if TEST_ARCHIVE_CODE
int MojoSetup_testArchiveCode(int argc, char **argv)
{
int i;
printf("Testing archiver code...\n\n");
for (i = 1; i < argc; i++)
{
MojoArchive *archive = MojoArchive_newFromDirectory(argv[i]);
if (archive != NULL)
printf("directory '%s'...\n", argv[i]);
else
{
MojoInput *io = MojoInput_newFromFile(argv[i]);
if (io != NULL)
{
archive = MojoArchive_newFromInput(io, argv[i]);
if (archive != NULL)
printf("archive '%s'...\n", argv[i]);
} // if
} // else
if (archive == NULL)
fprintf(stderr, "Couldn't handle '%s'\n", argv[i]);
else
{
if (!archive->enumerate(archive))
fprintf(stderr, "enumerate() failed.\n");
else
{
const MojoArchiveEntry *ent;
while ((ent = archive->enumNext(archive)) != NULL)
{
printf("%s ", ent->filename);
if (ent->type == MOJOARCHIVE_ENTRY_FILE)
{
printf("(file, %d bytes, %o)\n",
(int) ent->filesize, ent->perms);
MojoInput *input = archive->openCurrentEntry(archive);
if(&archive->prevEnum != ent)
{
fprintf(stderr, "Address of MojoArchiveEntry pointer differs\n");
exit(EXIT_FAILURE);
} // if
if(!input)
{
fprintf(stderr, "Could not open current entry\n");
exit(EXIT_FAILURE);
} // if
if(!input->ready(input))
{
input->close(input);
continue;
} // if
uint64 pos = input->tell(input);
if(0 != pos)
{
fprintf(stderr, "position has to be 0 on start, is: %d\n", (int) pos);
exit(EXIT_FAILURE);
} // if
int64 filesize = input->length(input);
if(filesize != ent->filesize)
{
fprintf(stderr, "file size mismatch %d != %d\n",
(int) filesize, (int) ent->filesize);
exit(EXIT_FAILURE);
} // if
boolean ret = input->seek(input, filesize - 1);
if(!ret)
{
fprintf(stderr, "seek() has to return 'true'.\n");
exit(EXIT_FAILURE);
} // if
ret = input->seek(input, filesize);
if(ret)
{
fprintf(stderr, "seek() has to return 'false'.\n");
exit(EXIT_FAILURE);
} // if
pos = input->tell(input);
if(filesize -1 != pos)
{
fprintf(stderr, "position should be %d after seek(), is: %d\n",
(int) filesize - 1, (int) pos);
exit(EXIT_FAILURE);
} // if
input->close(input);
} // if
else if (ent->type == MOJOARCHIVE_ENTRY_DIR)
printf("(dir, %o)\n", ent->perms);
else if (ent->type == MOJOARCHIVE_ENTRY_SYMLINK)
printf("(symlink -> '%s')\n", ent->linkdest);
else
{
printf("(UNKNOWN?!, %d bytes, -> '%s', %o)\n",
(int) ent->filesize, ent->linkdest,
ent->perms);
} // else
} // while
} // else
archive->close(archive);
printf("\n\n");
} // else
} // for
return 0;
} // MojoSetup_testArchiveCode
#endif
#if TEST_NETWORK_CODE
int MojoSetup_testNetworkCode(int argc, char **argv)
{
int i;
fprintf(stderr, "Testing networking code...\n\n");
for (i = 1; i < argc; i++)
{
static char buf[64 * 1024];
uint32 start = 0;
const char *url = argv[i];
int64 length = -1;
int64 total_br = 0;
int64 br = 0;
printf("\n\nFetching '%s' ...\n", url);
MojoInput *io = MojoInput_newFromURL(url);
if (io == NULL)
{
fprintf(stderr, "failed!\n");
continue;
} // if
start = MojoPlatform_ticks();
while (!io->ready(io))
MojoPlatform_sleep(10);
fprintf(stderr, "took about %d ticks to get started\n",
(int) (MojoPlatform_ticks() - start));
length = io->length(io);
fprintf(stderr, "Ready to read (%lld) bytes.\n",
(long long) length);
do
{
start = MojoPlatform_ticks();
if (!io->ready(io))
{
fprintf(stderr, "Not ready!\n");
while (!io->ready(io))
MojoPlatform_sleep(10);
fprintf(stderr, "took about %d ticks to get ready\n",
(int) (MojoPlatform_ticks() - start));
} // if
start = MojoPlatform_ticks();
br = io->read(io, buf, sizeof (buf));
fprintf(stderr, "read blocked for about %d ticks\n",
(int) (MojoPlatform_ticks() - start));
if (br > 0)
{
total_br += br;
fprintf(stderr, "read %lld bytes\n", (long long) br);
fwrite(buf, br, 1, stdout);
} // if
} while (br > 0);
if (br < 0)
fprintf(stderr, "ERROR IN TRANSMISSION.\n\n");
else
{
fprintf(stderr, "TRANSMISSION COMPLETE!\n\n");
fprintf(stderr, "(Read %lld bytes, expected %lld.)\n",
(long long) total_br, length);
} // else
io->close(io);
} // for
return 0;
} // MojoSetup_testNetworkCode
#endif
// end of mojosetup.c ...