r323 - in trunk: . scripts

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Fri Jun 1 06:33:34 EDT 2007


Author: icculus
Date: 2007-06-01 06:33:34 -0400 (Fri, 01 Jun 2007)
New Revision: 323

Added:
   trunk/gui_www.c
Modified:
   trunk/CMakeLists.txt
   trunk/gui.c
   trunk/gui.h
   trunk/scripts/localization.lua
Log:
Initial work on 'www' UI. Incomplete!


Modified: trunk/CMakeLists.txt
===================================================================
--- trunk/CMakeLists.txt	2007-06-01 10:33:05 UTC (rev 322)
+++ trunk/CMakeLists.txt	2007-06-01 10:33:34 UTC (rev 323)
@@ -374,7 +374,20 @@
 ENDIF(NOT BEOS)
 ENDIF(UNIX)
 
+# BINARY SIZE += !!! FIXME: check this.
+OPTION(MOJOSETUP_GUI_WWW "Enable www GUI" TRUE)
+IF(MOJOSETUP_GUI_WWW)
+    ADD_DEFINITIONS(-DSUPPORT_GUI_WWW=1)
+    OPTION(MOJOSETUP_GUI_WWW_STATIC "Statically link www GUI" TRUE)
+    IF(MOJOSETUP_GUI_WWW_STATIC)
+        ADD_DEFINITIONS(-DGUI_STATIC_LINK_WWW=1)
+        SET(OPTIONAL_SRCS ${OPTIONAL_SRCS} gui_www.c)
+    ELSE(MOJOSETUP_GUI_WWW_STATIC)
+        ADD_LIBRARY(mojosetupgui_www SHARED gui_www.c)
+    ENDIF(MOJOSETUP_GUI_WWW_STATIC)
+ENDIF(MOJOSETUP_GUI_WWW)
 
+
 # Archivers...
 
 # BINARY SIZE += 8

Modified: trunk/gui.c
===================================================================
--- trunk/gui.c	2007-06-01 10:33:05 UTC (rev 322)
+++ trunk/gui.c	2007-06-01 10:33:34 UTC (rev 323)
@@ -32,6 +32,9 @@
 #if GUI_STATIC_LINK_GTKPLUS2
     MojoGuiPlugin_gtkplus2,
 #endif
+#if GUI_STATIC_LINK_WWW
+    MojoGuiPlugin_www,
+#endif
 #if GUI_STATIC_LINK_NCURSES
     MojoGuiPlugin_ncurses,
 #endif

Modified: trunk/gui.h
===================================================================
--- trunk/gui.h	2007-06-01 10:33:05 UTC (rev 322)
+++ trunk/gui.h	2007-06-01 10:33:34 UTC (rev 323)
@@ -169,6 +169,7 @@
 const MojoGui *MojoGuiPlugin_stdio(int rev, const MojoSetupEntryPoints *e);
 const MojoGui *MojoGuiPlugin_ncurses(int rev, const MojoSetupEntryPoints *e);
 const MojoGui *MojoGuiPlugin_gtkplus2(int rev, const MojoSetupEntryPoints *e);
+const MojoGui *MojoGuiPlugin_www(int rev, const MojoSetupEntryPoints *e);
 const MojoGui *MojoGuiPlugin_macosx(int rev, const MojoSetupEntryPoints *e);
 
 // !!! FIXME: Qt? KDE? Gnome? Console? Cocoa?

Added: trunk/gui_www.c
===================================================================
--- trunk/gui_www.c	                        (rev 0)
+++ trunk/gui_www.c	2007-06-01 10:33:34 UTC (rev 323)
@@ -0,0 +1,906 @@
+/**
+ * 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.
+ */
+
+#if !SUPPORT_GUI_WWW
+#error Something is wrong in the build system.
+#endif
+
+#define BUILDING_EXTERNAL_PLUGIN 1
+#include "gui.h"
+
+MOJOGUI_PLUGIN(www)
+
+#if !GUI_STATIC_LINK_WWW
+CREATE_MOJOGUI_ENTRY_POINT(www)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define FREE_AND_NULL(x) { free(x); x = NULL; }
+
+
+// tapdance between things WinSock and BSD Sockets define differently...
+#if PLATFORM_WINDOWS
+    #include <winsock.h>
+
+    typedef int socklen_t;
+
+    #define sockErrno() WSAGetLastError()
+    #define wouldBlockError(err) (err == WSAEWOULDBLOCK)
+    #define intrError(err) (err == WSAEINTR)
+
+    static inline void setBlocking(SOCKET s, boolean blocking)
+    {
+        u_long val = (blocking) ? 0 : 1;
+        ioctlsocket(s, FIONBIO, &val);
+    } // setBlocking
+
+    static const char *sockStrErrVal(int val)
+    {
+        STUBBED("Windows strerror");
+    } // sockStrErrVal
+
+
+    static boolean initSocketSupport(void)
+    {
+        WSADATA data;
+        int rc = WSAStartup(MAKEWORD(1, 1), &data);
+        if (rc != 0)
+        {
+            entry->logError("www: WSAStartup() failed: %s", 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);
+
+        return true;
+    } // initSocketSupport
+
+    #define deinitSocketSupport() WSACleanup()
+
+#else
+    #include <unistd.h>
+    #include <errno.h>
+    #include <sys/types.h>
+    #include <sys/socket.h>
+    #include <netinet/in.h>
+    #include <arpa/inet.h>
+    #include <signal.h>
+    #include <netdb.h>
+    #include <fcntl.h>
+
+    typedef int SOCKET;
+
+    #define SOCKET_ERROR (-1)
+    #define INVALID_SOCKET (-1)
+    #define closesocket(x) close(x)
+    #define sockErrno() (errno)
+    #define sockStrErrVal(val) strerror(val)
+    #define intrError(err) (err == EINTR)
+    #define initSocketSupport() (true)
+    #define deinitSocketSupport()
+
+    static inline boolean wouldBlockError(int err)
+    {
+        return ((err == EWOULDBLOCK) || (err == EAGAIN));
+    } // wouldBlockError
+
+    static void setBlocking(SOCKET s, boolean blocking)
+    {
+        int flags = fcntl(s, F_GETFL, 0);
+        if (blocking)
+            flags &= ~O_NONBLOCK;
+        else
+            flags |= O_NONBLOCK;
+        fcntl(s, F_SETFL, flags);
+    } // setBlocking
+
+#endif
+
+#define sockStrError() (sockStrErrVal(sockErrno()))
+
+
+typedef struct _S_WebRequest
+{
+    char *key;
+    char *value;
+    struct _S_WebRequest *next;
+} WebRequest;
+
+
+static char *output = NULL;
+static char *lastProgressType = NULL;
+static char *lastComponent = NULL;
+static char *baseUrl = NULL;
+static WebRequest *webRequest = NULL;
+static uint32 percentTicks = 0;
+static SOCKET listenSocket = INVALID_SOCKET;
+static SOCKET clientSocket = INVALID_SOCKET;
+
+
+static uint8 MojoGui_www_priority(void)
+{
+    return MOJOGUI_PRIORITY_TRY_LAST;
+} // MojoGui_www_priority
+
+
+static void freeWebRequest(void)
+{
+    while (webRequest)
+    {
+        WebRequest *next = webRequest->next;
+        free(webRequest->key);
+        free(webRequest->value);
+        free(webRequest);
+        webRequest = next;
+    } // while
+} // freeWebRequest
+
+
+static void addWebRequest(const char *key, const char *val)
+{
+    if ((key != NULL) && (*key != '\0'))
+    {
+        WebRequest *req = (WebRequest *) entry->xmalloc(sizeof (WebRequest));
+        req->key = entry->xstrdup(key);
+        req->value = entry->xstrdup(val);
+        req->next = webRequest;
+        webRequest = req;
+        entry->logDebug("www: request element '%s' = '%s'", key, val);
+    } // if
+} // addWebRequest
+
+
+static int hexVal(char ch)
+{
+    if ((ch >= 'a') && (ch <= 'f'))
+        return (ch - 'a') + 16;
+    else if ((ch >= 'A') && (ch <= 'F'))
+        return (ch - 'A') + 16;
+    else if ((ch >= '0') && (ch <= '9'))
+        return (ch - '0');
+    return -1;
+} // hexVal
+
+
+static void unescapeUri(char *uri)
+{
+    char *ptr = uri;
+    while ((ptr = strchr(ptr, '%')) != NULL)
+    {
+        int a, b;
+        if ((a = hexVal(ptr[1])) != -1)
+        {
+            if ((b = hexVal(ptr[2])) != -1)
+            {
+                *(ptr++) = (char) ((a * 16) + b);
+                memmove(ptr, ptr+2, strlen(ptr+1));
+            } // if
+            else
+            {
+                *(ptr++) = '?';
+                memmove(ptr, ptr+1, strlen(ptr));
+            } // else
+        } // if
+        else
+        {
+            *(ptr++) = '?';
+        } // else
+    } // while
+} // unescapeUri
+
+
+static int strAdd(char **ptr, size_t *len, size_t *alloc, const char *fmt, ...)
+{
+    int bw = 0;
+    size_t avail = *alloc - *len;
+    va_list ap;
+    va_start(ap, fmt);
+    bw = vsnprintf(*ptr + *len, avail, fmt, ap);
+    va_end(ap);
+
+    if (bw >= avail)
+    {
+        const size_t add = (*alloc + (bw + 1));  // double plus the new len.
+        *alloc += add;
+        avail += add;
+        *ptr = entry->xrealloc(*ptr, *alloc);
+        va_start(ap, fmt);
+        bw = vsnprintf(*ptr + *len, avail, fmt, ap);
+        va_end(ap);
+    } // if
+
+    *len += bw;
+    return bw;
+} // strAdd
+
+
+static char *htmlescape(const char *str)
+{
+    size_t len = 0, alloc = 0;
+    char *retval = NULL;
+    char ch;
+
+    for (ch = *str; ch != '\0'; ch = *(str++))
+    {
+        switch (ch)
+        {
+            case '&': strAdd(&retval, &len, &alloc, "&amp;"); break;
+            case '<': strAdd(&retval, &len, &alloc, "&lt;"); break;
+            case '>': strAdd(&retval, &len, &alloc, "&gt;"); break;
+            case '"': strAdd(&retval, &len, &alloc, "&quot;"); break;
+            case '\'': strAdd(&retval, &len, &alloc, "&#39;"); break;
+            default: strAdd(&retval, &len, &alloc, "%c", ch); break;
+        } // switch
+    } // while
+
+    return retval;
+} // htmlescape
+
+
+static const char *standardResponseHeaders =
+    "Content-Type: text/html; charset=utf-8\n"
+    "Accept-Ranges: none\n"
+    "Cache-Control: no-cache\n"
+    "Connection: close\n\n";
+
+
+static void setHtmlString(char **str, int responseCode,
+                          const char *responseString,
+                          const char *title, const char *html)
+{
+    size_t len = 0, alloc = 0;
+    FREE_AND_NULL(*str);
+    strAdd(str, &len, &alloc,
+              "HTTP/1.1 %d %s\n"   // responseCode, responseString
+              "%s"  // standardResponseHeaders
+              "<html>"
+               "<head>"
+                "<title>%s</title>"  // title
+               "</head>"
+               "<body>%s</body>"  // html
+              "</html>\n",
+           responseCode, responseString,
+           standardResponseHeaders,
+           title, html);
+} // setHtmlString
+
+
+static void setHtml(const char *title, const char *html)
+{
+    setHtmlString(&output, 200, "OK", title, html);
+} // setHtml
+
+
+static void sendStringAndDrop(SOCKET *_s, const char *str)
+{
+    SOCKET s = *_s;
+    int outlen = 0;
+    if (str == NULL)
+        str = "";
+    else
+        outlen = strlen(str);
+
+    setBlocking(s, true);
+
+    while (outlen > 0)
+    {
+        int rc = send(s, str, outlen, 0);
+        if (rc != SOCKET_ERROR)
+        {
+            str += rc;
+            outlen -= rc;
+        } // if
+        else
+        {
+            const int err = sockErrno();
+            if (!intrError(err))
+            {
+                entry->logError("www: send() failed: %s", sockStrErrVal(err));
+                break;
+            } // if
+        } // else
+    } // while
+
+    closesocket(s);
+    *_s = INVALID_SOCKET;
+} // sendStringAndDrop
+
+
+static void respond404(SOCKET *s)
+{
+    char *text = htmlescape(entry->_("Not Found"));
+    char *str = NULL;
+    size_t len = 0, alloc = 0;
+    char *html = NULL;
+    strAdd(&html, &len, &alloc, "<center><h1>%s</h1></center>", text);
+    setHtmlString(&str, 404, text, text, html);
+    free(html);
+    free(text);
+    sendStringAndDrop(s, str);
+    free(str);
+} // respond404
+
+
+static boolean parseGet(char *get)
+{
+    char *uri = NULL;
+    char *ver = NULL;
+
+    uri = strchr(get, ' ');
+    if (uri == NULL) return false;
+    *(uri++) = '\0';
+
+    ver = strchr(uri, ' ');
+    if (ver == NULL) return false;
+    *(ver++) = '\0';
+
+    if (strcmp(get, "GET") != 0) return false;
+    if (uri[0] != '/') return false;
+    uri++;  // skip dirsep.
+
+    // !!! FIXME: we may want to feed stock files (<img> tags, etc)
+    // !!! FIXME:  at some point in the future.
+    if ((uri[0] != '?') && (uri[0] != '\0')) return false;
+    if (strncmp(ver, "HTTP/", 5) != 0) return false;
+
+    if (*uri == '?')
+        uri++;  // skip initial argsep.
+
+    do
+    {
+        char *next = strchr(uri, '&');
+        char *val = NULL;
+        if (next != NULL)
+            *(next++) = '\0';
+
+        val = strchr(uri, '=');
+        if (val == NULL)
+            val = "";
+        else
+            *(val++) = '\0';
+
+        unescapeUri(uri);
+        unescapeUri(val);
+        addWebRequest(uri, val);
+
+        uri = next;
+    } while (uri != NULL);
+
+    return true;
+} // parseGet
+
+
+static boolean parseRequest(char *reqstr)
+{
+    do
+    {
+        char *next = strchr(reqstr, '\n');
+        char *val = NULL;
+        if (next != NULL)
+            *(next++) = '\0';
+
+        val = strchr(reqstr, ':');
+        if (val == NULL)
+            val = "";
+        else
+        {
+            *(val++) = '\0';
+            while (*val == ' ')
+                val++;
+        } // else
+
+        if (*reqstr != '\0')
+        {
+            size_t len = 0, alloc = 0;
+            char *buf = NULL;
+            strAdd(&buf, &len, &alloc, "HTTP-%s", reqstr);
+            addWebRequest(buf, val);
+            free(buf);
+        } // if
+
+        reqstr = next;
+    } while (reqstr != NULL);
+
+    return true;
+} // parseRequest
+
+
+
+static WebRequest *servePage(boolean blocking)
+{
+    int newline = 0;
+    char ch = 0;
+    struct sockaddr_in addr;
+    socklen_t addrlen = 0;
+    int s = 0;
+    char *reqstr = NULL;
+    size_t len = 0, alloc = 0;
+    int err = 0;
+
+    freeWebRequest();
+
+    if (listenSocket == INVALID_SOCKET)
+        return NULL;
+
+    if (clientSocket != INVALID_SOCKET)  // response to feed to client.
+        sendStringAndDrop(&clientSocket, output);
+
+    if (blocking)
+        setBlocking(listenSocket, true);
+
+    do
+    {
+        s = accept(listenSocket, (struct sockaddr *) &addr, &addrlen);
+        err = sockErrno();
+    } while ( (s == INVALID_SOCKET) && (intrError(err)) );
+
+    if (blocking)
+        setBlocking(listenSocket, false);  // reset what we toggled up there.
+
+    if (s == INVALID_SOCKET)
+    {
+        if (wouldBlockError(err))
+            assert(!blocking);
+        else
+        {
+            entry->logError("www: accept() failed: %s", sockStrErrVal(err));
+            closesocket(listenSocket);  // make all future i/o fail too.
+            listenSocket = INVALID_SOCKET;
+        } // else
+        return NULL;
+    } // if
+
+    setBlocking(s, true);
+
+    // Doing this one char at a time isn't efficient, but it's easy.
+
+    while (1)
+    {
+        if (recv(s, &ch, 1, 0) == SOCKET_ERROR)
+        {
+            const int err = sockErrno();
+            if (!intrError(err))  // just try again on interrupt.
+            {
+                entry->logError("www: recv() failed: %s", sockStrErrVal(err));
+                FREE_AND_NULL(reqstr);
+                closesocket(s);
+                s = INVALID_SOCKET;
+                break;
+            } // if
+        } // if
+
+        else if (ch == '\n')  // newline
+        {
+            if (++newline == 2)
+                break;  // end of request.
+            strAdd(&reqstr, &len, &alloc, "\n");
+        } // if
+
+        else if (ch != '\r')
+        {
+            newline = 0;
+            strAdd(&reqstr, &len, &alloc, "%c", ch);
+        } // else if
+    } // while
+
+    if (reqstr != NULL)
+    {
+        char *get = NULL;
+        char *ptr = strchr(reqstr, '\n');
+        if (ptr != NULL)
+        {
+            *ptr = '\0';
+            ptr++;
+        } // if
+
+        // reqstr is the GET (or whatever) request, ptr is the rest.
+        get = entry->xstrdup(reqstr);
+        if (ptr == NULL)
+        {
+            *ptr = '\0';
+            len = 0;
+        } // if
+        else
+        {
+            len = strlen(ptr);
+            memmove(reqstr, ptr, len+1);
+        } // else
+
+        entry->logDebug("www: request '%s'", get);
+
+        // okay, now (get) and (reqptr) are separate strings.
+        // These parse*() functions update (webRequest).
+        if ( (parseGet(get)) && (parseRequest(reqstr)) )
+            entry->logDebug("www: accepted request");
+        else
+        {
+            entry->logError("www: rejected bogus request");
+            freeWebRequest();
+            respond404(&s);
+        } // else
+
+        free(reqstr);
+        free(get);
+    } // if
+
+    clientSocket = s;
+    return webRequest;
+} // servePage
+
+
+static SOCKET create_listen_socket(short portnum)
+{
+    SOCKET s = INVALID_SOCKET;
+    int protocol = 0;  // pray this is right.
+    struct protoent *prot;
+
+    setprotoent(0);
+    prot = getprotobyname("tcp");
+    if (prot != NULL)
+        protocol = prot->p_proto;
+
+    s = socket(PF_INET, SOCK_STREAM, protocol);
+    if (s == INVALID_SOCKET)
+        entry->logInfo("www: socket() failed ('%s')", sockStrError());
+    else
+    {
+        boolean success = false;
+        struct sockaddr_in addr;
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(portnum);
+        addr.sin_addr.s_addr = INADDR_ANY;  // !!! FIXME: bind to localhost.
+        if (bind(s, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
+            entry->logError("www: bind() failed ('%s')", sockStrError());
+        else if (listen(s, 5) == SOCKET_ERROR)
+            entry->logError("www: listen() failed ('%s')", sockStrError());
+        else
+        {
+            entry->logInfo("www: socket created on port %d", (int) portnum);
+            success = true;
+        } // else
+
+        if (!success)
+        {
+            closesocket(s);
+            s = INVALID_SOCKET;
+        } // if
+    } // if
+
+    return s;
+} // create_listen_socket
+
+
+static boolean MojoGui_www_init(void)
+{
+    size_t len = 0, alloc = 0;
+    short portnum = 7341;  // !!! FIXME: try some random ports.
+    percentTicks = 0;
+
+    if (!initSocketSupport())
+    {
+        entry->logInfo("www: socket subsystem init failed, use another UI.");
+        return false;
+    } // if
+    
+    listenSocket = create_listen_socket(portnum);
+    if (listenSocket < 0)
+    {
+        entry->logInfo("www: no listen socket, use another UI.");
+        return false;
+    } // if
+
+    setBlocking(listenSocket, false);
+
+    strAdd(&baseUrl, &len, &alloc, "http://localhost:%d/", (int) portnum);
+    return true;
+} // MojoGui_www_init
+
+
+static void MojoGui_www_deinit(void)
+{
+    // Catch any waiting browser connections...and tell them to buzz off!  :)
+    char *donetitle = htmlescape(entry->_("Shutting down..."));
+    char *donetext = htmlescape(entry->_(
+          "Setup program is shutting down. You can close this browser now."));
+    size_t len = 0, alloc = 0;
+    char *html = NULL;
+
+    strAdd(&html, &len, &alloc, "<center><h1>%s</h1></center>", donetext);
+    setHtml(donetitle, html);
+    free(html);
+    free(donetitle);
+    free(donetext);
+    while (servePage(false) != NULL) { /* no-op. */ }
+
+    freeWebRequest();
+    FREE_AND_NULL(output);
+    FREE_AND_NULL(lastProgressType);
+    FREE_AND_NULL(lastComponent);
+    FREE_AND_NULL(baseUrl);
+
+    if (clientSocket != INVALID_SOCKET)
+    {
+        closesocket(clientSocket);
+        clientSocket = INVALID_SOCKET;
+    } // if
+
+    if (listenSocket != INVALID_SOCKET)
+    {
+        closesocket(listenSocket);
+        listenSocket = INVALID_SOCKET;
+    } // if
+
+    deinitSocketSupport();
+} // MojoGui_www_deinit
+
+
+static int doPromptPage(const char *title, const char *text,
+                        const char *pagename,
+                        const char **buttons, const char **localizedButtons,
+                        int bcount)
+{
+    char *htmltitle = htmlescape(title);
+    boolean sawPage = false;
+    int answer = -1;
+    int i = 0;
+    char *html = NULL;
+    size_t len = 0, alloc = 0;
+
+    strAdd(&html, &len, &alloc,
+        "<center>"
+        "<form name='form_%s' method='get'>"  // pagename
+         "<input type='hidden' name='page' value='%s'>"  // pagename
+         "<table>"
+          "<tr><td align='center'>%s</td></tr>"   // text
+          "<tr>"
+           "<td align='center'>", pagename, pagename, text);
+
+    for (i = 0; i < bcount; i++)
+    {
+        const char *button = buttons[i];
+        const char *loc = localizedButtons[i];
+        strAdd(&html, &len, &alloc,
+            "<input type='submit' name='%s' value='%s'>", button, loc);
+    } // for
+
+    strAdd(&html, &len, &alloc,
+           "</td>"
+          "</tr>"
+         "</table>"
+        "</form>"
+        "</center>");
+
+    setHtml(htmltitle, html);
+    free(htmltitle);
+    free(html);
+
+    while ((!sawPage) || (answer == -1))
+    {
+        WebRequest *req = servePage(true);
+        sawPage = false;
+        answer = -1;
+        while (req != NULL)
+        {
+            const char *k = req->key;
+            const char *v = req->value;
+            if ( (strcmp(k, "page") == 0) && (strcmp(v, pagename) == 0) )
+                sawPage = true;
+            else
+            {
+                for (i = 0; i < bcount; i++)
+                {
+                    if (strcmp(k, buttons[i]) == 0)
+                    {
+                        answer = i;
+                        break;
+                    } // if
+                } // for
+            } // else
+
+            req = req->next;
+        } // for
+    } // while
+
+    return answer;
+} // doPromptPage
+
+
+static void MojoGui_www_msgbox(const char *title, const char *text)
+{
+    char *buttons[] = { "ok" };
+    char *localizedButtons[] = { htmlescape(entry->_("OK")) };
+    char *htmltext = htmlescape(text);
+    doPromptPage(title, htmltext, "msgbox", buttons, localizedButtons, 1);
+    free(htmltext);
+    free(localizedButtons[0]);
+} // MojoGui_www_msgbox
+
+
+static boolean MojoGui_www_promptyn(const char *title, const char *text)
+{
+    int i, rc;
+    char *htmltext = htmlescape(text);
+    char *buttons[] = { "no", "yes" };
+    char *localizedButtons[] = {
+            htmlescape(entry->_("No"))
+            htmlescape(entry->_("Yes")),
+    };
+    assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(localizedButtons));
+
+    rc = doPromptPage(title, htmltext, "promptyn", buttons, localizedButtons,
+                      STATICARRAYLEN(buttons));
+    free(htmltext);
+
+    for (i = 0; i < STATICARRAYLEN(localizedButtons); i++)
+        free(localizedButtons[i]);
+
+    return (rc == 1);
+} // MojoGui_www_promptyn
+
+
+static MojoGuiYNAN MojoGui_www_promptynan(const char *title, const char *txt)
+{
+    int i, rc;
+    char *htmltext = htmlescape(text);
+    char *buttons[] = { "no", "yes", "always", "never" };
+    char *localizedButtons[] = {
+            htmlescape(entry->_("No"))
+            htmlescape(entry->_("Yes")),
+            htmlescape(entry->_("Always")),
+            htmlescape(entry->_("Never")),
+    };
+    assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(localizedButtons));
+
+    rc = doPromptPage(title, htmltext, "promptynan", buttons, localizedButtons,
+                      STATICARRAYLEN(buttons));
+
+    free(htmltext);
+    for (i = 0; i < STATICARRAYLEN(localizedButtons); i++)
+        free(localizedButtons[i]);
+
+    return (MojoGuiYNAN) rc;
+} // MojoGui_www_promptynan
+
+
+static boolean MojoGui_www_start(const char *title, const char *splash)
+{
+    return true;
+} // MojoGui_www_start
+
+
+static void MojoGui_www_stop(void)
+{
+    // no-op.
+} // MojoGui_www_stop
+
+
+static int MojoGui_www_readme(const char *name, const uint8 *data,
+                              size_t datalen, boolean can_back,
+                              boolean can_fwd)
+{
+    char *text = NULL;
+    size_t len = 0, alloc = 0;
+    char *htmldata = htmlescape(data);
+    int i, rc;
+    int cancelbutton = -1;
+    int backbutton = -1;
+    int fwdbutton = -1;
+    int bcount = 0;
+    char *buttons[4] = { NULL, NULL, NULL, NULL };
+    char *localizedButtons[4] = { NULL, NULL, NULL, NULL };
+    assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(localizedButtons));
+
+    cancelbutton = bcount++;
+    buttons[cancelbutton] = "next";
+    localizedButtons[cancelbutton] = entry->xstrdup(entry->_("Cancel"));
+
+    if (can_back)
+    {
+        backbutton = bcount++;
+        buttons[backbutton] = "back";
+        localizedButtons[backbutton] = entry->xstrdup(entry->_("Back"));
+    } // if
+
+    if (can_fwd)
+    {
+        fwdbutton = bcount++;
+        buttons[fwdbutton] = "next";
+        localizedButtons[fwdbutton] = entry->xstrdup(entry->_("Next"));
+    } // if
+
+    strAdd(&text, &len, &alloc, "<pre>\n%s\n</pre>", htmldata);
+    free(htmldata);
+
+    rc = doPromptPage(name, text, "readme", buttons, localizedButtons, bcount);
+
+    free(text);
+    for (i = 0; i < STATICARRAYLEN(localizedButtons); i++)
+        free(localizedButtons[i]);
+
+    if (rc == backbutton)
+        return -1;
+    else if (rc == cancelbutton)
+        return 0;
+    return 1;
+} // MojoGui_www_readme
+
+
+static int MojoGui_www_options(MojoGuiSetupOptions *opts,
+                               boolean can_back, boolean can_fwd)
+{
+    return 1;
+} // MojoGui_www_options
+
+
+static char *MojoGui_www_destination(const char **recommends, int recnum,
+                                     int *command, boolean can_back,
+                                     boolean can_fwd)
+{
+    *command = 0;
+    return NULL;
+} // MojoGui_www_destination
+
+
+static boolean MojoGui_www_insertmedia(const char *medianame)
+{
+    char *htmltext = NULL;
+    char *text = NULL;
+    size_t len = 0, alloc = 0;
+
+    // !!! FIXME: better text.
+    const char *title = entry->xstrdup(entry->_("Media change"));
+    // !!! FIXME: better text.
+    strAdd(&text, &len, &alloc, entry->_("Please insert '%s'"), medianame);
+
+    htmltext = htmlescape(text);
+    free(text);
+
+    int i, rc;
+    char *buttons[] = { "cancel", "ok" };
+    char *localizedButtons[] = {
+            htmlescape(entry->_("Cancel"))
+            htmlescape(entry->_("OK")),
+    };
+    assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(localizedButtons));
+
+    rc = doPromptPage(title, htmltext, "insertmedia", buttons,
+                      localizedButtons, STATICARRAYLEN(buttons));
+
+    free(title);
+    free(htmltext);
+    for (i = 0; i < STATICARRAYLEN(localizedButtons); i++)
+        free(localizedButtons[i]);
+
+    return (rc == 1);
+} // MojoGui_www_insertmedia
+
+
+static boolean MojoGui_www_progress(const char *type, const char *component,
+                                    int percent, const char *item)
+{
+    return true;
+} // MojoGui_www_progress
+
+
+static void MojoGui_www_final(const char *msg)
+{
+} // MojoGui_www_final
+
+// end of gui_www.c ...
+

Modified: trunk/scripts/localization.lua
===================================================================
--- trunk/scripts/localization.lua	2007-06-01 10:33:05 UTC (rev 322)
+++ trunk/scripts/localization.lua	2007-06-01 10:33:34 UTC (rev 323)
@@ -142,6 +142,17 @@
 
     ["(I want to specify a path.)"] = {
     };
+
+    -- as in "404 Not Found" in a web browser.
+    ["Not Found"] = {
+    };
+
+    -- title bar in browser page, used with next item for page's text.
+    ["Shutting down..."] = {
+    };
+
+    ["Setup program is shutting down. You can close this browser now."] = {
+    };
 };
 
 -- end of localization.lua ...




More information about the mojosetup-commits mailing list