mirror of
https://github.com/NVIDIA/nvidia-installer.git
synced 2025-07-23 02:13:00 +02:00
531 lines
13 KiB
C
531 lines
13 KiB
C
/*
|
|
* nvidia-installer: A tool for installing NVIDIA software packages on
|
|
* Unix and Linux systems.
|
|
*
|
|
* Copyright (C) 2003 NVIDIA Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses>.
|
|
*
|
|
*
|
|
* stream_ui.c - implementation of the nvidia-installer ui using printf
|
|
* and friends. This user interface is compiled into nvidia-installer to
|
|
* ensure that we always have a ui available.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include "nvidia-installer.h"
|
|
#include "nvidia-installer-ui.h"
|
|
#include "misc.h"
|
|
#include "files.h"
|
|
#include "common-utils.h"
|
|
#include "msg.h"
|
|
|
|
/* prototypes of each of the stream ui functions */
|
|
|
|
int stream_detect (Options*);
|
|
int stream_init (Options*, FormatTextRows format_text_rows);
|
|
void stream_set_title (Options*, const char*);
|
|
char *stream_get_input (Options*, const char*, const char*);
|
|
void stream_message (Options*, int level, const char*);
|
|
void stream_command_output (Options*, const char*);
|
|
int stream_approve_command_list(Options*, CommandList*, const char*);
|
|
int stream_yes_no (Options*, const int, const char*);
|
|
int stream_multiple_choice (Options *op, const char *, const char * const *,
|
|
int, int);
|
|
int stream_paged_prompt (Options *op, const char *, const char *,
|
|
const char *, const char * const *, int, int);
|
|
void stream_status_begin (Options*, const char*, const char*);
|
|
void stream_status_update (Options*, const float, const char*);
|
|
void stream_status_end (Options*, const char*);
|
|
void stream_update_indeterminate(Options*, const char*);
|
|
void stream_close (Options*);
|
|
|
|
InstallerUI stream_ui_dispatch_table = {
|
|
stream_detect,
|
|
stream_init,
|
|
stream_set_title,
|
|
stream_get_input,
|
|
stream_message,
|
|
stream_command_output,
|
|
stream_approve_command_list,
|
|
stream_yes_no,
|
|
stream_multiple_choice,
|
|
stream_paged_prompt,
|
|
stream_status_begin,
|
|
stream_status_update,
|
|
stream_status_end,
|
|
stream_update_indeterminate,
|
|
stream_close
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
float percent;
|
|
} Data;
|
|
|
|
|
|
|
|
#define STATUS_BEGIN 0
|
|
#define STATUS_UPDATE 1
|
|
#define STATUS_END 2
|
|
#define STATUS_INDETERMINATE 3
|
|
|
|
#define STATUS_BAR_WIDTH 30
|
|
|
|
/*
|
|
* print_status_bar() -
|
|
*/
|
|
|
|
static void print_status_bar(Data *d, int status, float percent)
|
|
{
|
|
int i;
|
|
float val;
|
|
|
|
static int indeterminate_position;
|
|
|
|
switch (status) {
|
|
case STATUS_BEGIN:
|
|
/* reset the position of the indeterminate progress indicator */
|
|
indeterminate_position = 0;
|
|
break;
|
|
case STATUS_UPDATE:
|
|
case STATUS_END:
|
|
case STATUS_INDETERMINATE:
|
|
default:
|
|
printf("\r");
|
|
break;
|
|
}
|
|
|
|
printf(" [");
|
|
|
|
val = ((float) STATUS_BAR_WIDTH * percent);
|
|
|
|
for (i = 0; i < STATUS_BAR_WIDTH; i++) {
|
|
char c;
|
|
|
|
if (status == STATUS_INDETERMINATE) {
|
|
c = (i == indeterminate_position % STATUS_BAR_WIDTH) ? '#' : ' ';
|
|
} else {
|
|
c = (float) i < val ? '#' : ' ';
|
|
}
|
|
|
|
printf("%c", c);
|
|
}
|
|
|
|
indeterminate_position++;
|
|
|
|
printf("] ");
|
|
if (status == STATUS_INDETERMINATE) {
|
|
/* Clear any existing percentage display and rewind the cursor to
|
|
* just after the ']' printed above */
|
|
printf(" \b\b\b\b\b");
|
|
} else {
|
|
/* Display the current percentage */
|
|
printf("%3d%%", (int) (percent * 100.0));
|
|
}
|
|
|
|
if (status == STATUS_END) printf("\n");
|
|
|
|
fflush(stdout);
|
|
|
|
} /* print_status_bar() */
|
|
|
|
|
|
|
|
static void sigwinch_handler(int n)
|
|
{
|
|
reset_current_terminal_width(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* stream_detect - returns TRUE if the user interface is present; the
|
|
* stream_ui is always present, so this always returns TRUE.
|
|
*/
|
|
|
|
int stream_detect(Options *op)
|
|
{
|
|
return TRUE;
|
|
|
|
} /* stream_detect() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_init - initialize the ui and print a welcome message.
|
|
*/
|
|
|
|
int stream_init(Options *op, FormatTextRows format_text_rows)
|
|
{
|
|
Data *d = nvalloc(sizeof(Data));
|
|
|
|
op->ui.priv = d;
|
|
|
|
if (!op->silent) {
|
|
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "Welcome to the NVIDIA Software Installer for "
|
|
"Unix/Linux");
|
|
nv_info_msg(NULL, "");
|
|
|
|
/* register the SIGWINCH signal handler */
|
|
|
|
signal(SIGWINCH, sigwinch_handler);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} /* stream_init() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_set_title() - other ui's might use this to update a welcome
|
|
* banner, window title, etc, but it doesn't make sense for this ui.
|
|
*/
|
|
|
|
void stream_set_title(Options *op, const char *title)
|
|
{
|
|
return;
|
|
|
|
} /* stream_set_title() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_get_input - prompt for user input with the given msg;
|
|
* returns the user inputted string.
|
|
*/
|
|
|
|
char *stream_get_input(Options *op, const char *def, const char *msg)
|
|
{
|
|
char *buf;
|
|
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "%s", msg);
|
|
fprintf(stdout, " [default: '%s']: ", def);
|
|
fflush(stdout);
|
|
|
|
buf = fget_next_line(stdin, NULL);
|
|
|
|
if (!buf || !buf[0]) {
|
|
if (buf) free(buf);
|
|
buf = nvstrdup(def);
|
|
}
|
|
|
|
return buf;
|
|
|
|
} /* stream_get_input() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* stream_message() - print a message
|
|
*/
|
|
|
|
void stream_message(Options *op, const int level, const char *msg)
|
|
{
|
|
typedef struct {
|
|
char *prefix;
|
|
FILE *stream;
|
|
int newline;
|
|
} MessageLevelAttributes;
|
|
|
|
const MessageLevelAttributes msg_attrs[] = {
|
|
{ NULL, stdout, FALSE }, /* NV_MSG_LEVEL_LOG */
|
|
{ NULL, stdout, TRUE }, /* NV_MSG_LEVEL_MESSAGE */
|
|
{ "WARNING: ", stderr, TRUE }, /* NV_MSG_LEVEL_WARNING */
|
|
{ "ERROR: ", stderr, TRUE } /* NV_MSG_LEVEL_ERROR */
|
|
};
|
|
|
|
/* don't print log messages if we're currently displaying a status */
|
|
|
|
if ((level == NV_MSG_LEVEL_LOG) && (op->ui.status_active)) return;
|
|
|
|
if (msg_attrs[level].newline) {
|
|
nv_info_msg_to_file(msg_attrs[level].stream, NULL, "");
|
|
}
|
|
nv_info_msg_to_file(msg_attrs[level].stream,
|
|
msg_attrs[level].prefix,
|
|
"%s", msg);
|
|
if (msg_attrs[level].newline) {
|
|
nv_info_msg_to_file(msg_attrs[level].stream, NULL, "");
|
|
}
|
|
|
|
} /* stream_message() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_command_output() - if in expert mode, print output from
|
|
* executing a command.
|
|
*
|
|
* XXX Should this output be formatted?
|
|
*/
|
|
|
|
void stream_command_output(Options *op, const char *msg)
|
|
{
|
|
if ((!op->expert) || (op->ui.status_active)) return;
|
|
|
|
nv_info_msg(" ", "%s", msg);
|
|
|
|
} /* stream_command_output() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_approve_command_list() - list all the commands that will be
|
|
* executed, and ask for approval.
|
|
*/
|
|
|
|
int stream_approve_command_list(Options *op, CommandList *cl,
|
|
const char *descr)
|
|
{
|
|
int i;
|
|
const char *prefix = " --> ";
|
|
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "The following operations will be performed to install the %s:",
|
|
descr);
|
|
nv_info_msg(NULL, "");
|
|
|
|
for (i = 0; i < cl->num; i++) {
|
|
nv_info_msg(prefix, "%s", cl->descriptions[i]);
|
|
}
|
|
|
|
fflush(stdout);
|
|
|
|
if (!stream_yes_no(op, TRUE, "\nIs this acceptable? (answering 'no' will "
|
|
"abort installation)")) {
|
|
nv_error_msg("Command list not accepted; exiting installation.");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} /* stream_approve_command_list() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_yes_no() - ask the yes/no question 'msg' and return TRUE for
|
|
* yes, and FALSE for no.
|
|
*/
|
|
|
|
int stream_yes_no(Options *op, const int def, const char *msg)
|
|
{
|
|
char *buf;
|
|
int eof, ret = def;
|
|
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "%s", msg);
|
|
if (def) fprintf(stdout, " [default: (Y)es]: ");
|
|
else fprintf(stdout, " [default: (N)o]: ");
|
|
fflush(stdout);
|
|
|
|
buf = fget_next_line(stdin, &eof);
|
|
|
|
if (!buf) return FALSE;
|
|
|
|
/* key off the first letter; otherwise, just use the default */
|
|
|
|
if (tolower(buf[0]) == 'y') ret = TRUE;
|
|
if (tolower(buf[0]) == 'n') ret = FALSE;
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
|
|
} /* stream_yes_no() */
|
|
|
|
|
|
/*
|
|
* select_option - given a list of options and a user-provided selection, check
|
|
* to see if the selection matches any of the available options. Options can be
|
|
* selected either by index or by option name. Returns the caller-provided
|
|
* default option if the user's selection is an empty string, the index of the
|
|
* first (case-insensitive) matching option, or a negative number if no option
|
|
* matched the user's input.
|
|
*/
|
|
|
|
static int select_option(const char * const *options, int num_options,
|
|
int default_option, const char *selection)
|
|
{
|
|
int i;
|
|
|
|
if (strlen(selection) == 0) {
|
|
return default_option;
|
|
}
|
|
|
|
if (isdigit(selection[0])) {
|
|
int index = atoi(selection);
|
|
|
|
if (index < 1 || index > num_options) {
|
|
return -2;
|
|
}
|
|
|
|
return index - 1;
|
|
}
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
if (strcasecmp(selection, options[i]) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* stream_multiple_choice() - display a question with multiple-choice answers
|
|
* allowing the user to select a button corresponding to the desired response.
|
|
* Returns the index answer selected from the passed-in answers array.
|
|
*/
|
|
|
|
int stream_multiple_choice(Options *op, const char *question,
|
|
const char * const *answers, int num_answers,
|
|
int default_answer)
|
|
{
|
|
int ret = default_answer;
|
|
|
|
do {
|
|
char *str;
|
|
int i;
|
|
|
|
if (ret < 0) {
|
|
nv_error_msg("Invalid response!");
|
|
}
|
|
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "%s", question);
|
|
nv_info_msg(NULL, "Valid responses are: ");
|
|
|
|
for (i = 0; i < num_answers; i++) {
|
|
nv_info_msg(NULL, " (%d)\t\"%s\"%s", i + 1, answers[i],
|
|
i == default_answer ? " [ default ]" : "");
|
|
}
|
|
|
|
nv_info_msg(NULL, "Please select your response by number or name:");
|
|
str = fget_next_line(stdin, NULL);
|
|
|
|
ret = select_option(answers, num_answers, default_answer, str);
|
|
free(str);
|
|
} while (ret < 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* stream_paged_prompt() - display a question with multiple-choice answers
|
|
* along with a text area (not scrollable), allowing the user to review the text
|
|
* and select a button corresponding to the desired response. Returns the index
|
|
* answer selected from the passed-in answers array.
|
|
*/
|
|
|
|
int stream_paged_prompt(Options *op, const char *question,
|
|
const char *pager_title, const char *pager_text,
|
|
const char * const *answers, int num_answers,
|
|
int default_answer)
|
|
{
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "________");
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "%s", pager_title);
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "%s", pager_text);
|
|
nv_info_msg(NULL, "");
|
|
nv_info_msg(NULL, "________");
|
|
|
|
return stream_multiple_choice(op, question, answers, num_answers,
|
|
default_answer);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* stream_status_begin() - we assume that title is always passed, but
|
|
* sometimes msg will be NULL.
|
|
*/
|
|
|
|
void stream_status_begin(Options *op, const char *title, const char *msg)
|
|
{
|
|
Data *d = op->ui.priv;
|
|
|
|
d->percent = 0;
|
|
|
|
nv_info_msg(NULL, "%s: %s\n", title, msg ? msg : "");
|
|
|
|
print_status_bar(d, STATUS_BEGIN, 0.0);
|
|
|
|
} /* stream_status_begin() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_status_update() -
|
|
*/
|
|
|
|
void stream_status_update(Options *op, const float percent, const char *msg)
|
|
{
|
|
Data *d = op->ui.priv;
|
|
|
|
if (d->percent != percent) {
|
|
d->percent = percent;
|
|
print_status_bar(op->ui.priv, STATUS_UPDATE, percent);
|
|
}
|
|
|
|
} /* stream_status_update() */
|
|
|
|
|
|
void stream_update_indeterminate(Options *op, const char *msg)
|
|
{
|
|
Data *d = op->ui.priv;
|
|
|
|
print_status_bar(d, STATUS_INDETERMINATE, 0.0);
|
|
usleep(250000);
|
|
}
|
|
|
|
|
|
/*
|
|
* stream_status_end() -
|
|
*/
|
|
|
|
void stream_status_end(Options *op, const char *msg)
|
|
{
|
|
print_status_bar(op->ui.priv, STATUS_END, 1.0);
|
|
} /* stream_status_end() */
|
|
|
|
|
|
|
|
/*
|
|
* stream_close() - close the ui
|
|
*/
|
|
|
|
void stream_close(Options *op)
|
|
{
|
|
if (op) {
|
|
nvfree(op->ui.priv);
|
|
}
|
|
}
|