r400 - in trunk: . scripts
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Sat Jan 12 05:48:34 EST 2008
Author: icculus
Date: 2008-01-12 05:48:34 -0500 (Sat, 12 Jan 2008)
New Revision: 400
Modified:
trunk/docs.txt
trunk/fileio.c
trunk/gui.c
trunk/gui_gtkplus2.c
trunk/gui_ncurses.c
trunk/gui_stdio.c
trunk/gui_www.c
trunk/lua_glue.c
trunk/mojosetup.c
trunk/platform_unix.c
trunk/scripts/localization.lua
trunk/universal.h
Log:
Reworked string formatting, so you can reorder formatting args by localization,
dynamically allocate the formatted string, and not depend so much on the C
runtime.
Modified: trunk/docs.txt
===================================================================
--- trunk/docs.txt 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/docs.txt 2008-01-12 10:48:34 UTC (rev 400)
@@ -740,7 +740,16 @@
the basic config schema. Everything else is free game. Here are the globals
that MojoSetup provides:
+ MojoSetup.format(fmt, ...)
+ Format a string, sort of (but not exactly!) like sprintf().
+ The only formatters accepted are %0 through %9 (and %%), which do not
+ have to appear in order in the string, but match the varargs in the
+ order they are passed to the function.
+
+ format('%1 %0 %1 %%', 'X', 'Y', 'Z') will return the string: 'Y X Y %'
+
+
MojoSetup.fatal(errstr)
Display (errstr) to the end user and stop the installation. The installer
Modified: trunk/fileio.c
===================================================================
--- trunk/fileio.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/fileio.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -570,7 +570,6 @@
#if !SUPPORT_URL_HTTP && !SUPPORT_URL_FTP
MojoInput *MojoInput_fromURL(const char *url)
{
- // !!! FIXME: localization.
logError(_("No networking support in this build."));
return NULL;
} // MojoInput_fromURL
Modified: trunk/gui.c
===================================================================
--- trunk/gui.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/gui.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -73,7 +73,7 @@
{
if ( (i->priority == p) && (i->gui->init()) )
{
- logInfo("Selected '%s' UI.", i->gui->name());
+ logInfo("Selected '%0' UI.", i->gui->name());
return i;
} // if
} // for
Modified: trunk/gui_gtkplus2.c
===================================================================
--- trunk/gui_gtkplus2.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/gui_gtkplus2.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -781,14 +781,15 @@
gint rc = 0;
// !!! FIXME: Use stock GTK icon for "media"?
// !!! FIXME: better text.
- const char *title = entry->_("Media change");
+ const char *title = entry->xstrdup(entry->_("Media change"));
// !!! FIXME: better text.
- const char *fmt = entry->_("Please insert '%s'");
- size_t len = strlen(fmt) + strlen(medianame) + 1;
- char *text = (char *) entry->xmalloc(len);
- snprintf(text, len, fmt, medianame);
+ const char *fmt = entry->xstrdup(entry->_("Please insert '%0'"));
+ const char *text = entry->format(fmt, medianame);
rc = do_msgbox(title, text, GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, NULL);
+ free((void *) text);
+ free((void *) fmt);
+ free((void *) title);
return (rc == GTK_RESPONSE_OK);
} // MojoGui_gtkplus2_insertmedia
Modified: trunk/gui_ncurses.c
===================================================================
--- trunk/gui_ncurses.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/gui_ncurses.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -1285,25 +1285,22 @@
static boolean MojoGui_ncurses_insertmedia(const char *medianame)
{
- char *fmt = entry->xstrdup(entry->_("Please insert '%s'"));
- const size_t len = strlen(fmt) + strlen(medianame) + 1;
- char *text = (char *) entry->xmalloc(len);
+ char *fmt = entry->xstrdup(entry->_("Please insert '%0'"));
+ char *text = entry->format(fmt, medianame);
char *localized_ok = entry->xstrdup(entry->_("Ok"));
char *localized_cancel = entry->xstrdup(entry->_("Cancel"));
char *buttons[] = { localized_ok, localized_cancel };
MojoBox *mojobox = NULL;
int rc = 0;
- snprintf(text, len, fmt, medianame);
- free(fmt);
-
mojobox = makeBox(entry->_("Media change"), text, buttons, 2, false, true);
while ((rc = upkeepBox(&mojobox, wgetch(mojobox->mainwin))) == -1) {}
freeBox(mojobox, true);
- free(text);
free(localized_cancel);
free(localized_ok);
+ free(text);
+ free(fmt);
return (rc == 0);
} // MojoGui_ncurses_insertmedia
Modified: trunk/gui_stdio.c
===================================================================
--- trunk/gui_stdio.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/gui_stdio.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -46,16 +46,18 @@
// !!! FIXME: abort in read_stdin() if i/o fails?
int retval = 0;
- const char *backstr = NULL;
+ char *backstr = (back) ? entry->xstrdup(entry->_("back")) : NULL;
if (prompt != NULL)
printf("%s\n", prompt);
if (back)
{
- backstr = entry->xstrdup(entry->_("back"));
- printf(entry->_("Type '%s' to go back."), backstr);
- printf("\n");
+ char *fmt = entry->xstrdup(entry->_("Type '%0' to go back."));
+ char *msg = entry->format(fmt, backstr);
+ printf("%s\n", msg);
+ free(msg);
+ free(fmt);
} // if
if (fwd)
@@ -73,7 +75,7 @@
retval = -1;
} // if
- free((void *) backstr);
+ free(backstr);
return retval;
} // readstr
@@ -111,7 +113,11 @@
static void MojoGui_stdio_msgbox(const char *title, const char *text)
{
char buf[128];
- printf(entry->_("NOTICE: %s\n[hit enter]"), text);
+ char *fmt = entry->xstrdup(entry->_("NOTICE: %0\n[hit enter]"));
+ char *msg = entry->format(fmt, text);
+ printf("%s\n", msg);
+ free(msg);
+ free(fmt);
fflush(stdout);
read_stdin(buf, sizeof (buf));
} // MojoGui_stdio_msgbox
@@ -123,10 +129,11 @@
boolean retval = false;
if (!feof(stdin))
{
- const char *fmt = ((defval) ? "%s\n[Y/n]: " : "%s\n[y/N]: ");
- const char *localized_fmt = entry->xstrdup(entry->_(fmt));
- const char *localized_no = entry->xstrdup(entry->_("N"));
- const char *localized_yes = entry->xstrdup(entry->_("Y"));
+ const char *_fmt = ((defval) ? "%1\n[Y/n]: " : "%1\n[y/N]: ");
+ char *fmt = entry->xstrdup(entry->_(_fmt));
+ char *msg = entry->format(fmt, text);
+ char *localized_no = entry->xstrdup(entry->_("N"));
+ char *localized_yes = entry->xstrdup(entry->_("Y"));
boolean getout = false;
char buf[128];
@@ -135,7 +142,7 @@
int rc = 0;
getout = true; // we may reset this later.
- printf(localized_fmt, text);
+ printf("%s\n", msg);
fflush(stdout);
rc = read_stdin(buf, sizeof (buf));
@@ -151,9 +158,10 @@
getout = false; // try again.
} // while
- free((void *) localized_yes);
- free((void *) localized_no);
- free((void *) localized_fmt);
+ free(localized_yes);
+ free(localized_no);
+ free(msg);
+ free(fmt);
} // if
return retval;
@@ -166,11 +174,12 @@
MojoGuiYNAN retval = MOJOGUI_NO;
if (!feof(stdin))
{
- const char *localized_fmt = entry->_("%s\n[y/n/Always/Never]: ");
- const char *localized_no = entry->xstrdup(entry->_("N"));
- const char *localized_yes = entry->xstrdup(entry->_("Y"));
- const char *localized_always = entry->xstrdup(entry->_("Always"));
- const char *localized_never = entry->xstrdup(entry->_("Never"));
+ char *fmt = entry->xstrdup(_("%0\n[y/n/Always/Never]: "));
+ char *msg = entry->format(fmt, txt);
+ char *localized_no = entry->xstrdup(entry->_("N"));
+ char *localized_yes = entry->xstrdup(entry->_("Y"));
+ char *localized_always = entry->xstrdup(entry->_("Always"));
+ char *localized_never = entry->xstrdup(entry->_("Never"));
boolean getout = false;
char buf[128];
@@ -179,7 +188,7 @@
int rc = 0;
getout = true; // we may reset this later.
- printf(localized_fmt, txt);
+ printf("%s\n", msg);
fflush(stdout);
rc = read_stdin(buf, sizeof (buf));
@@ -199,11 +208,12 @@
getout = false; // try again.
} // while
- free((void *) localized_never);
- free((void *) localized_always);
- free((void *) localized_yes);
- free((void *) localized_no);
- free((void *) localized_fmt);
+ free(localized_never);
+ free(localized_always);
+ free(localized_yes);
+ free(localized_no);
+ free(msg);
+ free(fmt);
} // if
return retval;
@@ -303,9 +313,7 @@
static void dumb_pager(const char *name, const char *data, size_t datalen)
{
const int MAX_PAGE_LINES = 21;
- char buf[256];
- const char *_fmt = entry->_("(%d-%d of %d lines, see more?)"); // !!! FIXME: localization
- char *fmt = entry->xstrdup(_fmt);
+ char *fmt = entry->xstrdup(entry->_("(%0-%1 of %2 lines, see more?)"));
int i = 0;
int w = 0;
int linecount = 0;
@@ -330,10 +338,13 @@
getout = true;
else
{
+ char *msg = NULL;
printf("\n");
- snprintf(buf, sizeof (buf), fmt,
- (printed-i)+1, printed, linecount);
- getout = !MojoGui_stdio_promptyn("", buf, true);
+ msg = entry->format(fmt, entry->numstr((printed-i)+1),
+ entry->numstr(printed),
+ entry->numstr(linecount));
+ getout = !MojoGui_stdio_promptyn("", msg, true);
+ free(msg);
printf("\n");
} // else
} while (!getout);
@@ -575,8 +586,11 @@
static boolean MojoGui_stdio_insertmedia(const char *medianame)
{
char buf[32];
- printf(entry->_("Please insert '%s'"), medianame);
- printf("\n");
+ char *fmt = entry->xstrdup(entry->_("Please insert '%0'"));
+ char *msg = entry->format(fmt, medianame);
+ printf("%s\n", msg);
+ free(msg);
+ free(fmt);
return (readstr(NULL, buf, sizeof (buf), false, true) >= 0);
} // MojoGui_stdio_insertmedia
@@ -601,12 +615,20 @@
// limit update spam... will only write every one second, tops.
if (percentTicks <= now)
{
+ char *fmt = NULL;
+ char *msg = NULL;
percentTicks = now + 1000;
// !!! FIXME: localization.
if (percent < 0)
- printf(entry->_("%s\n"), item);
+ printf("%s\n", item);
else
- printf(entry->_("%s (total progress: %d%%)\n"), item, percent);
+ {
+ fmt = entry->xstrdup(entry->_("%0 (total progress: %1%%)\n"));
+ msg = entry->format(fmt, item, entry->numstr(percent));
+ printf("%s\n", msg);
+ free(msg);
+ free(fmt);
+ } // else
} // if
return true;
Modified: trunk/gui_www.c
===================================================================
--- trunk/gui_www.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/gui_www.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -52,18 +52,19 @@
int rc = WSAStartup(MAKEWORD(1, 1), &data);
if (rc != 0)
{
- entry->logError("www: WSAStartup() failed: %s", sockStrErrVal(rc));
+ entry->logError("www: WSAStartup() failed: %0", sockStrErrVal(rc));
return false;
} // if
- entry->logInfo("www: WinSock initialized (want %d.%d, got %d.%d).",
- (int) (LOBYTE(data.wVersion)),
- (int) (HIBYTE(data.wVersion)),
- (int) (LOBYTE(data.wHighVersion)),
- (int) (HIBYTE(data.wHighVersion)));
- entry->logInfo("www: WinSock description: %s", data.szDescription);
- entry->logInfo("www: WinSock system status: %s", data.szSystemStatus);
- entry->logInfo("www: WinSock max sockets: %s", (int) data.iMaxSockets);
+ entry->logInfo("www: WinSock initialized (want %0.%1, got %2.%3).",
+ entry->numstr((int) (LOBYTE(data.wVersion))),
+ entry->numstr((int) (HIBYTE(data.wVersion))),
+ entry->numstr((int) (LOBYTE(data.wHighVersion))),
+ entry->numstr((int) (HIBYTE(data.wHighVersion))));
+ entry->logInfo("www: WinSock description: %0", data.szDescription);
+ entry->logInfo("www: WinSock system status: %0", data.szSystemStatus);
+ entry->logInfo("www: WinSock max sockets: %0",
+ entry->numstr((int) data.iMaxSockets));
return true;
} // initSocketSupport
@@ -158,7 +159,7 @@
req->value = entry->xstrdup(val);
req->next = webRequest;
webRequest = req;
- entry->logDebug("www: request element '%s' = '%s'", key, val);
+ entry->logDebug("www: request element '%0' = '%1'", key, val);
} // if
} // addWebRequest
@@ -308,7 +309,7 @@
const int err = sockErrno();
if (!intrError(err))
{
- entry->logError("www: send() failed: %s", sockStrErrVal(err));
+ entry->logError("www: send() failed: %0", sockStrErrVal(err));
break;
} // if
} // else
@@ -456,7 +457,7 @@
assert(!blocking);
else
{
- entry->logError("www: accept() failed: %s", sockStrErrVal(err));
+ entry->logError("www: accept() failed: %0", sockStrErrVal(err));
closesocket(listenSocket); // make all future i/o fail too.
listenSocket = INVALID_SOCKET;
} // else
@@ -474,7 +475,7 @@
const int err = sockErrno();
if (!intrError(err)) // just try again on interrupt.
{
- entry->logError("www: recv() failed: %s", sockStrErrVal(err));
+ entry->logError("www: recv() failed: %0", sockStrErrVal(err));
FREE_AND_NULL(reqstr);
closesocket(s);
s = INVALID_SOCKET;
@@ -519,7 +520,7 @@
memmove(reqstr, ptr, len+1);
} // else
- entry->logDebug("www: request '%s'", get);
+ entry->logDebug("www: request '%0'", get);
// okay, now (get) and (reqptr) are separate strings.
// These parse*() functions update (webRequest).
@@ -554,7 +555,7 @@
s = socket(PF_INET, SOCK_STREAM, protocol);
if (s == INVALID_SOCKET)
- entry->logInfo("www: socket() failed ('%s')", sockStrError());
+ entry->logInfo("www: socket() failed ('%0')", sockStrError());
else
{
boolean success = false;
@@ -572,12 +573,13 @@
#endif
if (bind(s, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
- entry->logError("www: bind() failed ('%s')", sockStrError());
+ entry->logError("www: bind() failed ('%0')", sockStrError());
else if (listen(s, 5) == SOCKET_ERROR)
- entry->logError("www: listen() failed ('%s')", sockStrError());
+ entry->logError("www: listen() failed ('%0')", sockStrError());
else
{
- entry->logInfo("www: socket created on port %d", (int) portnum);
+ entry->logInfo("www: socket created on port %0",
+ entry->numstr(portnum));
success = true;
} // else
@@ -987,10 +989,12 @@
htmlescape(entry->_("OK")),
};
- // !!! FIXME: better text.
char *title = entry->xstrdup(entry->_("Media change"));
- // !!! FIXME: better text.
- strAdd(&text, &len, &alloc, entry->_("Please insert '%s'"), medianame);
+ char *fmt = entry->xstrdup(entry->_("Please insert '%0'"));
+ char *msg = entry->format(fmt, medianame);
+ strAdd(&text, &len, &alloc, msg);
+ free(msg);
+ free(fmt);
htmltext = htmlescape(text);
free(text);
Modified: trunk/lua_glue.c
===================================================================
--- trunk/lua_glue.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/lua_glue.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -215,7 +215,7 @@
int i = 0;
if (errstr != NULL)
- logDebug("%s\n", errstr);
+ logDebug("%0", errstr);
logDebug("Lua stack backtrace:");
@@ -233,7 +233,7 @@
if (!lua_getinfo(L, "nSl", &ldbg))
{
snprintfcat(&ptr, &len, "???\n");
- logDebug((const char *) scratchbuf_128k);
+ logDebug("%0", (const char *) scratchbuf_128k);
continue;
} // if
@@ -252,7 +252,7 @@
snprintfcat(&ptr, &len, "unidentifiable function");
} // if
- logDebug((const char *) scratchbuf_128k);
+ logDebug("%0", (const char *) scratchbuf_128k);
ptr = (char *) scratchbuf_128k;
len = sizeof (scratchbuf_128k);
@@ -271,7 +271,7 @@
if (ldbg.currentline != -1)
snprintfcat(&ptr, &len, ":%d", ldbg.currentline);
} // else
- logDebug((const char *) scratchbuf_128k);
+ logDebug("%0", (const char *) scratchbuf_128k);
} // for
return retvalString(L, errstr ? errstr : "");
@@ -446,12 +446,13 @@
int post = 0;
pre = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
- logDebug("Collecting garbage (currently using %d bytes).", pre);
+ logDebug("Collecting garbage (currently using %0 bytes).", numstr(pre));
ticks = MojoPlatform_ticks();
lua_gc (L, LUA_GCCOLLECT, 0);
profile("Garbage collection", ticks);
post = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
- logDebug("Now using %d bytes (%d bytes savings).\n", post, pre - post);
+ logDebug("Now using %0 bytes (%1 bytes savings).",
+ numstr(post), numstr(pre - post));
} // MojoLua_collectGarbage
@@ -499,6 +500,37 @@
} // translate
+// Lua interface to format().
+static int luahook_format(lua_State *L)
+{
+ const int argc = lua_gettop(L);
+ const char *fmt = luaL_checkstring(L, 1);
+ char *formatted = NULL;
+ char *s[10];
+ int i;
+
+ assert(argc <= 11); // fmt, plus %0 through %9.
+
+ for (i = 0; i < STATICARRAYLEN(s); i++)
+ {
+ const char *str = NULL;
+ if ((i+2) <= argc)
+ str = lua_tostring(L, i+2);
+ s[i] = (str == NULL) ? NULL : xstrdup(str);
+ } // for
+
+ // I think this is legal (but probably not moral) C code.
+ formatted = format(fmt,s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9]);
+
+ for (i = 0; i < STATICARRAYLEN(s); i++)
+ free(s[i]);
+
+ lua_pushstring(L, formatted);
+ free(formatted);
+ return 1;
+} // luahook_format
+
+
// Use this instead of Lua's error() function if you don't have a
// programatic error, so you don't get stack callback stuff:
// MojoSetup.fatal("You need the base game to install this expansion pack.")
@@ -507,7 +539,9 @@
static int luahook_fatal(lua_State *L)
{
const char *errstr = lua_tostring(L, 1);
- return fatal(errstr); // doesn't actually return.
+ if (errstr == NULL)
+ return fatal(NULL); // doesn't actually return.
+ return fatal("%0", errstr); // doesn't actually return.
} // luahook_fatal
@@ -589,28 +623,28 @@
static int luahook_logwarning(lua_State *L)
{
- logWarning(luaL_checkstring(L, 1));
+ logWarning("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logwarning
static int luahook_logerror(lua_State *L)
{
- logError(luaL_checkstring(L, 1));
+ logError("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logerror
static int luahook_loginfo(lua_State *L)
{
- logInfo(luaL_checkstring(L, 1));
+ logInfo("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_loginfo
static int luahook_logdebug(lua_State *L)
{
- logDebug(luaL_checkstring(L, 1));
+ logDebug("%0", luaL_checkstring(L, 1));
return 0;
} // luahook_logdebug
@@ -729,7 +763,7 @@
const char *permstr = luaL_checkstring(L, 3);
perms = MojoPlatform_makePermissions(permstr, &valid);
if (!valid)
- fatal(_("BUG: '%s' is not a valid permission string"), permstr);
+ fatal(_("BUG: '%0' is not a valid permission string"), permstr);
} // if
rc = MojoInput_toPhysicalFile(in, path, perms, &sums,
writeCallback, L);
@@ -912,7 +946,7 @@
const char *permstr = luaL_checkstring(L, 2);
perms = MojoPlatform_makePermissions(permstr, &valid);
if (!valid)
- fatal(_("BUG: '%s' is not a valid permission string"), permstr);
+ fatal(_("BUG: '%0' is not a valid permission string"), permstr);
} // if
return retvalBoolean(L, MojoPlatform_mkdir(dir, perms));
} // luahook_platform_mkdir
@@ -1041,7 +1075,7 @@
const boolean can_go_fwd = canGoForward(thisstage, maxstage);
if (data == NULL)
- fatal(_("failed to load file '%s'"), fname);
+ fatal(_("failed to load file '%0'"), fname);
lua_pushnumber(L, GGui->readme(name, data, len, can_go_back, can_go_fwd));
free((void *) data);
@@ -1088,7 +1122,7 @@
if (required)
{
lua_getfield(L, -2, "description");
- logWarning("Option '%s' is both required and disabled!",
+ logWarning("Option '%0' is both required and disabled!",
lua_tostring(L, -1));
lua_pop(L, 1);
} // if
@@ -1166,7 +1200,7 @@
// !!! FIXME: schema should check?
if ((is_group) && (opts->is_group_parent))
{
- fatal("OptionGroup '%s' inside OptionGroup '%s'.",
+ fatal("OptionGroup '%0' inside OptionGroup '%1'.",
opts->description, parent->description);
} // if
@@ -1174,7 +1208,7 @@
{
if (seen_enabled)
{
- logWarning("Options '%s' and '%s' are both enabled in group '%s'.",
+ logWarning("Options '%0' and '%1' are both enabled in group '%2'.",
seen_enabled->description, opts->description,
parent->description);
seen_enabled->value = false;
@@ -1191,7 +1225,7 @@
if ((prev) && (is_group) && (!seen_enabled))
{
- logWarning("Option group '%s' has no enabled items, choosing first ('%s').",
+ logWarning("Option group '%0' has no enabled items, choosing first ('%1').",
parent->description, prev->description);
prev->value = true;
} // if
@@ -1460,6 +1494,7 @@
set_cfunc(luaState, luahook_runfile, "runfile");
set_cfunc(luaState, luahook_translate, "translate");
set_cfunc(luaState, luahook_ticks, "ticks");
+ set_cfunc(luaState, luahook_format, "format");
set_cfunc(luaState, luahook_fatal, "fatal");
set_cfunc(luaState, luahook_msgbox, "msgbox");
set_cfunc(luaState, luahook_promptyn, "promptyn");
Modified: trunk/mojosetup.c
===================================================================
--- trunk/mojosetup.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/mojosetup.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -33,6 +33,8 @@
logError,
logInfo,
logDebug,
+ format,
+ numstr,
MojoPlatform_ticks,
};
@@ -74,7 +76,7 @@
io->close(io);
if (br == imglen)
{
- logInfo("Switching binary with '%s'...", ar->prevEnum.filename);
+ logInfo("Switching binary with '%0'...", ar->prevEnum.filename);
MojoPlatform_switchBin(img, imglen); // no return on success.
logError("...Switch binary failed.");
} // if
@@ -329,6 +331,104 @@
} // 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
@@ -380,21 +480,22 @@
{
if (level <= MojoLog_logLevel)
{
- char buf[1024];
+ char *str = format_internal(fmt, ap);
//int len = vsnprintf(buf + 2, sizeof (buf) - 2, fmt, ap) + 2;
//buf[0] = levelchar;
//buf[1] = ' ';
- int len = vsnprintf(buf, sizeof (buf), fmt, ap);
- while ( (--len >= 0) && ((buf[len] == '\n') || (buf[len] == '\r')) ) {}
- buf[len+1] = '\0'; // delete trailing newline crap.
- MojoPlatform_log(buf);
+ 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, buf, strlen(buf));
+ MojoPlatform_write(logFile, str, strlen(str));
MojoPlatform_write(logFile, endl, strlen(endl));
MojoPlatform_flush(logFile);
} // if
+ free(str);
} // if
} // addLog
@@ -439,9 +540,7 @@
{
uint32 retval = MojoPlatform_ticks() - start_time;
if (what != NULL)
- {
- logDebug("%s took %lu ms.", what, (unsigned long) retval);
- } // if
+ logDebug("%0 took %1 ms.", what, numstr((int) retval));
return retval;
} // profile_start
@@ -458,24 +557,13 @@
// may not want to show a message, since we displayed one elsewhere, etc.
if (fmt != NULL)
{
+ char *buf = NULL;
va_list ap;
- int rc = 0;
- int len = 128;
- char *buf = xmalloc(len);
-
va_start(ap, fmt);
- rc = vsnprintf(buf, len, fmt, ap);
+ buf = format_internal(fmt, ap);
va_end(ap);
- if (rc >= len)
- {
- len = rc;
- buf = xrealloc(buf, len);
- va_start(ap, fmt);
- vsnprintf(buf, len, fmt, ap);
- va_end(ap);
- } // if
- logError("FATAL: %s", buf);
+ logError("FATAL: %0", buf);
if (GGui != NULL)
GGui->msgbox(_("Fatal error"), buf);
@@ -502,7 +590,7 @@
panic_runs++;
if (panic_runs == 1)
{
- logError("PANIC: %s", err);
+ logError("PANIC: %0", err);
panic(err);
} // if
@@ -583,7 +671,7 @@
#if MOJOSETUP_INTERNAL_BZLIB && BZ_NO_STDIO
void bz_internal_error(int errcode)
{
- fatal(_("bzlib triggered an internal error: %d"), errcode);
+ fatal(_("bzlib triggered an internal error: %0"), numstr(numbuf));
} // bz_internal_error
#endif
Modified: trunk/platform_unix.c
===================================================================
--- trunk/platform_unix.c 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/platform_unix.c 2008-01-12 10:48:34 UTC (rev 400)
@@ -1031,7 +1031,7 @@
if (first_shot)
{
first_shot = false;
- logError("Caught signal #%d", sig);
+ logError("Caught signal #%0", numstr(sig));
} // if
} // signal_catcher
Modified: trunk/scripts/localization.lua
===================================================================
--- trunk/scripts/localization.lua 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/scripts/localization.lua 2008-01-12 10:48:34 UTC (rev 400)
@@ -17,6 +17,17 @@
-- You should leave the existing strings here. They aren't hurting anything,
-- and most are used by MojoSetup itself. Add your own, though.
--
+-- Whenever you see a %x sequence, that is replaced with a string at runtime.
+-- So if you see, ["Hello, %0, my name is %1."], then this might become
+-- "Hello, Alice, my name is Bob." at runtime. If your culture would find
+-- introducing yourself second to be rude, you might translate this to:
+-- "My name is %1, hello %0." If you need a literal '%' char, write "%%":
+-- "Operation is %0%% complete" might give "Operation is 3% complete."
+-- All strings, from your locale or otherwise, are checked for formatter
+-- correctness at startup. This is to prevent the installer working fine
+-- in all reasonable tests, then finding out that one guy in Ghana has a
+-- crashing installer because his localization forgot to add a %1 somewhere.
+--
-- The table you create here goes away shortly after creation, as the relevant
-- parts of it get moved somewhere else. You should call MojoSetup.translate()
-- to get the proper translation for a given string.
@@ -42,19 +53,19 @@
};
-- stdio GUI plugin says this for msgboxes (printf format string).
- ["NOTICE: %s\n[hit enter]"] = {
+ ["NOTICE: %1\n[hit enter]"] = {
};
-- stdio GUI plugin says this for yes/no prompts that default to yes (printf format string).
- ["%s\n[Y/n]: "] = {
+ ["%1\n[Y/n]: "] = {
};
-- stdio GUI plugin says this for yes/no prompts that default to no (printf format string).
- ["%s\n[y/N]: "] = {
+ ["%1\n[y/N]: "] = {
};
-- stdio GUI plugin says this for yes/no/always/never prompts (printf format string).
- ["%s\n[y/n/Always/Never]: "] = {
+ ["%1\n[y/n/Always/Never]: "] = {
};
-- This is utf8casecmp()'d for "yes" answers in stdio GUI's promptyn().
@@ -66,7 +77,7 @@
};
-- This is shown when using stdio GUI's built-in README pager (printf format).
- ["(Viewing %d-%d of %d lines, see more?)"] = {
+ ["(Viewing %1-%2 of %3 lines, see more?)"] = {
};
-- This is utf8casecmp()'d for "always" answers in stdio GUI's promptyn().
@@ -95,10 +106,10 @@
["Choose number to change."] = {
};
- ["Type '%s' to go back."] = {
+ ["Type '%1' to go back."] = {
};
- -- This is the string used for the '%s' in the above string.
+ -- This is the string used for the '%1' in the above string.
["back"] = {
};
@@ -142,10 +153,10 @@
["You must accept the license before you may install"] = {
};
- ["failed to load file '%s'"] = {
+ ["failed to load file '%1'"] = {
};
- ["Please insert '%s'"] = {
+ ["Please insert '%1'"] = {
};
["(I want to specify a path.)"] = {
@@ -161,6 +172,9 @@
["Setup program is shutting down. You can close this browser now."] = {
};
+
+ ["No networking support in this build."] = {
+ };
};
-- end of localization.lua ...
Modified: trunk/universal.h
===================================================================
--- trunk/universal.h 2008-01-12 10:16:04 UTC (rev 399)
+++ trunk/universal.h 2008-01-12 10:48:34 UTC (rev 400)
@@ -96,6 +96,23 @@
// Static, non-stack memory for scratch work...not thread safe!
extern uint8 scratchbuf_128k[128 * 1024];
+// Format a string, sort of (but not exactly!) like sprintf().
+// The only formatters accepted are %0 through %9 (and %%), which do not
+// have to appear in order in the string, but match the varargs passed to the
+// function. Only strings are accepted for varargs. This function allocates
+// memory as necessary, so you need to free() the result, but don't need to
+// preallocate a buffer and be concerned about overflowing it.
+// This does not use scratchbuf_128k.
+char *format(const char *fmt, ...);
+
+// Convert an int to a string. This uses incremental pieces of
+// scratchbuf_128k for a buffer to store the results, and
+// will overwrite itself after some number of calls when the memory
+// is all used, but note that other things use scratchbuf_128k too,
+// so this is only good for printf() things:
+// fmtfunc("mission took %0 seconds, %1 points", numstr(secs), numstr(pts));
+const char *numstr(int val);
+
// Call this for fatal errors that require immediate app termination.
// Does not clean up, or translate the error string. Try to avoid this.
// These are for crucial lowlevel issues that preclude any meaningful
@@ -112,7 +129,9 @@
// If there's no GUI or Lua isn't initialized, this calls panic(). That's bad.
// Doesn't return, but if it did, you can assume it returns zero, so you can
// do: 'return fatal("missing config file");' or whatnot.
-int fatal(const char *fmt, ...) ISPRINTF(1,2);
+// THIS DOES NOT USE PRINTF-STYLE FORMAT CODES. Please see the comments for
+// format() for details.
+int fatal(const char *fmt, ...);
// The platform layer should set up signal/exception handlers before calling
// MojoSetup_main(), that will call these functions. "crashed" for bug
@@ -121,9 +140,6 @@
void MojoSetup_crashed(void);
void MojoSetup_terminated(void);
-// Call this to pop up a warning dialog box and block until user hits OK.
-void warn(const char *fmt, ...) ISPRINTF(1,2);
-
// Malloc replacements that blow up on allocation failure.
// Please note that xmalloc() will zero the newly-allocated memory buffer,
// like calloc() would, but xrealloc() makes no such promise!
@@ -253,12 +269,16 @@
extern MojoSetupLogLevel MojoLog_logLevel;
void MojoLog_initLogging(void);
void MojoLog_deinitLogging(void);
-void logWarning(const char *fmt, ...) ISPRINTF(1,2);
-void logError(const char *fmt, ...) ISPRINTF(1,2);
-void logInfo(const char *fmt, ...) ISPRINTF(1,2);
-void logDebug(const char *fmt, ...) ISPRINTF(1,2);
+// Logging facilities.
+// THESE DO NOT USE PRINTF-STYLE FORMAT CODES. Please see the comments for
+// format() for details.
+void logWarning(const char *fmt, ...);
+void logError(const char *fmt, ...);
+void logInfo(const char *fmt, ...);
+void logDebug(const char *fmt, ...);
+
// Checksums.
typedef uint32 MojoCrc32;
@@ -320,10 +340,12 @@
char *(*xstrdup)(const char *str);
char *(*xstrncpy)(char *dst, const char *src, size_t len);
const char *(*translate)(const char *str);
- void (*logWarning)(const char *fmt, ...) ISPRINTF(1,2);
- void (*logError)(const char *fmt, ...) ISPRINTF(1,2);
- void (*logInfo)(const char *fmt, ...) ISPRINTF(1,2);
- void (*logDebug)(const char *fmt, ...) ISPRINTF(1,2);
+ void (*logWarning)(const char *fmt, ...);
+ void (*logError)(const char *fmt, ...);
+ void (*logInfo)(const char *fmt, ...);
+ void (*logDebug)(const char *fmt, ...);
+ char *(*format)(const char *fmt, ...);
+ const char *(*numstr)(int val);
uint32 (*ticks)(void);
} MojoSetupEntryPoints;
extern MojoSetupEntryPoints GEntryPoints;
@@ -339,6 +361,9 @@
#endif
#endif // DOXYGEN_SHOULD_IGNORE_THIS
+#define DEFINE_TO_STR2(x) #x
+#define DEFINE_TO_STR(x) DEFINE_TO_STR2(x)
+
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
#define STUBBED(x) \
do { \
@@ -346,17 +371,14 @@
if (!seen_this) \
{ \
seen_this = true; \
- logDebug("STUBBED: %s at %s (%s:%d)\n", x, __FUNCTION__, \
- __FILE__, __LINE__); \
+ logDebug("STUBBED: %0 at %1 (%2:%3)\n", x, __FUNCTION__, \
+ __FILE__, DEFINE_TO_STR(__LINE__)); \
} \
} while (false)
#endif
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
-#define DEFINE_TO_STR2(x) #x
-#define DEFINE_TO_STR(x) DEFINE_TO_STR2(x)
-
#ifdef __cplusplus
}
#endif
More information about the mojosetup-commits
mailing list