mirror of
https://github.com/flatpak/flatpak.git
synced 2026-02-02 03:51:28 -05:00
These functions are to do with being an interactive, terminal-oriented CLI/TUI, so it would be inappropriate for library code in libflatpak to call them, and it would also be inappropriate for daemons like the session and system helpers to call them. In fact all calls to these were already isolated to app/, so we can easily move the terminal-related utilities themselves into app/. As well as shrinking libflatpak, this makes it obvious that the system helper does not actually need to call flatpak_disable_fancy_output(): it does not link any code that would be affected by that API call. Signed-off-by: Simon McVittie <smcv@collabora.com>
524 lines
11 KiB
C
524 lines
11 KiB
C
/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
|
|
* Copyright © 1995-1998 Free Software Foundation, Inc.
|
|
* Copyright © 2014-2019 Red Hat, Inc
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "flatpak-tty-utils-private.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
static int fancy_output = -1;
|
|
|
|
void
|
|
flatpak_disable_fancy_output (void)
|
|
{
|
|
fancy_output = FALSE;
|
|
}
|
|
|
|
void
|
|
flatpak_enable_fancy_output (void)
|
|
{
|
|
fancy_output = TRUE;
|
|
}
|
|
|
|
gboolean
|
|
flatpak_fancy_output (void)
|
|
{
|
|
static gsize fancy_output_once = 0;
|
|
enum {
|
|
PLAIN_OUTPUT = 1,
|
|
FANCY_OUTPUT = 2
|
|
};
|
|
|
|
if (fancy_output != -1)
|
|
return fancy_output;
|
|
|
|
if (g_once_init_enter (&fancy_output_once))
|
|
{
|
|
if (g_strcmp0 (g_getenv ("FLATPAK_FANCY_OUTPUT"), "0") == 0)
|
|
g_once_init_leave (&fancy_output_once, PLAIN_OUTPUT);
|
|
else if (getenv ("G_MESSAGES_DEBUG"))
|
|
g_once_init_leave (&fancy_output_once, PLAIN_OUTPUT);
|
|
else if (!isatty (STDOUT_FILENO))
|
|
g_once_init_leave (&fancy_output_once, PLAIN_OUTPUT);
|
|
else
|
|
g_once_init_leave (&fancy_output_once, FANCY_OUTPUT);
|
|
}
|
|
|
|
return fancy_output_once == FANCY_OUTPUT;
|
|
}
|
|
|
|
gboolean
|
|
flatpak_allow_fuzzy_matching (const char *term)
|
|
{
|
|
if (strchr (term, '/') != NULL || strchr (term, '.') != NULL)
|
|
return FALSE;
|
|
|
|
/* This env var is used by the unit tests and only skips the tty test not the
|
|
* check above.
|
|
*/
|
|
if (g_strcmp0 (g_getenv ("FLATPAK_FORCE_ALLOW_FUZZY_MATCHING"), "1") == 0)
|
|
return TRUE;
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
char *
|
|
flatpak_prompt (gboolean allow_empty,
|
|
const char *prompt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
while (TRUE)
|
|
{
|
|
g_print ("%s: ", s);
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
{
|
|
g_print ("n\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
return NULL;
|
|
|
|
g_strstrip (buf);
|
|
|
|
if (buf[0] != 0 || allow_empty)
|
|
return g_strdup (buf);
|
|
}
|
|
}
|
|
|
|
char *
|
|
flatpak_password_prompt (const char *prompt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
gboolean was_echo;
|
|
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
while (TRUE)
|
|
{
|
|
g_print ("%s: ", s);
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
return NULL;
|
|
|
|
was_echo = flatpak_set_tty_echo (FALSE);
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
return NULL;
|
|
|
|
flatpak_set_tty_echo (was_echo);
|
|
|
|
g_strstrip (buf);
|
|
|
|
/* We stole the return, so manual new line */
|
|
g_print ("\n");
|
|
return g_strdup (buf);
|
|
}
|
|
}
|
|
|
|
|
|
gboolean
|
|
flatpak_yes_no_prompt (gboolean default_yes, const char *prompt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
while (TRUE)
|
|
{
|
|
g_print ("%s %s: ", s, default_yes ? "[Y/n]" : "[y/n]");
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
{
|
|
g_print ("n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
return FALSE;
|
|
|
|
g_strstrip (buf);
|
|
|
|
if (default_yes && strlen (buf) == 0)
|
|
return TRUE;
|
|
|
|
if (g_ascii_strcasecmp (buf, "y") == 0 ||
|
|
g_ascii_strcasecmp (buf, "yes") == 0)
|
|
return TRUE;
|
|
|
|
if (g_ascii_strcasecmp (buf, "n") == 0 ||
|
|
g_ascii_strcasecmp (buf, "no") == 0)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
is_number (const char *s)
|
|
{
|
|
if (*s == '\0')
|
|
return FALSE;
|
|
|
|
while (*s != 0)
|
|
{
|
|
if (!g_ascii_isdigit (*s))
|
|
return FALSE;
|
|
s++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
long
|
|
flatpak_number_prompt (gboolean default_yes, int min, int max, const char *prompt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
while (TRUE)
|
|
{
|
|
g_print ("%s [%d-%d]: ", s, min, max);
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
{
|
|
g_print ("0\n");
|
|
return 0;
|
|
}
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
return 0;
|
|
|
|
g_strstrip (buf);
|
|
|
|
if (default_yes && strlen (buf) == 0 &&
|
|
max - min == 1 && min == 0)
|
|
return 1;
|
|
|
|
if (is_number (buf))
|
|
{
|
|
long res = strtol (buf, NULL, 10);
|
|
|
|
if (res >= min && res <= max)
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
parse_range (const char *s, int *a, int *b)
|
|
{
|
|
char *p;
|
|
|
|
p = strchr (s, '-');
|
|
if (!p)
|
|
return FALSE;
|
|
|
|
p++;
|
|
p[-1] = '\0';
|
|
|
|
if (is_number (s) && is_number (p))
|
|
{
|
|
*a = (int) strtol (s, NULL, 10);
|
|
*b = (int) strtol (p, NULL, 10);
|
|
p[-1] = '-';
|
|
return TRUE;
|
|
}
|
|
|
|
p[-1] = '-';
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
add_number (GArray *numbers,
|
|
int num)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < numbers->len; i++)
|
|
{
|
|
if (g_array_index (numbers, int, i) == num)
|
|
return;
|
|
}
|
|
|
|
g_array_append_val (numbers, num);
|
|
}
|
|
|
|
int *
|
|
flatpak_parse_numbers (const char *buf,
|
|
int min,
|
|
int max)
|
|
{
|
|
g_autoptr(GArray) numbers = g_array_new (FALSE, FALSE, sizeof (int));
|
|
g_auto(GStrv) parts = g_strsplit_set (buf, " ,", 0);
|
|
int i, j;
|
|
|
|
for (i = 0; parts[i]; i++)
|
|
{
|
|
int a, b;
|
|
|
|
g_strstrip (parts[i]);
|
|
|
|
if (parse_range (parts[i], &a, &b) &&
|
|
min <= a && a <= max &&
|
|
min <= b && b <= max)
|
|
{
|
|
for (j = a; j <= b; j++)
|
|
add_number (numbers, j);
|
|
}
|
|
else if (is_number (parts[i]))
|
|
{
|
|
int res = (int) strtol (parts[i], NULL, 10);
|
|
if (min <= res && res <= max)
|
|
add_number (numbers, res);
|
|
else
|
|
return NULL;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
j = 0;
|
|
g_array_append_val (numbers, j);
|
|
|
|
return (int *) g_array_free (g_steal_pointer (&numbers), FALSE);
|
|
}
|
|
|
|
/* Returns a 0-terminated array of ints. Free with g_free */
|
|
int *
|
|
flatpak_numbers_prompt (gboolean default_yes, int min, int max, const char *prompt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
g_autofree int *choice = g_new0 (int, 2);
|
|
int *numbers;
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
while (TRUE)
|
|
{
|
|
g_print ("%s [%d-%d]: ", s, min, max);
|
|
|
|
if (!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))
|
|
{
|
|
g_print ("0\n");
|
|
choice[0] = 0;
|
|
return g_steal_pointer (&choice);
|
|
}
|
|
|
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
|
{
|
|
choice[0] = 0;
|
|
return g_steal_pointer (&choice);
|
|
}
|
|
|
|
g_strstrip (buf);
|
|
|
|
if (default_yes && strlen (buf) == 0 &&
|
|
max - min == 1 && min == 0)
|
|
{
|
|
choice[0] = 0;
|
|
return g_steal_pointer (&choice);
|
|
}
|
|
|
|
numbers = flatpak_parse_numbers (buf, min, max);
|
|
if (numbers)
|
|
return numbers;
|
|
}
|
|
}
|
|
|
|
void
|
|
flatpak_format_choices (const char **choices,
|
|
const char *prompt,
|
|
...)
|
|
{
|
|
va_list var_args;
|
|
g_autofree char *s = NULL;
|
|
int i;
|
|
|
|
va_start (var_args, prompt);
|
|
s = g_strdup_vprintf (prompt, var_args);
|
|
va_end (var_args);
|
|
|
|
g_print ("%s\n\n", s);
|
|
for (i = 0; choices[i]; i++)
|
|
g_print (" %2d) %s\n", i + 1, choices[i]);
|
|
g_print ("\n");
|
|
}
|
|
|
|
void
|
|
flatpak_get_window_size (int *rows, int *cols)
|
|
{
|
|
struct winsize w;
|
|
|
|
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
|
|
{
|
|
/* For whatever reason, in buildbot this returns 0, 0 so add a fallback */
|
|
if (w.ws_row == 0)
|
|
w.ws_row = 24;
|
|
if (w.ws_col == 0)
|
|
w.ws_col = 80;
|
|
*rows = w.ws_row;
|
|
*cols = w.ws_col;
|
|
}
|
|
else
|
|
{
|
|
*rows = 24;
|
|
*cols = 80;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
flatpak_set_tty_echo (gboolean echo)
|
|
{
|
|
struct termios term;
|
|
gboolean was;
|
|
|
|
tcgetattr (STDIN_FILENO, &term);
|
|
was = (term.c_lflag & ECHO) != 0;
|
|
|
|
if (echo)
|
|
term.c_lflag |= ECHO;
|
|
else
|
|
term.c_lflag &= ~ECHO;
|
|
tcsetattr (STDIN_FILENO, TCSANOW, &term);
|
|
|
|
return was;
|
|
}
|
|
|
|
gboolean
|
|
flatpak_get_cursor_pos (int * row, int *col)
|
|
{
|
|
fd_set readset;
|
|
struct timeval time;
|
|
struct termios term, initial_term;
|
|
int res = 0;
|
|
|
|
tcgetattr (STDIN_FILENO, &initial_term);
|
|
term = initial_term;
|
|
term.c_lflag &= ~ICANON;
|
|
term.c_lflag &= ~ECHO;
|
|
tcsetattr (STDIN_FILENO, TCSANOW, &term);
|
|
|
|
printf ("\033[6n");
|
|
fflush (stdout);
|
|
|
|
FD_ZERO (&readset);
|
|
FD_SET (STDIN_FILENO, &readset);
|
|
time.tv_sec = 0;
|
|
time.tv_usec = 100000;
|
|
|
|
if (select (STDIN_FILENO + 1, &readset, NULL, NULL, &time) == 1)
|
|
res = scanf ("\033[%d;%dR", row, col);
|
|
|
|
tcsetattr (STDIN_FILENO, TCSADRAIN, &initial_term);
|
|
|
|
return res == 2;
|
|
}
|
|
|
|
void
|
|
flatpak_hide_cursor (void)
|
|
{
|
|
const size_t flatpak_hide_cursor_len = strlen (FLATPAK_ANSI_HIDE_CURSOR);
|
|
const ssize_t write_ret = write (STDOUT_FILENO, FLATPAK_ANSI_HIDE_CURSOR,
|
|
flatpak_hide_cursor_len);
|
|
|
|
if (write_ret < 0)
|
|
g_warning ("write() failed: %zd = write(STDOUT_FILENO, FLATPAK_ANSI_HIDE_CURSOR, %zu)",
|
|
write_ret, flatpak_hide_cursor_len);
|
|
}
|
|
|
|
void
|
|
flatpak_show_cursor (void)
|
|
{
|
|
const size_t flatpak_show_cursor_len = strlen (FLATPAK_ANSI_SHOW_CURSOR);
|
|
const ssize_t write_ret = write (STDOUT_FILENO, FLATPAK_ANSI_SHOW_CURSOR,
|
|
flatpak_show_cursor_len);
|
|
|
|
if (write_ret < 0)
|
|
g_warning ("write() failed: %zd = write(STDOUT_FILENO, FLATPAK_ANSI_SHOW_CURSOR, %zu)",
|
|
write_ret, flatpak_show_cursor_len);
|
|
}
|
|
|
|
void
|
|
flatpak_enable_raw_mode (void)
|
|
{
|
|
struct termios raw;
|
|
|
|
tcgetattr (STDIN_FILENO, &raw);
|
|
|
|
raw.c_lflag &= ~(ECHO | ICANON);
|
|
|
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw);
|
|
}
|
|
|
|
void
|
|
flatpak_disable_raw_mode (void)
|
|
{
|
|
struct termios raw;
|
|
|
|
tcgetattr (STDIN_FILENO, &raw);
|
|
|
|
raw.c_lflag |= (ECHO | ICANON);
|
|
|
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw);
|
|
}
|
|
|
|
void
|
|
flatpak_print_escaped_string (const char *s,
|
|
FlatpakEscapeFlags flags)
|
|
{
|
|
g_autofree char *escaped = flatpak_escape_string (s, flags);
|
|
g_print ("%s", escaped);
|
|
}
|