/* * Copyright (C) 2020, Red Hat Inc. * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "tracker-term-utils.h" #include #include #include #include #include #include static GSubprocess *pager = NULL; static gint stdout_fd = 0; static guint signal_handler_id = 0; gchar * tracker_term_ellipsize (const gchar *str, gint max_len, TrackerEllipsizeMode mode) { gsize size = strlen (str); glong len = g_utf8_strlen (str, size); const gchar *begin, *end, *pos; gchar *substr, *retval; gint i; if (len < max_len) return g_strdup (str); /* Account for the ellipsizing char */ max_len--; if (max_len <= 0) return g_strdup ("…"); begin = str; end = &str[size]; if (mode == TRACKER_ELLIPSIZE_END) { pos = begin; for (i = 0; i < max_len; i++) pos = g_utf8_find_next_char (pos, end); substr = g_strndup (begin, pos - begin); retval = g_strdup_printf ("%s…", substr); g_free (substr); } else { pos = end; for (i = 0; i < max_len; i++) pos = g_utf8_find_prev_char (begin, pos); substr = g_strndup (pos, end - pos); retval = g_strdup_printf ("…%s", substr); g_free (substr); } return retval; } static void fd_term_dimensions (gint fd, guint *cols, guint *rows) { struct winsize ws = { 0 }; if (ioctl(fd, TIOCGWINSZ, &ws) < 0) { *cols = 0; *rows = 0; } *cols = ws.ws_col; *rows = ws.ws_row; } void tracker_term_dimensions (guint *columns, guint *rows) { static guint n_columns = 0; static guint n_rows = 0; if (n_columns == 0 || n_rows == 0) { fd_term_dimensions (STDOUT_FILENO, &n_columns, &n_rows); if (n_columns == 0) n_columns = 80; if (n_rows == 0) n_rows = 24; } if (columns) *columns = n_columns; if (rows) *rows = n_rows; } gboolean tracker_term_is_tty (void) { return isatty (STDOUT_FILENO) > 0; } static gboolean ignore_signal_cb (gpointer user_data) { return G_SOURCE_CONTINUE; } static gchar * best_pager (void) { guint i; gchar *command; const gchar *pagers[] = { "pager", "less", "most", "more", }; for (i = 0; i < G_N_ELEMENTS (pagers); i++) { command = g_find_program_in_path (pagers[i]); if (command) return command; } return NULL; } gboolean tracker_term_pipe_to_pager (void) { GSubprocessLauncher *launcher; gchar *pager_command; gint fds[2]; if (!tracker_term_is_tty ()) return FALSE; if (g_unix_open_pipe (fds, FD_CLOEXEC, NULL) < 0) return FALSE; pager_command = best_pager (); if (!pager_command) return FALSE; /* Ensure this is cached before we redirect to the pager */ tracker_term_dimensions (NULL, NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); g_subprocess_launcher_take_stdin_fd (launcher, fds[0]); g_subprocess_launcher_setenv (launcher, "LESS", "FRSXMK", TRUE); pager = g_subprocess_launcher_spawn (launcher, NULL, pager_command, NULL); g_free (pager_command); stdout_fd = dup (STDOUT_FILENO); close (fds[0]); if (dup2(fds[1], STDOUT_FILENO) < 0) return FALSE; close (fds[1]); signal_handler_id = g_unix_signal_add (SIGINT, ignore_signal_cb, NULL); return TRUE; } gboolean tracker_term_pager_close (void) { if (!pager) return FALSE; fflush (stdout); /* Restore stdout */ dup2 (stdout_fd, STDOUT_FILENO); close (stdout_fd); g_subprocess_send_signal (pager, SIGCONT); g_subprocess_wait (pager, NULL, NULL); g_source_remove (signal_handler_id); return TRUE; }