mirror of
https://github.com/glest/glest-source.git
synced 2025-02-25 04:02:30 +01:00
632 lines
17 KiB
C
632 lines
17 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.
|
|
*/
|
|
|
|
#if !SUPPORT_GUI_STDIO
|
|
#error Something is wrong in the build system.
|
|
#endif
|
|
|
|
#define BUILDING_EXTERNAL_PLUGIN 1
|
|
#include "gui.h"
|
|
|
|
MOJOGUI_PLUGIN(stdio)
|
|
|
|
#if !GUI_STATIC_LINK_STDIO
|
|
CREATE_MOJOGUI_ENTRY_POINT(stdio)
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
static char *lastProgressType = NULL;
|
|
static char *lastComponent = NULL;
|
|
static uint32 percentTicks = 0;
|
|
|
|
static int read_stdin(char *buf, int len)
|
|
{
|
|
if (fgets(buf, len, stdin) == NULL)
|
|
return -1;
|
|
|
|
len = strlen(buf) - 1;
|
|
while ( (len >= 0) && ((buf[len] == '\n') || (buf[len] == '\r')) )
|
|
buf[len--] = '\0';
|
|
|
|
return len+1;
|
|
} // read_stdin
|
|
|
|
|
|
static int readstr(const char *prompt, char *buf, int len,
|
|
boolean back, boolean fwd)
|
|
{
|
|
// !!! FIXME: if read_stdin() returns -1, we return 0, which makes it
|
|
// !!! FIXME: indistinguishable from "user hit enter" ... maybe we should
|
|
// !!! FIXME: abort in read_stdin() if i/o fails?
|
|
|
|
int retval = 0;
|
|
char *backstr = (back) ? xstrdup(_("back")) : NULL;
|
|
|
|
if (prompt != NULL)
|
|
printf("%s\n", prompt);
|
|
|
|
if (back)
|
|
{
|
|
char *fmt = xstrdup(_("Type '%0' to go back."));
|
|
char *msg = format(fmt, backstr);
|
|
printf("%s\n", msg);
|
|
free(msg);
|
|
free(fmt);
|
|
} // if
|
|
|
|
if (fwd)
|
|
{
|
|
printf("%s", _("Press enter to continue."));
|
|
printf("\n");
|
|
} // if
|
|
|
|
printf("%s",_("> "));
|
|
fflush(stdout);
|
|
|
|
if ((retval = read_stdin(buf, len)) >= 0)
|
|
{
|
|
if ((back) && (strcmp(buf, backstr) == 0)) // !!! FIXME: utf8casecmp?
|
|
retval = -1;
|
|
} // if
|
|
|
|
free(backstr);
|
|
return retval;
|
|
} // readstr
|
|
|
|
|
|
static uint8 MojoGui_stdio_priority(boolean istty)
|
|
{
|
|
// if not a tty and no other GUI plugins worked out, let the base
|
|
// application try to spawn a terminal and try again. If it can't do so,
|
|
// it will panic() and thus end the process, so we don't end up blocking
|
|
// on some prompt the user can't see.
|
|
|
|
if (!istty)
|
|
return MOJOGUI_PRIORITY_NEVER_TRY;
|
|
|
|
return MOJOGUI_PRIORITY_TRY_ABSOLUTELY_LAST; // always a last resort.
|
|
} // MojoGui_stdio_priority
|
|
|
|
|
|
static boolean MojoGui_stdio_init(void)
|
|
{
|
|
percentTicks = 0;
|
|
return true; // always succeeds.
|
|
} // MojoGui_stdio_init
|
|
|
|
|
|
static void MojoGui_stdio_deinit(void)
|
|
{
|
|
free(lastProgressType);
|
|
free(lastComponent);
|
|
lastProgressType = NULL;
|
|
lastComponent = NULL;
|
|
} // MojoGui_stdio_deinit
|
|
|
|
|
|
static void MojoGui_stdio_msgbox(const char *title, const char *text)
|
|
{
|
|
char buf[128];
|
|
char *fmt = xstrdup(_("NOTICE: %0\n[hit enter]"));
|
|
char *msg = format(fmt, text);
|
|
printf("%s\n", msg);
|
|
free(msg);
|
|
free(fmt);
|
|
fflush(stdout);
|
|
read_stdin(buf, sizeof (buf));
|
|
} // MojoGui_stdio_msgbox
|
|
|
|
|
|
static boolean MojoGui_stdio_promptyn(const char *title, const char *text,
|
|
boolean defval)
|
|
{
|
|
boolean retval = false;
|
|
if (!feof(stdin))
|
|
{
|
|
const char *_fmt = ((defval) ? _("%0 [Y/n]: ") : _("%0 [y/N]: "));
|
|
char *fmt = xstrdup(_fmt);
|
|
char *msg = format(fmt, text);
|
|
char *localized_no = xstrdup(_("N"));
|
|
char *localized_yes = xstrdup(_("Y"));
|
|
boolean getout = false;
|
|
char buf[128];
|
|
|
|
while (!getout)
|
|
{
|
|
int rc = 0;
|
|
|
|
getout = true; // we may reset this later.
|
|
printf("%s", msg);
|
|
fflush(stdout);
|
|
rc = read_stdin(buf, sizeof (buf));
|
|
|
|
if (rc < 0)
|
|
retval = false;
|
|
else if (rc == 0)
|
|
retval = defval;
|
|
else if (strcasecmp(buf, localized_no) == 0)
|
|
retval = false;
|
|
else if (strcasecmp(buf, localized_yes) == 0)
|
|
retval = true;
|
|
else
|
|
getout = false; // try again.
|
|
} // while
|
|
|
|
free(localized_yes);
|
|
free(localized_no);
|
|
free(msg);
|
|
free(fmt);
|
|
} // if
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_promptyn
|
|
|
|
|
|
static MojoGuiYNAN MojoGui_stdio_promptynan(const char *title, const char *txt,
|
|
boolean defval)
|
|
{
|
|
MojoGuiYNAN retval = MOJOGUI_NO;
|
|
if (!feof(stdin))
|
|
{
|
|
char *fmt = xstrdup(_("%0\n[y/n/Always/Never]: "));
|
|
char *msg = format(fmt, txt);
|
|
char *localized_no = xstrdup(_("N"));
|
|
char *localized_yes = xstrdup(_("Y"));
|
|
char *localized_always = xstrdup(_("Always"));
|
|
char *localized_never = xstrdup(_("Never"));
|
|
boolean getout = false;
|
|
char buf[128];
|
|
|
|
while (!getout)
|
|
{
|
|
int rc = 0;
|
|
|
|
getout = true; // we may reset this later.
|
|
printf("%s\n", msg);
|
|
fflush(stdout);
|
|
rc = read_stdin(buf, sizeof (buf));
|
|
|
|
if (rc < 0)
|
|
retval = MOJOGUI_NO;
|
|
else if (rc == 0)
|
|
retval = (defval) ? MOJOGUI_YES : MOJOGUI_NO;
|
|
else if (strcasecmp(buf, localized_no) == 0)
|
|
retval = MOJOGUI_NO;
|
|
else if (strcasecmp(buf, localized_yes) == 0)
|
|
retval = MOJOGUI_YES;
|
|
else if (strcasecmp(buf, localized_always) == 0)
|
|
retval = MOJOGUI_ALWAYS;
|
|
else if (strcasecmp(buf, localized_never) == 0)
|
|
retval = MOJOGUI_NEVER;
|
|
else
|
|
getout = false; // try again.
|
|
} // while
|
|
|
|
free(localized_never);
|
|
free(localized_always);
|
|
free(localized_yes);
|
|
free(localized_no);
|
|
free(msg);
|
|
free(fmt);
|
|
} // if
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_promptynan
|
|
|
|
|
|
static boolean MojoGui_stdio_start(const char *title,
|
|
const MojoGuiSplash *splash)
|
|
{
|
|
printf("%s\n", title);
|
|
return true;
|
|
} // MojoGui_stdio_start
|
|
|
|
|
|
static void MojoGui_stdio_stop(void)
|
|
{
|
|
// no-op.
|
|
} // MojoGui_stdio_stop
|
|
|
|
|
|
static void dumb_pager(const char *name, const char *data, size_t datalen)
|
|
{
|
|
const int MAX_PAGE_LINES = 21;
|
|
char *fmt = xstrdup(_("(%0-%1 of %2 lines, see more?)"));
|
|
int i = 0;
|
|
int w = 0;
|
|
int linecount = 0;
|
|
boolean getout = false;
|
|
char **lines = splitText(data, 80, &linecount, &w);
|
|
|
|
assert(linecount >= 0);
|
|
|
|
printf("%s\n", name);
|
|
|
|
if (lines == NULL) // failed to parse?!
|
|
printf("%s\n", data); // just dump it all. Oh well.
|
|
else
|
|
{
|
|
int printed = 0;
|
|
do
|
|
{
|
|
for (i = 0; (i < MAX_PAGE_LINES) && (printed < linecount); i++)
|
|
printf("%s", lines[printed++]);
|
|
|
|
if (printed >= linecount)
|
|
getout = true;
|
|
else
|
|
{
|
|
char *msg = NULL;
|
|
printf("\n");
|
|
msg = format(fmt, numstr((printed-i)+1),
|
|
numstr(printed), numstr(linecount));
|
|
getout = !MojoGui_stdio_promptyn("", msg, true);
|
|
free(msg);
|
|
printf("\n");
|
|
} // else
|
|
} while (!getout);
|
|
} // while
|
|
|
|
for (i = 0; i < linecount; i++)
|
|
free(lines[i]);
|
|
free(lines);
|
|
free(fmt);
|
|
} // dumb_pager
|
|
|
|
|
|
static int MojoGui_stdio_readme(const char *name, const uint8 *_data,
|
|
size_t datalen, boolean can_back,
|
|
boolean can_fwd)
|
|
{
|
|
const char *data = (const char *) _data;
|
|
char buf[256];
|
|
int retval = -1;
|
|
boolean failed = true;
|
|
|
|
// !!! FIXME: popen() isn't reliable.
|
|
#if 0 //PLATFORM_UNIX
|
|
const size_t namelen = strlen(name);
|
|
const char *programs[] = { getenv("PAGER"), "more", "less -M", "less" };
|
|
int i = 0;
|
|
|
|
// flush streams, so output doesn't mingle with the popen()'d process.
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
for (i = 0; i < STATICARRAYLEN(programs); i++)
|
|
{
|
|
const char *cmd = programs[i];
|
|
if (cmd != NULL)
|
|
{
|
|
FILE *io = popen(cmd, "w");
|
|
if (io != NULL)
|
|
{
|
|
failed = false;
|
|
if (!failed) failed = (fwrite("\n", 1, 1, io) != 1);
|
|
if (!failed) failed = (fwrite(name, namelen, 1, io) != 1);
|
|
if (!failed) failed = (fwrite("\n", 1, 1, io) != 1);
|
|
if (!failed) failed = (fwrite(data, datalen, 1, io) != 1);
|
|
if (!failed) failed = (fwrite("\n", 1, 1, io) != 1);
|
|
failed |= (pclose(io) != 0); // call whether we failed or not.
|
|
if (!failed)
|
|
break; // it worked, we're done!
|
|
} // if
|
|
} // if
|
|
} // for
|
|
#endif // PLATFORM_UNIX
|
|
|
|
if (failed) // We're not Unix, or none of the pagers worked?
|
|
dumb_pager(name, data, datalen);
|
|
|
|
// Put up the "hit enter to continue (or 'back' to go back)" prompt,
|
|
// but only if there's an choice to be made here.
|
|
if ((!can_back) || (readstr(NULL, buf, sizeof (buf), can_back, true) >= 0))
|
|
retval = 1;
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_readme
|
|
|
|
|
|
static void toggle_option(MojoGuiSetupOptions *parent,
|
|
MojoGuiSetupOptions *opts, int *line, int target)
|
|
{
|
|
if ((opts != NULL) && (target > *line))
|
|
{
|
|
if (!opts->is_group_parent)
|
|
{
|
|
if (++(*line) == target)
|
|
{
|
|
const boolean toggled = ((opts->value) ? false : true);
|
|
|
|
// "radio buttons" in a group?
|
|
if ((parent) && (parent->is_group_parent))
|
|
{
|
|
if (toggled) // drop unless we weren't the current toggle.
|
|
{
|
|
// set all siblings to false...
|
|
MojoGuiSetupOptions *i = parent->child;
|
|
while (i != NULL)
|
|
{
|
|
i->value = false;
|
|
i = i->next_sibling;
|
|
} // while
|
|
opts->value = true; // reset us to be true.
|
|
} // if
|
|
} // if
|
|
|
|
else // individual "check box" was chosen.
|
|
{
|
|
opts->value = toggled;
|
|
} // else
|
|
|
|
return; // we found it, bail.
|
|
} // if
|
|
} // if
|
|
|
|
if (opts->value) // if option is toggled on, descend to children.
|
|
toggle_option(opts, opts->child, line, target);
|
|
toggle_option(parent, opts->next_sibling, line, target);
|
|
} // if
|
|
} // toggle_option
|
|
|
|
|
|
static void print_options(MojoGuiSetupOptions *opts, int *line, int level)
|
|
{
|
|
if (opts != NULL)
|
|
{
|
|
int i;
|
|
int spacing = 1;
|
|
if (opts->is_group_parent)
|
|
spacing += 6;
|
|
else
|
|
{
|
|
(*line)++;
|
|
printf("%2d [%c]", *line, opts->value ? 'X' : ' ');
|
|
} // else
|
|
|
|
for (i = 0; i < (level + spacing); i++)
|
|
putchar(' ');
|
|
|
|
printf("%s%s\n", opts->description, opts->is_group_parent ? ":" : "");
|
|
|
|
if ((opts->value) || (opts->is_group_parent))
|
|
print_options(opts->child, line, level+1);
|
|
print_options(opts->next_sibling, line, level);
|
|
} // if
|
|
} // print_options
|
|
|
|
|
|
static int MojoGui_stdio_options(MojoGuiSetupOptions *opts,
|
|
boolean can_back, boolean can_fwd)
|
|
{
|
|
const char *inst_opts_str = xstrdup(_("Options"));
|
|
const char *prompt = xstrdup(_("Choose number to change."));
|
|
int retval = -1;
|
|
boolean getout = false;
|
|
char buf[128];
|
|
int len = 0;
|
|
|
|
while (!getout)
|
|
{
|
|
int line = 0;
|
|
|
|
printf("\n\n");
|
|
printf("%s", inst_opts_str);
|
|
printf("\n");
|
|
print_options(opts, &line, 1);
|
|
printf("\n");
|
|
|
|
if ((len = readstr(prompt, buf, sizeof (buf), can_back, true)) < 0)
|
|
getout = true;
|
|
else if (len == 0)
|
|
{
|
|
getout = true;
|
|
retval = 1;
|
|
} // else if
|
|
else
|
|
{
|
|
char *endptr = NULL;
|
|
int target = (int) strtol(buf, &endptr, 10);
|
|
if (*endptr == '\0') // complete string was a valid number?
|
|
{
|
|
line = 0;
|
|
toggle_option(NULL, opts, &line, target);
|
|
} // if
|
|
} // else
|
|
} // while
|
|
|
|
free((void *) inst_opts_str);
|
|
free((void *) prompt);
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_options
|
|
|
|
|
|
static char *MojoGui_stdio_destination(const char **recommends, int recnum,
|
|
int *command, boolean can_back,
|
|
boolean can_fwd)
|
|
{
|
|
const char *instdeststr = xstrdup(_("Destination"));
|
|
const char *prompt = NULL;
|
|
char *retval = NULL;
|
|
boolean getout = false;
|
|
char buf[128];
|
|
int len = 0;
|
|
int i = 0;
|
|
|
|
*command = -1;
|
|
|
|
if (recnum > 0)
|
|
prompt = xstrdup(_("Choose install destination by number (hit enter for #1), or enter your own."));
|
|
else
|
|
prompt = xstrdup(_("Enter path where files will be installed."));
|
|
|
|
while (!getout)
|
|
{
|
|
printf("\n\n%s\n", instdeststr);
|
|
for (i = 0; i < recnum; i++)
|
|
printf(" %2d %s\n", i+1, recommends[i]);
|
|
printf("\n");
|
|
|
|
if ((len = readstr(prompt, buf, sizeof (buf), can_back, false)) < 0)
|
|
getout = true;
|
|
|
|
else if ((len == 0) && (recnum > 0)) // default to first in list.
|
|
{
|
|
retval = xstrdup(recommends[0]);
|
|
*command = 1;
|
|
getout = true;
|
|
} // else if
|
|
|
|
else if (len > 0)
|
|
{
|
|
char *endptr = NULL;
|
|
int target = (int) strtol(buf, &endptr, 10);
|
|
// complete string was a valid number?
|
|
if ((*endptr == '\0') && (target > 0) && (target <= recnum))
|
|
retval = xstrdup(recommends[target-1]);
|
|
else
|
|
retval = xstrdup(buf);
|
|
|
|
*command = 1;
|
|
getout = true;
|
|
} // else
|
|
} // while
|
|
|
|
free((void *) prompt);
|
|
free((void *) instdeststr);
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_destination
|
|
|
|
|
|
static int MojoGui_stdio_productkey(const char *desc, const char *fmt,
|
|
char *buf, const int buflen,
|
|
boolean can_back, boolean can_fwd)
|
|
{
|
|
const char *prompt = xstrdup(_("Please enter your product key"));
|
|
char *defval = ((*buf) ? xstrdup(buf) : NULL);
|
|
boolean getout = false;
|
|
int retval = -1;
|
|
char *msg = NULL;
|
|
|
|
if (defval != NULL)
|
|
{
|
|
char *locfmt = xstrdup(_("(just press enter to use '%0')"));
|
|
msg = format(locfmt, defval);
|
|
free(locfmt);
|
|
} // if
|
|
|
|
while (!getout)
|
|
{
|
|
int len;
|
|
printf("\n\n%s\n", desc);
|
|
if (msg != NULL)
|
|
printf("%s\n", msg);
|
|
if ((len = readstr(prompt, buf, buflen, can_back, false)) < 0)
|
|
getout = true;
|
|
else
|
|
{
|
|
if ((len == 0) && (defval != NULL))
|
|
strcpy(buf, defval);
|
|
|
|
if (isValidProductKey(fmt, buf))
|
|
{
|
|
retval = 1;
|
|
getout = true;
|
|
} // else if
|
|
else
|
|
{
|
|
// We can't check the input character-by-character, so reuse
|
|
// the failed-verification localized string.
|
|
printf("\n%s\n\n",
|
|
_("That key appears to be invalid. Please try again."));
|
|
} // else
|
|
} // else
|
|
} // while
|
|
|
|
free(msg);
|
|
free(defval);
|
|
free((void *) prompt);
|
|
|
|
return retval;
|
|
} // MojoGui_stdio_productkey
|
|
|
|
|
|
static boolean MojoGui_stdio_insertmedia(const char *medianame)
|
|
{
|
|
char buf[32];
|
|
char *fmt = xstrdup(_("Please insert '%0'"));
|
|
char *msg = format(fmt, medianame);
|
|
printf("%s\n", _("Media change"));
|
|
printf("%s\n", msg);
|
|
free(msg);
|
|
free(fmt);
|
|
return (readstr(NULL, buf, sizeof (buf), false, true) >= 0);
|
|
} // MojoGui_stdio_insertmedia
|
|
|
|
|
|
static void MojoGui_stdio_progressitem(void)
|
|
{
|
|
// force new line of output on next call to MojoGui_stdio_progress()
|
|
percentTicks = 0;
|
|
} // MojoGui_stdio_progressitem
|
|
|
|
|
|
static boolean MojoGui_stdio_progress(const char *type, const char *component,
|
|
int percent, const char *item,
|
|
boolean can_cancel)
|
|
{
|
|
const uint32 now = ticks();
|
|
|
|
if ( (lastComponent == NULL) ||
|
|
(strcmp(lastComponent, component) != 0) ||
|
|
(lastProgressType == NULL) ||
|
|
(strcmp(lastProgressType, type) != 0) )
|
|
{
|
|
free(lastProgressType);
|
|
free(lastComponent);
|
|
lastProgressType = xstrdup(type);
|
|
lastComponent = xstrdup(component);
|
|
printf("%s\n%s\n", type, component);
|
|
} // if
|
|
|
|
// limit update spam... will only write every one second, tops,
|
|
// on any given filename, but it writes each filename at least once
|
|
// so it doesn't look like we only installed a few things.
|
|
if (percentTicks <= now)
|
|
{
|
|
char *fmt = NULL;
|
|
char *msg = NULL;
|
|
percentTicks = now + 1000;
|
|
if (percent < 0)
|
|
printf("%s\n", item);
|
|
else
|
|
{
|
|
fmt = xstrdup(_("%0 (total progress: %1%%)"));
|
|
msg = format(fmt, item, numstr(percent));
|
|
printf("%s\n", msg);
|
|
free(msg);
|
|
free(fmt);
|
|
} // else
|
|
} // if
|
|
|
|
return true;
|
|
} // MojoGui_stdio_progress
|
|
|
|
|
|
static void MojoGui_stdio_final(const char *msg)
|
|
{
|
|
printf("%s\n\n", msg);
|
|
fflush(stdout);
|
|
} // MojoGui_stdio_final
|
|
|
|
// end of gui_stdio.c ...
|
|
|