summaryrefslogtreecommitdiffstats
path: root/src/viewer/mcviewer.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/viewer/mcviewer.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/src/viewer/mcviewer.c b/src/viewer/mcviewer.c
new file mode 100644
index 0000000..0fdf2b7
--- /dev/null
+++ b/src/viewer/mcviewer.c
@@ -0,0 +1,467 @@
+/*
+ Internal file viewer for the Midnight Commander
+ Interface functions
+
+ Copyright (C) 1994-2022
+ Free Software Foundation, Inc
+
+ Written by:
+ Miguel de Icaza, 1994, 1995, 1998
+ Janne Kukonlehto, 1994, 1995
+ Jakub Jelinek, 1995
+ Joseph M. Hinkle, 1996
+ Norbert Warmuth, 1997
+ Pavel Machek, 1998
+ Roland Illig <roland.illig@gmx.de>, 2004, 2005
+ Slava Zanko <slavazanko@google.com>, 2009, 2013
+ Andrew Borodin <aborodin@vmail.ru>, 2009-2022
+ Ilia Maslakov <il.smind@gmail.com>, 2009
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander 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.
+
+ The Midnight Commander 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/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+
+#include "lib/global.h"
+#include "lib/tty/tty.h"
+#include "lib/vfs/vfs.h"
+#include "lib/strutil.h"
+#include "lib/util.h" /* load_file_position() */
+#include "lib/widget.h"
+
+#include "src/filemanager/layout.h"
+#include "src/filemanager/filemanager.h" /* the_menubar */
+
+#include "internal.h"
+
+/*** global variables ****************************************************************************/
+
+mcview_mode_flags_t mcview_global_flags = {
+ .wrap = TRUE,
+ .hex = FALSE,
+ .magic = TRUE,
+ .nroff = FALSE
+};
+
+mcview_mode_flags_t mcview_altered_flags = {
+ .wrap = FALSE,
+ .hex = FALSE,
+ .magic = FALSE,
+ .nroff = FALSE
+};
+
+gboolean mcview_remember_file_position = FALSE;
+
+/* Maxlimit for skipping updates */
+int mcview_max_dirt_limit = 10;
+
+/* Scrolling is done in pages or line increments */
+gboolean mcview_mouse_move_pages = TRUE;
+
+/* end of file will be showen from mcview_show_eof */
+char *mcview_show_eof = NULL;
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+/*** file scope variables ************************************************************************/
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+mcview_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
+{
+ WView *view = (WView *) w;
+ const WRect *r = &view->data_area;
+ gboolean ok = TRUE;
+
+ switch (msg)
+ {
+ case MSG_MOUSE_DOWN:
+ if (mcview_is_in_panel (view))
+ {
+ if (event->y == WIDGET (w->owner)->rect.y)
+ {
+ /* return MOU_UNHANDLED */
+ event->result.abort = TRUE;
+ /* don't draw viewer over menu */
+ ok = FALSE;
+ break;
+ }
+
+ if (!widget_get_state (w, WST_FOCUSED))
+ {
+ /* Grab focus */
+ (void) change_panel ();
+ }
+ }
+ MC_FALLTHROUGH;
+
+ case MSG_MOUSE_CLICK:
+ if (!view->mode_flags.wrap)
+ {
+ /* Scrolling left and right */
+ int x;
+
+ x = event->x + 1; /* FIXME */
+
+ if (x < r->cols * 1 / 4)
+ {
+ mcview_move_left (view, 1);
+ event->result.repeat = msg == MSG_MOUSE_DOWN;
+ }
+ else if (x < r->cols * 3 / 4)
+ {
+ /* ignore the click */
+ ok = FALSE;
+ }
+ else
+ {
+ mcview_move_right (view, 1);
+ event->result.repeat = msg == MSG_MOUSE_DOWN;
+ }
+ }
+ else
+ {
+ /* Scrolling up and down */
+ int y;
+
+ y = event->y + 1; /* FIXME */
+
+ if (y < r->y + r->lines * 1 / 3)
+ {
+ if (mcview_mouse_move_pages)
+ mcview_move_up (view, r->lines / 2);
+ else
+ mcview_move_up (view, 1);
+
+ event->result.repeat = msg == MSG_MOUSE_DOWN;
+ }
+ else if (y < r->y + r->lines * 2 / 3)
+ {
+ /* ignore the click */
+ ok = FALSE;
+ }
+ else
+ {
+ if (mcview_mouse_move_pages)
+ mcview_move_down (view, r->lines / 2);
+ else
+ mcview_move_down (view, 1);
+
+ event->result.repeat = msg == MSG_MOUSE_DOWN;
+ }
+ }
+ break;
+
+ case MSG_MOUSE_SCROLL_UP:
+ mcview_move_up (view, 2);
+ break;
+
+ case MSG_MOUSE_SCROLL_DOWN:
+ mcview_move_down (view, 2);
+ break;
+
+ default:
+ ok = FALSE;
+ break;
+ }
+
+ if (ok)
+ mcview_update (view);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+WView *
+mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
+{
+ WRect r = { y, x, lines, cols };
+ WView *view;
+ Widget *w;
+
+ view = g_new0 (WView, 1);
+ w = WIDGET (view);
+
+ widget_init (w, &r, mcview_callback, mcview_mouse_callback);
+ w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
+ w->keymap = viewer_map;
+
+ mcview_clear_mode_flags (&view->mode_flags);
+ view->hexedit_mode = FALSE;
+ view->hex_keymap = viewer_hex_map;
+ view->hexview_in_text = FALSE;
+ view->locked = FALSE;
+
+ view->dpy_frame_size = is_panel ? 1 : 0;
+ view->converter = str_cnv_from_term;
+
+ mcview_init (view);
+
+ if (mcview_global_flags.hex)
+ mcview_toggle_hex_mode (view);
+ if (mcview_global_flags.nroff)
+ mcview_toggle_nroff_mode (view);
+ if (mcview_global_flags.wrap)
+ mcview_toggle_wrap_mode (view);
+ if (mcview_global_flags.magic)
+ mcview_toggle_magic_mode (view);
+
+ return view;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/** Real view only */
+
+gboolean
+mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line,
+ off_t search_start, off_t search_end)
+{
+ gboolean succeeded;
+ WView *lc_mcview;
+ WDialog *view_dlg;
+ Widget *vw, *b;
+ WGroup *g;
+
+ /* Create dialog and widgets, put them on the dialog */
+ view_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, mcview_dialog_callback,
+ NULL, "[Internal File Viewer]", NULL);
+ vw = WIDGET (view_dlg);
+ widget_want_tab (vw, TRUE);
+
+ g = GROUP (view_dlg);
+
+ lc_mcview = mcview_new (vw->rect.y, vw->rect.x, vw->rect.lines - 1, vw->rect.cols, FALSE);
+ group_add_widget_autopos (g, lc_mcview, WPOS_KEEP_ALL, NULL);
+
+ b = WIDGET (buttonbar_new ());
+ group_add_widget_autopos (g, b, b->pos_flags, NULL);
+
+ view_dlg->get_title = mcview_get_title;
+
+ succeeded =
+ mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line, search_start,
+ search_end);
+
+ if (succeeded)
+ dlg_run (view_dlg);
+ else
+ dlg_stop (view_dlg);
+
+ if (widget_get_state (vw, WST_CLOSED))
+ widget_destroy (vw);
+
+ return succeeded;
+}
+
+/* {{{ Miscellaneous functions }}} */
+
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+mcview_load (WView * view, const char *command, const char *file, int start_line,
+ off_t search_start, off_t search_end)
+{
+ gboolean retval = FALSE;
+ vfs_path_t *vpath = NULL;
+
+ g_assert (view->bytes_per_line != 0);
+
+ view->filename_vpath = vfs_path_from_str (file);
+
+ /* get working dir */
+ if (file != NULL && file[0] != '\0')
+ {
+ vfs_path_free (view->workdir_vpath, TRUE);
+
+ if (!g_path_is_absolute (file))
+ {
+ vfs_path_t *p;
+
+ p = vfs_path_clone (vfs_get_raw_current_dir ());
+ view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
+ vfs_path_free (p, TRUE);
+ }
+ else
+ {
+ /* try extract path from filename */
+ const char *fname;
+ char *dir;
+
+ fname = x_basename (file);
+ dir = g_strndup (file, (size_t) (fname - file));
+ view->workdir_vpath = vfs_path_from_str (dir);
+ g_free (dir);
+ }
+ }
+
+ if (!mcview_is_in_panel (view))
+ view->dpy_text_column = 0;
+
+#ifdef HAVE_CHARSET
+ mcview_set_codeset (view);
+#endif
+
+ if (command != NULL && (view->mode_flags.magic || file == NULL || file[0] == '\0'))
+ retval = mcview_load_command_output (view, command);
+ else if (file != NULL && file[0] != '\0')
+ {
+ int fd;
+ char tmp[BUF_MEDIUM];
+ struct stat st;
+
+ /* Open the file */
+ vpath = vfs_path_from_str (file);
+ fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
+ if (fd == -1)
+ {
+ g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
+ file, unix_error_string (errno));
+ mcview_close_datasource (view);
+ mcview_show_error (view, tmp);
+ vfs_path_free (view->filename_vpath, TRUE);
+ view->filename_vpath = NULL;
+ vfs_path_free (view->workdir_vpath, TRUE);
+ view->workdir_vpath = NULL;
+ goto finish;
+ }
+
+ /* Make sure we are working with a regular file */
+ if (mc_fstat (fd, &st) == -1)
+ {
+ mc_close (fd);
+ g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
+ file, unix_error_string (errno));
+ mcview_close_datasource (view);
+ mcview_show_error (view, tmp);
+ vfs_path_free (view->filename_vpath, TRUE);
+ view->filename_vpath = NULL;
+ vfs_path_free (view->workdir_vpath, TRUE);
+ view->workdir_vpath = NULL;
+ goto finish;
+ }
+
+ if (!S_ISREG (st.st_mode))
+ {
+ mc_close (fd);
+ mcview_close_datasource (view);
+ mcview_show_error (view, _("Cannot view: not a regular file"));
+ vfs_path_free (view->filename_vpath, TRUE);
+ view->filename_vpath = NULL;
+ vfs_path_free (view->workdir_vpath, TRUE);
+ view->workdir_vpath = NULL;
+ goto finish;
+ }
+
+ if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
+ {
+ /* Must be one of those nice files that grow (/proc) */
+ mcview_set_datasource_vfs_pipe (view, fd);
+ }
+ else
+ {
+ if (view->mode_flags.magic)
+ {
+ int type;
+
+ type = get_compression_type (fd, file);
+
+ if (type != COMPRESSION_NONE)
+ {
+ char *tmp_filename;
+ vfs_path_t *vpath1;
+ int fd1;
+
+ tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
+ vpath1 = vfs_path_from_str (tmp_filename);
+ g_free (tmp_filename);
+ fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
+ vfs_path_free (vpath1, TRUE);
+
+ if (fd1 == -1)
+ {
+ g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
+ file, unix_error_string (errno));
+ mcview_close_datasource (view);
+ mcview_show_error (view, tmp);
+ }
+ else
+ {
+ mc_close (fd);
+ fd = fd1;
+ mc_fstat (fd, &st);
+ }
+ }
+ }
+
+ mcview_set_datasource_file (view, fd, &st);
+ }
+ retval = TRUE;
+ }
+
+ finish:
+ view->command = g_strdup (command);
+ view->dpy_start = 0;
+ view->dpy_paragraph_skip_lines = 0;
+ mcview_state_machine_init (&view->dpy_state_top, 0);
+ view->dpy_wrap_dirty = FALSE;
+ view->force_max = -1;
+ view->dpy_text_column = 0;
+
+ mcview_compute_areas (view);
+ mcview_update_bytes_per_line (view);
+
+ if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
+ {
+ long line, col;
+ off_t new_offset, max_offset;
+
+ load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
+ max_offset = mcview_get_filesize (view) - 1;
+ if (max_offset < 0)
+ new_offset = 0;
+ else
+ new_offset = MIN (new_offset, max_offset);
+ if (!view->mode_flags.hex)
+ {
+ view->dpy_start = mcview_bol (view, new_offset, 0);
+ view->dpy_wrap_dirty = TRUE;
+ }
+ else
+ {
+ view->dpy_start = new_offset - new_offset % view->bytes_per_line;
+ view->hex_cursor = new_offset;
+ }
+ }
+ else if (start_line > 0)
+ mcview_moveto (view, start_line - 1, 0);
+
+ view->search_start = search_start;
+ view->search_end = search_end;
+ view->hexedit_lownibble = FALSE;
+ view->hexview_in_text = FALSE;
+ view->change_list = NULL;
+ vfs_path_free (vpath, TRUE);
+ return retval;
+}
+
+/* --------------------------------------------------------------------------------------------- */