Check assert.h header and use it conditionally.
[midnight-commander.git] / src / viewer / mcviewer.c
blob14a98d69ba194fa1151e2287e38fb7ff5166b746
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, 2011
7 The Free Software Foundation, Inc.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009
19 Ilia Maslakov <il.smind@gmail.com>, 2009
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
37 #include <config.h>
38 #include <errno.h>
39 #include <fcntl.h>
41 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/mouse.h"
44 #include "lib/vfs/vfs.h"
45 #include "lib/strutil.h"
46 #include "lib/util.h" /* load_file_position() */
47 #include "lib/widget.h"
48 #include "lib/charsets.h"
50 #include "src/main.h"
52 #include "src/filemanager/layout.h" /* menubar_visible */
53 #include "src/filemanager/midnight.h" /* the_menubar */
55 #include "internal.h"
56 #include "mcviewer.h"
58 /*** global variables ****************************************************************************/
60 int mcview_default_hex_mode = 0;
61 int mcview_default_nroff_flag = 0;
62 int mcview_global_wrap_mode = 1;
63 int mcview_default_magic_flag = 1;
65 int mcview_altered_hex_mode = 0;
66 int mcview_altered_magic_flag = 0;
67 int mcview_altered_nroff_flag = 0;
69 int mcview_remember_file_position = FALSE;
71 /* Maxlimit for skipping updates */
72 int mcview_max_dirt_limit = 10;
74 /* Scrolling is done in pages or line increments */
75 int mcview_mouse_move_pages = 1;
77 /* end of file will be showen from mcview_show_eof */
78 char *mcview_show_eof = NULL;
80 /*** file scope macro definitions ****************************************************************/
82 /*** file scope type declarations ****************************************************************/
84 /*** file scope variables ************************************************************************/
87 /*** file scope functions ************************************************************************/
88 /* --------------------------------------------------------------------------------------------- */
90 /** Both views */
92 static int
93 mcview_event (mcview_t * view, Gpm_Event * event, int *result)
95 screen_dimen y, x;
97 *result = MOU_NORMAL;
99 /* rest of the upper frame, the menu is invisible - call menu */
100 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
102 event->x += view->widget.x;
103 *result = the_menubar->widget.mouse (event, the_menubar);
104 return 0; /* don't draw viewer over menu */
107 /* We are not interested in the release events */
108 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
109 return 0;
111 /* Wheel events */
112 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN))
114 mcview_move_up (view, 2);
115 return 1;
117 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN))
119 mcview_move_down (view, 2);
120 return 1;
123 x = event->x;
124 y = event->y;
126 /* Scrolling left and right */
127 if (!view->text_wrap_mode)
129 if (x < view->data_area.width * 1 / 4)
131 mcview_move_left (view, 1);
132 goto processed;
134 else if (x < view->data_area.width * 3 / 4)
136 /* ignore the click */
138 else
140 mcview_move_right (view, 1);
141 goto processed;
145 /* Scrolling up and down */
146 if (y < view->data_area.top + view->data_area.height * 1 / 3)
148 if (mcview_mouse_move_pages)
149 mcview_move_up (view, view->data_area.height / 2);
150 else
151 mcview_move_up (view, 1);
152 goto processed;
154 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
156 /* ignore the click */
158 else
160 if (mcview_mouse_move_pages)
161 mcview_move_down (view, view->data_area.height / 2);
162 else
163 mcview_move_down (view, 1);
164 goto processed;
167 return 0;
169 processed:
170 *result = MOU_REPEAT;
171 return 1;
174 /* --------------------------------------------------------------------------------------------- */
175 /** Real view only */
177 static int
178 mcview_real_event (Gpm_Event * event, void *x)
180 mcview_t *view = (mcview_t *) x;
181 int result;
183 if (mcview_event (view, event, &result))
184 mcview_update (view);
185 return result;
189 /* --------------------------------------------------------------------------------------------- */
190 /*** public functions ****************************************************************************/
191 /* --------------------------------------------------------------------------------------------- */
193 mcview_t *
194 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
196 mcview_t *view = g_new0 (mcview_t, 1);
198 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
200 view->hex_mode = FALSE;
201 view->hexedit_mode = FALSE;
202 view->locked = FALSE;
203 view->hexview_in_text = FALSE;
204 view->text_nroff_mode = FALSE;
205 view->text_wrap_mode = FALSE;
206 view->magic_mode = FALSE;
208 view->dpy_frame_size = is_panel ? 1 : 0;
209 view->converter = str_cnv_from_term;
211 mcview_init (view);
213 if (mcview_default_hex_mode)
214 mcview_toggle_hex_mode (view);
215 if (mcview_default_nroff_flag)
216 mcview_toggle_nroff_mode (view);
217 if (mcview_global_wrap_mode)
218 mcview_toggle_wrap_mode (view);
219 if (mcview_default_magic_flag)
220 mcview_toggle_magic_mode (view);
222 return view;
225 /* --------------------------------------------------------------------------------------------- */
226 /** Real view only */
228 mcview_ret_t
229 mcview_viewer (const char *command, const char *file, int start_line)
231 gboolean succeeded;
232 mcview_t *lc_mcview;
233 Dlg_head *view_dlg;
234 mcview_ret_t ret;
236 /* Create dialog and widgets, put them on the dialog */
237 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback,
238 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
240 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
241 add_widget (view_dlg, lc_mcview);
243 add_widget (view_dlg, buttonbar_new (TRUE));
245 view_dlg->get_title = mcview_get_title;
247 succeeded = mcview_load (lc_mcview, command, file, start_line);
249 if (succeeded)
251 run_dlg (view_dlg);
253 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
254 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
256 else
258 view_dlg->state = DLG_CLOSED;
259 ret = MCVIEW_EXIT_FAILURE;
262 if (view_dlg->state == DLG_CLOSED)
263 destroy_dlg (view_dlg);
265 return ret;
268 /* {{{ Miscellaneous functions }}} */
270 /* --------------------------------------------------------------------------------------------- */
272 gboolean
273 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
275 gboolean retval = FALSE;
277 #ifdef HAVE_ASSERT_H
278 assert (view->bytes_per_line != 0);
279 #endif
281 view->filename = g_strdup (file);
283 if ((view->workdir == NULL) && (file != NULL))
285 if (!g_path_is_absolute (file))
286 view->workdir = vfs_get_current_dir ();
287 else
289 /* try extract path form filename */
290 char *dirname;
292 dirname = g_path_get_dirname (file);
293 if (strcmp (dirname, ".") != 0)
294 view->workdir = dirname;
295 else
297 g_free (dirname);
298 view->workdir = vfs_get_current_dir ();
303 if (!mcview_is_in_panel (view))
304 view->dpy_text_column = 0;
306 mcview_set_codeset (view);
308 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
309 retval = mcview_load_command_output (view, command);
310 else if (file != NULL && file[0] != '\0')
312 int fd = -1;
313 char tmp[BUF_MEDIUM];
314 struct stat st;
316 /* Open the file */
317 fd = mc_open (file, O_RDONLY | O_NONBLOCK);
318 if (fd == -1)
320 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
321 file, unix_error_string (errno));
322 mcview_show_error (view, tmp);
323 g_free (view->filename);
324 view->filename = NULL;
325 g_free (view->workdir);
326 view->workdir = NULL;
327 goto finish;
330 /* Make sure we are working with a regular file */
331 if (mc_fstat (fd, &st) == -1)
333 mc_close (fd);
334 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
335 file, unix_error_string (errno));
336 mcview_show_error (view, tmp);
337 g_free (view->filename);
338 view->filename = NULL;
339 g_free (view->workdir);
340 view->workdir = NULL;
341 goto finish;
344 if (!S_ISREG (st.st_mode))
346 mc_close (fd);
347 mcview_show_error (view, _("Cannot view: not a regular file"));
348 g_free (view->filename);
349 view->filename = NULL;
350 g_free (view->workdir);
351 view->workdir = NULL;
352 goto finish;
355 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
357 /* Must be one of those nice files that grow (/proc) */
358 mcview_set_datasource_vfs_pipe (view, fd);
360 else
362 int type;
364 type = get_compression_type (fd, file);
366 if (view->magic_mode && (type != COMPRESSION_NONE))
368 g_free (view->filename);
369 view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
371 mcview_set_datasource_file (view, fd, &st);
373 retval = TRUE;
376 finish:
377 view->command = g_strdup (command);
378 view->dpy_start = 0;
379 view->search_start = 0;
380 view->search_end = 0;
381 view->dpy_text_column = 0;
383 mcview_compute_areas (view);
384 mcview_update_bytes_per_line (view);
386 if (mcview_remember_file_position && view->filename != NULL && start_line == 0)
388 char *canon_fname;
389 long line, col;
390 off_t new_offset, max_offset;
391 vfs_path_t *vpath;
393 vpath = vfs_path_from_str (view->filename);
394 canon_fname = vfs_path_to_str (vpath);
395 load_file_position (canon_fname, &line, &col, &new_offset, &view->saved_bookmarks);
396 max_offset = mcview_get_filesize (view) - 1;
397 if (max_offset < 0)
398 new_offset = 0;
399 else
400 new_offset = min (new_offset, max_offset);
401 if (!view->hex_mode)
402 view->dpy_start = mcview_bol (view, new_offset, 0);
403 else
405 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
406 view->hex_cursor = new_offset;
408 g_free (canon_fname);
409 vfs_path_free (vpath);
411 else if (start_line > 0)
412 mcview_moveto (view, start_line - 1, 0);
414 view->hexedit_lownibble = FALSE;
415 view->hexview_in_text = FALSE;
416 view->change_list = NULL;
417 return retval;
420 /* --------------------------------------------------------------------------------------------- */