MegaGlest/mk/linux/mojosetup/gui_stdio.c

656 lines
18 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>
*/
#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);
for (i = 0; i < linecount; i++)
free(lines[i]);
free(lines);
} // while
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 ...