sortix-mirror/sysinstall/interactive.c

223 lines
5.0 KiB
C

/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
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/>.
interactive.c
Interactive utility functions.
*******************************************************************************/
#include <sys/termmode.h>
#include <sys/types.h>
#include <ctype.h>
#include <err.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <wchar.h>
#include "execute.h"
#include "interactive.h"
void shlvl(void)
{
long shlvl = 0;
if ( getenv("SHLVL") && (shlvl = atol(getenv("SHLVL"))) < 0 )
shlvl = 0;
if ( shlvl < LONG_MAX )
shlvl++;
char shlvl_string[sizeof(long) * 3];
snprintf(shlvl_string, sizeof(shlvl_string), "%li", shlvl);
setenv("SHLVL", shlvl_string, 1);
}
void text(const char* str)
{
fflush(stdout);
struct winsize ws;
if ( tcgetwinsize(1, &ws) < 0 )
err(2, "tcgetwinsize");
struct wincurpos wcp;
if ( tcgetwincurpos(1, &wcp) < 0 )
err(2, "tcgetwinsize");
size_t columns = ws.ws_col;
size_t column = wcp.wcp_col;
bool blank = false;
while ( str[0] )
{
if ( str[0] == '\n' )
{
putchar('\n');
blank = false;
column = 0;
str++;
continue;
}
else if ( isblank((unsigned char) str[0]) )
{
blank = true;
str++;
continue;
}
size_t word_length = 0;
size_t word_columns = 0;
mbstate_t ps = { 0 };
while ( str[word_length] &&
str[word_length] != '\n' &&
!isblank((unsigned char) str[word_length]) )
{
wchar_t wc;
size_t amount = mbrtowc(&wc, str + word_length, SIZE_MAX, &ps);
if ( amount == (size_t) -2 )
break;
if ( amount == (size_t) -1 )
{
memset(&ps, 0, sizeof(ps));
amount = 1;
}
if ( amount == (size_t) 0 )
break;
word_length += amount;
int width = wcwidth(wc);
if ( width < 0 )
continue;
word_columns += width;
}
if ( (column && blank ? 1 : 0) + word_columns <= columns - column )
{
if ( column && blank )
{
putchar(' ');
column++;
}
blank = false;
fwrite(str, 1, word_length, stdout);
column += word_columns;
if ( column == columns )
column = 0;
}
else
{
if ( column != 0 && column != columns )
putchar('\n');
column = 0;
blank = false;
fwrite(str, 1, word_length, stdout);
column += word_columns;
column %= columns;
}
str += word_length;
}
fflush(stdout);
}
void textf(const char* format, ...)
{
va_list ap;
va_start(ap, format);
char* str;
int len = vasprintf(&str, format, ap);
va_end(ap);
if ( len < 0 )
{
vprintf(format, ap);
return;
}
text(str);
free(str);
}
void prompt(char* buffer,
size_t buffer_size,
const char* question,
const char* answer)
{
while ( true )
{
text(question);
if ( answer )
printf(" [%s] ", answer);
else
printf(" ");
fflush(stdout);
fgets(buffer, buffer_size, stdin);
size_t buffer_length = strlen(buffer);
if ( buffer_length && buffer[buffer_length-1] == '\n' )
buffer[--buffer_length] = '\0';
while ( buffer_length && buffer[buffer_length-1] == ' ' )
buffer[--buffer_length] = '\0';
if ( !strcmp(buffer, "") )
{
if ( !answer )
continue;
strlcpy(buffer, answer, buffer_size);
}
if ( !strcmp(buffer, "!") )
{
printf("Type 'exit' to return to install.\n");
fflush(stdout);
execute((const char*[]) { "sh", NULL }, "f");
continue;
}
if ( !strcmp(buffer, "!man") )
{
execute((const char*[]) { "man", prompt_man_section,
prompt_man_page, NULL }, "f");
continue;
}
break;
}
}
void password(char* buffer,
size_t buffer_size,
const char* question)
{
unsigned int termmode;
gettermmode(0, &termmode);
settermmode(0, termmode & ~TERMMODE_ECHO);
text(question);
printf(" ");
fflush(stdout);
fflush(stdin);
// TODO: This may leave a copy of the password in the stdio buffer.
fgets(buffer, buffer_size, stdin);
fflush(stdin);
printf("\n");
size_t buffer_length = strlen(buffer);
if ( buffer_length && buffer[buffer_length-1] == '\n' )
buffer[--buffer_length] = '\0';
settermmode(0, termmode);
}
static bool has_program(const char* program)
{
return execute((const char*[]) { "which", "--", program, NULL }, "q") == 0;
}
bool missing_program(const char* program)
{
if ( has_program(program) )
return false;
warnx("%s: Program is absent", program);
return true;
}