Permalink
Fetching contributors…
Cannot retrieve contributors at this time
767 lines (675 sloc) 22.7 KB
/* Modifications by Borland/Inprise Corp.
04/11/2000: copied topmost_valid_path function from gtk_ui.c and added
code to install_state function to use the topmost path for
checking write access.
added check in install_state to make sure install and binary
paths are different. If not give an error and ask for paths
again.
05/18/2000: Modified console_setup to support the install path and binary
being provided as command line arguments (see main.c). If the
command-line paths are invalid, install will abort.
*/
#include "config.h"
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <ctype.h>
#include <stdlib.h>
#include "install.h"
#include "install_ui.h"
#include "detect.h"
#include "file.h"
#include "copy.h"
#include "bools.h"
#include "loki_launchurl.h"
/* The README viewer program - on all Linux sytems */
#define DEFAULT_PAGER_COMMAND "more"
static char pagercmd[PATH_MAX];
static const char *yes_letter = gettext_noop("Y");
static const char *no_letter = gettext_noop("N");
static int prompt_user(const char *prompt, const char *default_answer,
char *answer, int maxlen)
{
printf("%s", prompt);
printf(" [%s] ", default_answer && *default_answer ? default_answer : "");
fflush(stdout);
if ( fgets(answer, maxlen, stdin) ) {
answer[strlen(answer)-1] = '\0';
if ( (answer[0] == '\0') && default_answer ) {
strcpy(answer, default_answer);
}
}
return answer[0];
}
static yesno_answer console_prompt(const char *prompt, yesno_answer suggest)
{
char line[BUFSIZ];
line[0] = '\0';
switch (suggest) {
case RESPONSE_YES:
prompt_user(prompt, _("Y/n"), line, (sizeof line));
break;
case RESPONSE_NO:
prompt_user(prompt, _("N/y"), line, (sizeof line));
break;
case RESPONSE_OK:
printf(_("%s [Press Enter] "), prompt);
getchar();
return RESPONSE_OK;
default:
fprintf(stderr, _("Warning, invalid yesno prompt: %s\n"), prompt);
return(RESPONSE_INVALID);
}
if(!strncasecmp(line, _(yes_letter), 1)) {
return RESPONSE_YES;
} else if(!strncasecmp(line, _(no_letter), 1)) {
return RESPONSE_NO;
} else {
return RESPONSE_INVALID;
}
}
static yesno_answer prompt_yesnohelp(const char *prompt, yesno_answer suggest)
{
char line[BUFSIZ];
line[0] = '\0';
switch (suggest) {
case RESPONSE_YES:
prompt_user(prompt, _("Y/n/?"), line, (sizeof line));
break;
case RESPONSE_NO:
prompt_user(prompt, _("N/y/?"), line, (sizeof line));
break;
default:
fprintf(stderr, _("Warning, invalid yesno prompt: %s\n"), prompt);
return(RESPONSE_INVALID);
}
if(!strncasecmp(line, _(yes_letter), 1)) {
return RESPONSE_YES;
} else if(!strncasecmp(line, _(no_letter), 1)) {
return RESPONSE_NO;
} else if(!strncasecmp(line, "?", 1)) {
return RESPONSE_HELP;
} else {
return RESPONSE_INVALID;
}
}
static yesno_answer prompt_warning(const char *warning)
{
printf("%s\n", warning);
return (console_prompt(_("Continue?"), RESPONSE_NO));
}
/* This function returns a boolean value that tells if parsing for this node's siblings
should continue (used for exclusive options) */
static int parse_option(install_info *info, const char *component, xmlNodePtr node, int exclusive, int excl_reinst)
{
const char *help = "", *name;
char line[BUFSIZ];
char prompt[BUFSIZ];
const char *warn;
char *wanted;
xmlNodePtr kid;
int retval = 1;
yesno_answer response = RESPONSE_INVALID, default_response = RESPONSE_INVALID;
/* Check if we are on a valid tag */
if ( strcmp((char *)node->name, "option") && strcmp((char *)node->name, "exclusive") ) {
return retval;
}
/* See if this node matches the current architecture */
wanted = (char *)xmlGetProp(node, BAD_CAST "arch");
if ( ! match_arch(info, wanted) ) {
xmlFree(wanted);
return retval;
}
xmlFree(wanted);
wanted = (char *)xmlGetProp(node, BAD_CAST "libc");
if ( ! match_libc(info, wanted) ) {
xmlFree(wanted);
return retval;
}
xmlFree(wanted);
wanted = (char *)xmlGetProp(node, BAD_CAST "distro");
if ( ! match_distro(info, wanted) ) {
xmlFree(wanted);
return retval;
}
xmlFree(wanted);
wanted = (char *)xmlGetProp(node, BAD_CAST "if");
if ( ! match_condition(wanted) ) {
xmlFree(wanted);
return retval;
}
xmlFree(wanted);
if ( ! get_option_displayed(info, node) ) {
return retval;
}
/* Skip any options that are already installed */
if ( info->product ) {
product_component_t *comp;
if ( component ) {
comp = loki_find_component(info->product, component);
} else {
comp = loki_getdefault_component(info->product);
}
if ( exclusive && ! excl_reinst ) {
if ( comp &&
loki_find_option(comp, get_option_name(info,node,NULL,0)) ) {
printf(_("Previously installed option '%s' will be installed.\n"), get_option_name(info,node,line,BUFSIZ));
response = RESPONSE_YES;
} else {
response = RESPONSE_NO;
}
} else if ( ! GetProductReinstall(info) || !GetReinstallNode(info, node) ) {
if ( comp &&
loki_find_option(comp, get_option_name(info,node,NULL,0)) ) {
/* Recurse down any other options */
node = XML_CHILDREN(node);
while ( node ) {
if ( ! strcmp((char *)node->name, "option") ) {
parse_option(info, component, node, 0, 0);
} else if ( ! strcmp((char *)node->name, "exclusive") ) {
xmlNodePtr child;
int reinst = GetReinstallNode(info, node);
for ( child = XML_CHILDREN(node); child && parse_option(info, component, child, 1, reinst); child = child->next)
;
}
node = node->next;
}
if ( exclusive ) /* We stop prompting the user once an option has been chosen */
retval = 0;
return(retval);
}
}
}
/* Check for required option */
if ( xmlGetProp(node, BAD_CAST "required") ) {
printf(_("'%s' option will be installed.\n"), get_option_name(info,node,line,BUFSIZ));
response = RESPONSE_YES;
}
if ( !strcmp((char *)node->name, "exclusive")) {
printf(_("'%s' :\n"), get_option_name(info,node,line,BUFSIZ));
response = RESPONSE_YES;
}
options_loop:
/* See if the user wants this option */
while ( response == RESPONSE_INVALID ) {
snprintf(prompt, sizeof(prompt), _("Option: '%s' ?"), get_option_name(info,node,line,BUFSIZ));
wanted = (char *)xmlGetProp(node, BAD_CAST "install");
if ( (wanted && (strcmp(wanted, "true") == 0)) || !strcmp((char *)node->name, "exclusive") ) {
default_response = RESPONSE_YES;
} else {
default_response = RESPONSE_NO;
}
help = get_option_help(info, node);
if ( help ) {
response = prompt_yesnohelp(prompt, default_response);
} else {
response = console_prompt(prompt, default_response);
}
}
switch(response) {
case RESPONSE_YES:
/* See if there is an EULA for this option */
name = GetProductEULANode(info, node, NULL);
if ( name ) {
run_command(info, pagercmd, name, NULL, 1);
if ( console_prompt(_("Do you agree with the license?"), RESPONSE_YES) !=
RESPONSE_YES ) {
response = RESPONSE_INVALID;
goto options_loop;
}
}
if ( default_response != RESPONSE_YES ) {
warn = get_option_warn(info, node);
if ( warn && excl_reinst ) { /* Display a warning message to the user */
console_prompt(warn, RESPONSE_OK);
}
}
/* Mark this option for installation */
mark_option(info, node, "true", 0);
/* Add this option size to the total */
info->install_size += size_node(info, node);
/* Recurse down any other options */
kid = XML_CHILDREN(node);
while ( kid ) {
if ( ! strcmp((char *)kid->name, "option") ) {
if ( !strcmp((char *)node->name, "exclusive") ) {
parse_option(info, component, kid, 1, GetReinstallNode(info, node));
} else {
parse_option(info, component, kid, 0, 0);
}
} else if ( ! strcmp((char *)kid->name, "exclusive") ) {
xmlNodePtr child;
int reinst = GetReinstallNode(info, kid);
for ( child = XML_CHILDREN(kid); child && parse_option(info, component, child, 1, reinst); child = child->next)
;
}
kid = kid->next;
}
if ( exclusive ) /* We stop prompting the user once an option has been chosen */
retval = 0;
break;
case RESPONSE_HELP:
if ( help ) {
printf("%s\n", help);
} else {
printf(_("No help available\n"));
}
parse_option(info, component, node, exclusive, 0);
break;
default:
/* Unmark this option for installation */
mark_option(info, node, "false", 1);
break;
}
return retval;
}
static install_state console_init(install_info *info, int argc, char **argv, int noninteractive)
{
install_state state;
setup_add_bool("console", 1);
if ( info->component ) {
printf(_("----====== %s / %s installation program ======----\n"), info->desc,
GetProductComponent(info));
} else {
printf(_("----====== %s installation program ======----\n"), info->desc);
}
printf("\n");
#ifdef __linux /* Glibc version only matters on Linux anyway */
printf(_("You are running a %s machine with %s (%s)\n"), info->arch, info->libc, distribution_name[info->distro]);
#else
printf(_("You are running a %s machine (%s)\n"), info->arch, distribution_name[info->distro]);
#endif
printf(_("Hit Control-C anytime to cancel this installation program.\n"));
printf("\n");
if ( GetProductEULA(info, NULL) ) {
state = SETUP_LICENSE;
} else {
state = SETUP_README;
}
info->install_size = size_tree(info, XML_CHILDREN(XML_ROOT(info->config)));
return state;
}
static install_state console_license(install_info *info)
{
install_state state;
sleep(1);
run_command(info, pagercmd, GetProductEULA(info, NULL), NULL, 1);
if ( console_prompt(_("Do you agree with the license?"), RESPONSE_YES) ==
RESPONSE_YES ) {
state = SETUP_README;
} else {
state = SETUP_EXIT;
}
return state;
}
static install_state console_readme(install_info *info)
{
const char *readme;
readme = GetProductREADME(info, NULL);
if ( readme ) {
char prompt[256];
const char *str;
str = readme + strlen(info->setup_path)+1; /* Skip the install path */
snprintf(prompt, sizeof(prompt), _("Would you like to read the %s file ?"), str);
if ( console_prompt(prompt, RESPONSE_YES) == RESPONSE_YES ) {
run_command(info, pagercmd, readme, NULL, 1);
}
}
if ( GetProductAllowsExpress(info) ) {
return SETUP_CLASS;
} else {
return SETUP_OPTIONS;
}
}
/* hacked in cdkey support. --ryan. */
extern char gCDKeyString[128];
static install_state console_setup(install_info *info)
{
int okay = 0;
char path[PATH_MAX];
xmlNodePtr node;
if ( express_setup ) {
/* Install desktop menu items */
if((!GetProductHasNoBinaries(info)) && (GetProductInstallMenuItems(info))) {
info->options.install_menuitems = 1;
}
return SETUP_INSTALL;
}
if ( GetProductIsMeta(info) ) {
while ( ! okay ) {
int index = 1, chosen;
const char *wanted;
node = XML_CHILDREN(XML_ROOT(info->config));
printf("%s\n", GetProductDesc(info));
while ( node ) {
if ( strcmp((char *)node->name, "option") == 0 ) {
printf("%d) %s\n", index, get_option_name(info, node, NULL, 0));
wanted = (char *)xmlGetProp(node, BAD_CAST "install"); /* Look for a default value */
if ( wanted && (strcmp(wanted, "true") == 0) ) {
snprintf(path, sizeof(path), "%d", index);
}
++ index;
}
node = node->next;
}
if ( ! prompt_user(_("Please choose the product to install, or Q to quit. "),
path, path, sizeof(path)) ) {
return SETUP_ABORT;
}
if ( *path == 'q' || *path == 'Q' ) {
return SETUP_ABORT;
}
chosen = atoi(path);
if ( chosen > 0 && chosen < index ) {
node = XML_CHILDREN(XML_ROOT(info->config));
index = 1;
while ( node ) {
if ( strcmp((char *)node->name, "option") == 0 ) {
if ( index == chosen ) {
mark_option(info, node, "true", 0);
} else {
mark_option(info, node, "false", 0);
}
++ index;
}
node = node->next;
}
return SETUP_INSTALL;
}
}
} else {
while ( ! okay ) {
/* Find out where to install the game, unless it was set
with a command-line argument */
if (! disable_install_path) {
if ( ! prompt_user(_("Please enter the installation path"),
info->install_path, path, sizeof(path)) ) {
return SETUP_ABORT;
}
} else {
printf(_("Install path set to: %s\n"), info->install_path);
strcpy(path, info->install_path);
}
set_installpath(info, path, 1);
/* Check permissions on the install path */
topmost_valid_path(path, info->install_path);
if ( access(path, F_OK) < 0 ) {
if ( (strcmp(path, "/") != 0) && strrchr(path, '/') ) {
*(strrchr(path, '/')+1) = '\0';
}
}
if ( ! dir_is_accessible(path) ) {
printf(_("No write permission to %s\n"), path);
if ( ! disable_install_path ) {
continue;
} else {
return SETUP_ABORT;
}
}
/* Default to empty */
path[0] = '\0';
/* Find out where to install the binary symlinks, unless the binary path
was provided as a command line argument */
if ( ! disable_binary_path ) {
int get_path = 1;
if (GetProductHasNoBinaries(info))
get_path = 0;
if (get_path) {
/*----------------------------------------
** Optionally, ask the user whether
** they want to create a symlink
** to the path or not.
**--------------------------------------*/
if (GetProductHasPromptBinaries(info)) {
if (console_prompt(_("Do you want to install symbolic links to a directory in your path?"), RESPONSE_YES) != RESPONSE_YES) {
get_path = 0;
}
}
}
if (get_path)
if ( ! prompt_user(_("Please enter the path in which to create the symbolic links"),
info->symlinks_path, path, sizeof(path)) ) {
return SETUP_ABORT;
}
} else {
printf(_("Binary path set to: %s\n"), info->symlinks_path);
strcpy(path, info->symlinks_path);
}
set_symlinkspath(info, path);
/* Check for manual path */
if ( GetProductHasManPages(info) ) {
if ( ! prompt_user(_("Please enter the path in which to install manual pages for this product"),
"/usr/local/man", path, sizeof(path)) ) {
return SETUP_ABORT;
}
set_manpath(info, path);
}
/* If the binary and install path are the same, give an error */
if (strcmp(info->symlinks_path, info->install_path) == 0) {
printf(_("Binary path must be different than the install path.\nThe binary path must be an existing directory.\n"));
continue;
}
/* Check permissions on the symlinks path, if the path was
provided as a command-line argument and it is invalid, then
abort */
if ( *info->symlinks_path ) {
if ( access(info->symlinks_path, W_OK) < 0 ) {
printf(_("No write permission to %s\n"), info->symlinks_path);
if (! disable_binary_path) {
continue;
} else {
return SETUP_ABORT;
}
}
}
/* Go through the install options */
info->install_size = 0;
node = XML_CHILDREN(XML_ROOT(info->config));
while ( node ) {
if ( ! strcmp((char *)node->name, "option") ) {
parse_option(info, NULL, node, 0, 0);
} else if ( ! strcmp((char *)node->name, "exclusive") ) {
xmlNodePtr child;
int reinst = GetReinstallNode(info, node);
for ( child = XML_CHILDREN(node); child && parse_option(info, NULL, child, 1, reinst); child = child->next)
;
} else if ( ! strcmp((char *)node->name, "component") ) {
char *arch = (char *)xmlGetProp(node, BAD_CAST "arch"),
*libc = (char *)xmlGetProp(node, BAD_CAST "libc"),
*distro = (char *)xmlGetProp(node, BAD_CAST "distro"),
*cond = (char *)xmlGetProp(node, BAD_CAST "if");
if ( match_arch(info, arch) &&
match_libc(info, libc) &&
match_distro(info, distro) && match_condition(cond) ) {
xmlNodePtr child;
if ( xmlGetProp(node, BAD_CAST "showname") ) {
const char *str = (char *)xmlGetProp(node, BAD_CAST "name");
if ( !str || !strcmp(str, "Default") ) { /* Show the name of the product instead */
str = info->desc;
}
printf(_("\n%s component\n\n"), str);
}
for ( child = XML_CHILDREN(node); child; child = child->next) {
parse_option(info, (char *)xmlGetProp(node, BAD_CAST "name"), child, 0, 0);
}
}
xmlFree(arch); xmlFree(libc); xmlFree(distro); xmlFree(cond);
}
node = node->next;
}
/* Ask for desktop menu items */
if ( !GetProductHasNoBinaries(info) && (GetProductInstallMenuItems(info)) &&
console_prompt(_("Do you want to install startup menu entries?"),
RESPONSE_YES) == RESPONSE_YES ) {
info->options.install_menuitems = 1;
}
/* Confirm the install */
printf(_("Installing to %s\n"), info->install_path);
printf(_("%d MB available, %d MB will be installed.\n"),
detect_diskspace(info->install_path), (int) BYTES2MB(info->install_size));
printf("\n");
if ( console_prompt(_("Continue install?"), RESPONSE_YES) == RESPONSE_YES ) {
okay = 1;
} else
return SETUP_ABORT;
}
}
/* HACK: Use external cd key validation program, if it exists. --ryan. */
if(GetProductCDKey(info))
{
#define CDKEYCHECK_PROGRAM "./vcdk"
char cmd[sizeof (gCDKeyString) + sizeof (CDKEYCHECK_PROGRAM) + 1];
char *p;
int cdkey_is_okay = 0;
while (!cdkey_is_okay)
{
if ( ! prompt_user(_("Please enter your CD key"),
NULL, gCDKeyString, sizeof(gCDKeyString)) ) {
return SETUP_ABORT;
}
snprintf(cmd, sizeof (cmd), "%s-%s", CDKEYCHECK_PROGRAM, info->arch);
if (access(cmd, X_OK) != 0)
{
printf(_("ERROR: vcdk is missing. Installation aborted.\n"));
return SETUP_ABORT;
}
else
{
snprintf(cmd, sizeof (cmd), "%s-%s %s", CDKEYCHECK_PROGRAM, info->arch, gCDKeyString);
if (system(cmd) == 0) /* binary ran and reported key invalid? */
printf(_("CD key is invalid!\nPlease double check your key and enter it again.\n"));
else
cdkey_is_okay = 1;
}
}
p = gCDKeyString;
while(*p)
{
*p = toupper(*p);
p++;
}
}
return SETUP_INSTALL;
}
static int console_update(install_info *info, const char *path, size_t progress, size_t size, const char *current)
{
static char previous[200] = "";
static int lastpercentage = -1;
if(strcmp(previous, current)){
strncpy(previous,current, sizeof(previous));
printf(_("Installing %s ...\n"), current);
}
if ( progress && size ) {
int percentage = (int) (((float)progress/(float)size)*100.0);
if (percentage == lastpercentage)
return 1; /* don't output the same thing again. */
lastpercentage = percentage;
printf(" %3d%% - %s\r", percentage, path);
} else { /* "Running script" */
printf(" %s\r", path);
}
if(progress==size)
putchar('\n');
fflush(stdout);
return 1;
}
static void console_abort(install_info *info)
{
printf("\n");
printf(_("Install aborted - cleaning up files\n"));
}
static install_state console_complete(install_info *info)
{
install_state new_state;
printf("\n");
printf(_("Installation complete.\n"));
new_state = SETUP_EXIT;
if ( info->installed_symlink && *info->play_binary &&
console_prompt(_("Would you like to start now?"), RESPONSE_YES)
== RESPONSE_YES ) {
new_state = SETUP_PLAY;
if ( getuid() == 0 ) {
const char *warning_text =
_("If you run the program as root, the preferences will be stored in\n"
"root's home directory instead of your user account directory.");
if ( prompt_warning(warning_text) != RESPONSE_YES ) {
new_state = SETUP_EXIT;
}
}
}
return new_state;
}
static install_state console_pick_class(install_info *info)
{
char buf[BUFSIZ];
const char *msg = IsReadyToInstall(info);
express_setup = (console_prompt(_("Do you want to proceed with Recommended installation?"),
msg ? RESPONSE_NO : RESPONSE_YES) == RESPONSE_YES);
if ( express_setup && msg ) {
snprintf(buf, sizeof(buf),
_("Installation could not proceed due to the following error:\n%s\nTry to use 'Expert' installation."),
msg);
console_prompt(buf, RESPONSE_OK);
return SETUP_CLASS;
}
return SETUP_OPTIONS;
}
static install_state console_website(install_info *info)
{
const char *website_text;
printf(_("Thank you for installing %s!\n"), GetProductDesc(info) );
website_text = GetWebsiteText(info);
if ( website_text ) {
printf("%s\n", website_text);
} else {
printf(_("Please visit our website for updates and support.\n"));
}
sleep(2);
if ( (strcmp( GetAutoLaunchURL(info), "true" )==0)
|| (console_prompt(_("Would you like to launch web browser?"), RESPONSE_YES) == RESPONSE_YES ) ) {
launch_browser(info, loki_launchURL);
}
return SETUP_COMPLETE;
}
int console_okay(Install_UI *UI, int *argc, char ***argv)
{
const char *envr;
if(!isatty(STDIN_FILENO)){
fprintf(stderr,_("Standard input is not a terminal!\n"));
return(0);
}
envr = getenv("PAGER");
if ((envr == NULL) || (strlen(envr) >= sizeof (pagercmd)))
envr = DEFAULT_PAGER_COMMAND;
strcpy(pagercmd, envr);
/* Set up the driver */
UI->init = console_init;
UI->license = console_license;
UI->readme = console_readme;
UI->setup = console_setup;
UI->update = console_update;
UI->abort = console_abort;
UI->prompt = console_prompt;
UI->website = console_website;
UI->complete = console_complete;
UI->pick_class = console_pick_class;
UI->idle = NULL;
UI->exit = NULL;
UI->shutdown = NULL;
UI->is_gui = 0;
return(1);
}
#ifdef STUB_UI
int gtkui_okay(Install_UI *UI, int *argc, char ***argv)
{
return(0);
}
int carbonui_okay(Install_UI *UI, int *argc, char ***argv)
{
return(0);
}
#endif