Ticket #2370 (Commands like 'cat myfile | mcview' doesn't work)
[midnight-commander.git] / src / viewer / mcviewer.c
blob37bc8210a9a64bf6655e677ec7e8bf9542b3214b
1 /*
2 Internal file viewer for the Midnight Commander
3 Interface functions
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software; you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
38 #include <config.h>
39 #include <errno.h>
40 #include <fcntl.h>
42 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/tty/mouse.h"
45 #include "lib/vfs/vfs.h"
46 #include "lib/strutil.h"
47 #include "lib/util.h" /* load_file_position() */
48 #include "lib/widget.h"
49 #include "lib/charsets.h"
51 #include "src/main.h"
53 #include "src/filemanager/layout.h" /* menubar_visible */
54 #include "src/filemanager/midnight.h" /* the_menubar */
56 #include "internal.h"
57 #include "mcviewer.h"
59 /*** global variables ****************************************************************************/
61 int mcview_default_hex_mode = 0;
62 int mcview_default_nroff_flag = 0;
63 int mcview_global_wrap_mode = 1;
64 int mcview_default_magic_flag = 1;
66 int mcview_altered_hex_mode = 0;
67 int mcview_altered_magic_flag = 0;
68 int mcview_altered_nroff_flag = 0;
70 int mcview_remember_file_position = FALSE;
72 /* Maxlimit for skipping updates */
73 int mcview_max_dirt_limit = 10;
75 /* Scrolling is done in pages or line increments */
76 int mcview_mouse_move_pages = 1;
78 /* end of file will be showen from mcview_show_eof */
79 char *mcview_show_eof = NULL;
81 /*** file scope macro definitions ****************************************************************/
83 /*** file scope type declarations ****************************************************************/
85 /*** file scope variables ************************************************************************/
88 /*** file scope functions ************************************************************************/
89 /* --------------------------------------------------------------------------------------------- */
91 /** Both views */
93 static int
94 mcview_event (mcview_t * view, Gpm_Event * event, int *result)
96 screen_dimen y, x;
98 *result = MOU_NORMAL;
100 /* rest of the upper frame, the menu is invisible - call menu */
101 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
103 event->x += view->widget.x;
104 *result = the_menubar->widget.mouse (event, the_menubar);
105 return 0; /* don't draw viewer over menu */
108 /* We are not interested in the release events */
109 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
110 return 0;
112 /* Wheel events */
113 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN))
115 mcview_move_up (view, 2);
116 return 1;
118 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN))
120 mcview_move_down (view, 2);
121 return 1;
124 x = event->x;
125 y = event->y;
127 /* Scrolling left and right */
128 if (!view->text_wrap_mode)
130 if (x < view->data_area.width * 1 / 4)
132 mcview_move_left (view, 1);
133 goto processed;
135 else if (x < view->data_area.width * 3 / 4)
137 /* ignore the click */
139 else
141 mcview_move_right (view, 1);
142 goto processed;
146 /* Scrolling up and down */
147 if (y < view->data_area.top + view->data_area.height * 1 / 3)
149 if (mcview_mouse_move_pages)
150 mcview_move_up (view, view->data_area.height / 2);
151 else
152 mcview_move_up (view, 1);
153 goto processed;
155 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
157 /* ignore the click */
159 else
161 if (mcview_mouse_move_pages)
162 mcview_move_down (view, view->data_area.height / 2);
163 else
164 mcview_move_down (view, 1);
165 goto processed;
168 return 0;
170 processed:
171 *result = MOU_REPEAT;
172 return 1;
175 /* --------------------------------------------------------------------------------------------- */
176 /** Real view only */
178 static int
179 mcview_real_event (Gpm_Event * event, void *x)
181 mcview_t *view = (mcview_t *) x;
182 int result;
184 if (mcview_event (view, event, &result))
185 mcview_update (view);
186 return result;
190 /* --------------------------------------------------------------------------------------------- */
191 /*** public functions ****************************************************************************/
192 /* --------------------------------------------------------------------------------------------- */
194 mcview_t *
195 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
197 mcview_t *view = g_new0 (mcview_t, 1);
199 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
201 view->hex_mode = FALSE;
202 view->hexedit_mode = FALSE;
203 view->locked = FALSE;
204 view->hexview_in_text = FALSE;
205 view->text_nroff_mode = FALSE;
206 view->text_wrap_mode = FALSE;
207 view->magic_mode = FALSE;
209 view->dpy_frame_size = is_panel ? 1 : 0;
210 view->converter = str_cnv_from_term;
212 mcview_init (view);
214 if (mcview_default_hex_mode)
215 mcview_toggle_hex_mode (view);
216 if (mcview_default_nroff_flag)
217 mcview_toggle_nroff_mode (view);
218 if (mcview_global_wrap_mode)
219 mcview_toggle_wrap_mode (view);
220 if (mcview_default_magic_flag)
221 mcview_toggle_magic_mode (view);
223 return view;
226 /* --------------------------------------------------------------------------------------------- */
227 /** Real view only */
229 mcview_ret_t
230 mcview_viewer (const char *command, const char *file, int start_line)
232 gboolean succeeded;
233 mcview_t *lc_mcview;
234 Dlg_head *view_dlg;
235 mcview_ret_t ret;
237 /* Create dialog and widgets, put them on the dialog */
238 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback,
239 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
241 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
242 add_widget (view_dlg, lc_mcview);
244 add_widget (view_dlg, buttonbar_new (TRUE));
246 view_dlg->get_title = mcview_get_title;
248 succeeded = mcview_load (lc_mcview, command, file, start_line);
250 if (succeeded)
252 run_dlg (view_dlg);
254 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
255 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
257 else
259 view_dlg->state = DLG_CLOSED;
260 ret = MCVIEW_EXIT_FAILURE;
263 if (view_dlg->state == DLG_CLOSED)
264 destroy_dlg (view_dlg);
266 return ret;
269 /* {{{ Miscellaneous functions }}} */
271 /* --------------------------------------------------------------------------------------------- */
273 gboolean
274 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
276 gboolean retval = FALSE;
278 assert (view->bytes_per_line != 0);
280 view->filename = g_strdup (file);
282 if ((view->workdir == NULL) && (file != NULL))
284 if (!g_path_is_absolute (file))
285 view->workdir = vfs_get_current_dir ();
286 else
288 /* try extract path form filename */
289 char *dirname;
291 dirname = g_path_get_dirname (file);
292 if (strcmp (dirname, ".") != 0)
293 view->workdir = dirname;
294 else
296 g_free (dirname);
297 view->workdir = vfs_get_current_dir ();
302 if (!mcview_is_in_panel (view))
303 view->dpy_text_column = 0;
305 mcview_set_codeset (view);
307 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
308 retval = mcview_load_command_output (view, command);
309 else if (file != NULL && file[0] != '\0')
311 int fd = -1;
312 char tmp[BUF_MEDIUM];
313 struct stat st;
315 /* See if "-" filename refers to a standart input pipe */
316 if (file[0] == '-' && file[1] == '\0' && !isatty (fileno(stdin)))
318 mcview_set_datasource_stdin_pipe (view);
319 retval = TRUE;
320 goto finish;
323 /* Open the file */
324 fd = mc_open (file, O_RDONLY | O_NONBLOCK);
325 if (fd == -1)
327 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
328 file, unix_error_string (errno));
329 mcview_show_error (view, tmp);
330 g_free (view->filename);
331 view->filename = NULL;
332 g_free (view->workdir);
333 view->workdir = NULL;
334 goto finish;
337 /* Make sure we are working with a regular file */
338 if (mc_fstat (fd, &st) == -1)
340 mc_close (fd);
341 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
342 file, unix_error_string (errno));
343 mcview_show_error (view, tmp);
344 g_free (view->filename);
345 view->filename = NULL;
346 g_free (view->workdir);
347 view->workdir = NULL;
348 goto finish;
351 if (!S_ISREG (st.st_mode))
353 mc_close (fd);
354 mcview_show_error (view, _("Cannot view: not a regular file"));
355 g_free (view->filename);
356 view->filename = NULL;
357 g_free (view->workdir);
358 view->workdir = NULL;
359 goto finish;
362 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
364 /* Must be one of those nice files that grow (/proc) */
365 mcview_set_datasource_vfs_pipe (view, fd);
367 else
369 int type;
371 type = get_compression_type (fd, file);
373 if (view->magic_mode && (type != COMPRESSION_NONE))
375 g_free (view->filename);
376 view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
378 mcview_set_datasource_file (view, fd, &st);
380 retval = TRUE;
383 finish:
384 view->command = g_strdup (command);
385 view->dpy_start = 0;
386 view->search_start = 0;
387 view->search_end = 0;
388 view->dpy_text_column = 0;
390 mcview_compute_areas (view);
391 mcview_update_bytes_per_line (view);
393 if (mcview_remember_file_position && view->filename != NULL && start_line == 0)
395 char *canon_fname;
396 long line, col;
397 off_t new_offset, max_offset;
398 vfs_path_t *vpath;
400 vpath = vfs_path_from_str (view->filename);
401 canon_fname = vfs_path_to_str (vpath);
402 load_file_position (canon_fname, &line, &col, &new_offset, &view->saved_bookmarks);
403 max_offset = mcview_get_filesize (view) - 1;
404 if (max_offset < 0)
405 new_offset = 0;
406 else
407 new_offset = min (new_offset, max_offset);
408 if (!view->hex_mode)
409 view->dpy_start = mcview_bol (view, new_offset, 0);
410 else
412 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
413 view->hex_cursor = new_offset;
415 g_free (canon_fname);
416 vfs_path_free (vpath);
418 else if (start_line > 0)
419 mcview_moveto (view, start_line - 1, 0);
421 view->hexedit_lownibble = FALSE;
422 view->hexview_in_text = FALSE;
423 view->change_list = NULL;
424 return retval;
427 /* --------------------------------------------------------------------------------------------- */