Ticket #2370 (Commands like 'cat myfile | mcview' doesn't work)
[midnight-commander.git] / src / viewer / lib.c
blob9a91a9051a74461c9d2ec6a4223492bd9fd92fe3
1 /*
2 Internal file viewer for the Midnight Commander
3 Common finctions (used from some other mcviewer 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>
40 #include <limits.h>
41 #include <sys/types.h>
43 #include "lib/global.h"
44 #include "lib/vfs/vfs.h"
45 #include "lib/strutil.h"
46 #include "lib/util.h" /* save_file_position() */
47 #include "lib/lock.h" /* unlock_file() */
48 #include "lib/widget.h"
49 #include "lib/charsets.h"
51 #include "src/main.h"
52 #include "src/selcodepage.h"
54 #include "internal.h"
55 #include "mcviewer.h"
57 /*** global variables ****************************************************************************/
59 #define OFF_T_BITWIDTH (unsigned int) (sizeof (off_t) * CHAR_BIT - 1)
60 const off_t INVALID_OFFSET = (off_t) - 1;
61 const off_t OFFSETTYPE_MAX = ((off_t) 1 << (OFF_T_BITWIDTH - 1)) - 1;
63 /*** file scope macro definitions ****************************************************************/
65 /*** file scope type declarations ****************************************************************/
67 /*** file scope variables ************************************************************************/
69 /*** file scope functions ************************************************************************/
70 /* --------------------------------------------------------------------------------------------- */
72 /* --------------------------------------------------------------------------------------------- */
73 /*** public functions ****************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 void
77 mcview_toggle_magic_mode (mcview_t * view)
79 char *filename, *command;
81 if (view->datasource == DS_STDIN_PIPE) /* stdin can't be "re-opened" */
82 return; /* and we can't do magic toggle without reopen, yet */
84 mcview_altered_magic_flag = 1;
85 view->magic_mode = !view->magic_mode;
86 filename = g_strdup (view->filename);
87 command = g_strdup (view->command);
89 mcview_done (view);
90 mcview_init (view);
91 mcview_load (view, command, filename, 0);
92 g_free (filename);
93 g_free (command);
94 view->dpy_bbar_dirty = TRUE;
95 view->dirty++;
98 /* --------------------------------------------------------------------------------------------- */
100 void
101 mcview_toggle_wrap_mode (mcview_t * view)
103 if (view->text_wrap_mode)
104 view->dpy_start = mcview_bol (view, view->dpy_start, 0);
105 view->text_wrap_mode = !view->text_wrap_mode;
106 view->dpy_bbar_dirty = TRUE;
107 view->dirty++;
110 /* --------------------------------------------------------------------------------------------- */
112 void
113 mcview_toggle_nroff_mode (mcview_t * view)
115 view->text_nroff_mode = !view->text_nroff_mode;
116 mcview_altered_nroff_flag = 1;
117 view->dpy_bbar_dirty = TRUE;
118 view->dirty++;
121 /* --------------------------------------------------------------------------------------------- */
123 void
124 mcview_toggle_hex_mode (mcview_t * view)
126 view->hex_mode = !view->hex_mode;
128 if (view->hex_mode)
130 view->hex_cursor = view->dpy_start;
131 view->dpy_start = mcview_offset_rounddown (view->dpy_start, view->bytes_per_line);
132 widget_want_cursor (view->widget, 1);
134 else
136 view->dpy_start = mcview_bol (view, view->hex_cursor, 0);
137 view->hex_cursor = view->dpy_start;
138 widget_want_cursor (view->widget, 0);
140 mcview_altered_hex_mode = 1;
141 view->dpy_bbar_dirty = TRUE;
142 view->dirty++;
145 /* --------------------------------------------------------------------------------------------- */
147 gboolean
148 mcview_ok_to_quit (mcview_t * view)
150 int r;
152 if (view->change_list == NULL)
153 return TRUE;
155 if (!mc_global.widget.midnight_shutdown)
157 query_set_sel (2);
158 r = query_dialog (_("Quit"),
159 _("File was modified. Save with exit?"), D_NORMAL, 3,
160 _("&Yes"), _("&No"), _("&Cancel quit"));
162 else
164 r = query_dialog (_("Quit"),
165 _("Midnight Commander is being shut down.\nSave modified file?"),
166 D_NORMAL, 2, _("&Yes"), _("&No"));
167 /* Esc is No */
168 if (r == -1)
169 r = 1;
172 switch (r)
174 case 0: /* Yes */
175 return mcview_hexedit_save_changes (view) || mc_global.widget.midnight_shutdown;
176 case 1: /* No */
177 mcview_hexedit_free_change_list (view);
178 return TRUE;
179 default:
180 return FALSE;
184 /* --------------------------------------------------------------------------------------------- */
186 void
187 mcview_init (mcview_t * view)
189 size_t i;
191 view->filename = NULL;
192 view->workdir = NULL;
193 view->command = NULL;
194 view->search_nroff_seq = NULL;
196 mcview_set_datasource_none (view);
198 view->growbuf_in_use = FALSE;
199 /* leave the other growbuf fields uninitialized */
201 view->hexedit_lownibble = FALSE;
202 view->locked = FALSE;
203 view->coord_cache = NULL;
205 view->dpy_start = 0;
206 view->dpy_text_column = 0;
207 view->dpy_end = 0;
208 view->hex_cursor = 0;
209 view->cursor_col = 0;
210 view->cursor_row = 0;
211 view->change_list = NULL;
213 /* {status,ruler,data}_area are left uninitialized */
215 view->dirty = 0;
216 view->dpy_bbar_dirty = TRUE;
217 view->bytes_per_line = 1;
219 view->search_start = 0;
220 view->search_end = 0;
222 view->marker = 0;
223 for (i = 0; i < sizeof (view->marks) / sizeof (view->marks[0]); i++)
224 view->marks[i] = 0;
226 view->move_dir = 0;
227 view->update_steps = 0;
228 view->update_activate = 0;
230 view->saved_bookmarks = NULL;
233 /* --------------------------------------------------------------------------------------------- */
235 void
236 mcview_done (mcview_t * view)
238 /* Save current file position */
239 if (mcview_remember_file_position && view->filename != NULL)
241 char *canon_fname;
242 vfs_path_t *vpath;
243 vpath = vfs_path_from_str (view->filename);
244 canon_fname = vfs_path_to_str (vpath);
245 save_file_position (canon_fname, -1, 0,
246 view->hex_mode ? view->hex_cursor : view->dpy_start,
247 view->saved_bookmarks);
248 view->saved_bookmarks = NULL;
249 g_free (canon_fname);
250 vfs_path_free (vpath);
253 /* Write back the global viewer mode */
254 mcview_default_hex_mode = view->hex_mode;
255 mcview_default_nroff_flag = view->text_nroff_mode;
256 mcview_default_magic_flag = view->magic_mode;
257 mcview_global_wrap_mode = view->text_wrap_mode;
259 /* Free memory used by the viewer */
261 /* view->widget needs no destructor */
263 g_free (view->filename);
264 view->filename = NULL;
265 g_free (view->workdir);
266 view->workdir = NULL;
267 g_free (view->command);
268 view->command = NULL;
270 mcview_close_datasource (view);
271 /* the growing buffer is freed with the datasource */
273 coord_cache_free (view->coord_cache), view->coord_cache = NULL;
275 if (view->converter == INVALID_CONV)
276 view->converter = str_cnv_from_term;
278 if (view->converter != str_cnv_from_term)
280 str_close_conv (view->converter);
281 view->converter = str_cnv_from_term;
284 mc_search_free (view->search);
285 view->search = NULL;
286 g_free (view->last_search_string);
287 view->last_search_string = NULL;
288 mcview_nroff_seq_free (&view->search_nroff_seq);
289 mcview_hexedit_free_change_list (view);
292 /* --------------------------------------------------------------------------------------------- */
294 void
295 mcview_set_codeset (mcview_t * view)
297 #ifdef HAVE_CHARSET
298 const char *cp_id = NULL;
300 view->utf8 = TRUE;
301 cp_id =
302 get_codepage_id (mc_global.source_codepage >=
303 0 ? mc_global.source_codepage : mc_global.display_codepage);
304 if (cp_id != NULL)
306 GIConv conv;
307 conv = str_crt_conv_from (cp_id);
308 if (conv != INVALID_CONV)
310 if (view->converter != str_cnv_from_term)
311 str_close_conv (view->converter);
312 view->converter = conv;
314 view->utf8 = (gboolean) str_isutf8 (cp_id);
316 #else
317 (void) view;
318 #endif
321 /* --------------------------------------------------------------------------------------------- */
323 void
324 mcview_select_encoding (mcview_t * view)
326 #ifdef HAVE_CHARSET
327 if (do_select_codepage ())
328 mcview_set_codeset (view);
329 #else
330 (void) view;
331 #endif
334 /* --------------------------------------------------------------------------------------------- */
336 void
337 mcview_show_error (mcview_t * view, const char *msg)
339 mcview_close_datasource (view);
340 if (mcview_is_in_panel (view))
342 mcview_set_datasource_string (view, msg);
344 else
346 message (D_ERROR, MSG_ERROR, "%s", msg);
350 /* --------------------------------------------------------------------------------------------- */
351 /** returns index of the first char in the line
352 * it is constant for all line characters
355 off_t
356 mcview_bol (mcview_t * view, off_t current, off_t limit)
358 int c;
359 off_t filesize;
360 filesize = mcview_get_filesize (view);
361 if (current <= 0)
362 return 0;
363 if (current > filesize)
364 return filesize;
365 if (!mcview_get_byte (view, current, &c))
366 return current;
367 if (c == '\n')
369 if (!mcview_get_byte (view, current - 1, &c))
370 return current;
371 if (c == '\r')
372 current--;
374 while (current > 0 && current >= limit)
376 if (!mcview_get_byte (view, current - 1, &c))
377 break;
378 if (c == '\r' || c == '\n')
379 break;
380 current--;
382 return current;
385 /* --------------------------------------------------------------------------------------------- */
386 /** returns index of last char on line + width EOL
387 * mcview_eol of the current line == mcview_bol next line
390 off_t
391 mcview_eol (mcview_t * view, off_t current, off_t limit)
393 int c, prev_ch = 0;
394 off_t filesize;
395 filesize = mcview_get_filesize (view);
396 if (current < 0)
397 return 0;
398 if (current >= filesize)
399 return filesize;
400 while (current < filesize && current < limit)
402 if (!mcview_get_byte (view, current, &c))
403 break;
404 if (c == '\n')
406 current++;
407 break;
409 else if (prev_ch == '\r')
411 break;
413 current++;
414 prev_ch = c;
416 return current;
419 /* --------------------------------------------------------------------------------------------- */
421 char *
422 mcview_get_title (const Dlg_head * h, size_t len)
424 const mcview_t *view = (const mcview_t *) find_widget_type (h, mcview_callback);
425 const char *modified = view->hexedit_mode && (view->change_list != NULL) ? "(*) " : " ";
426 const char *file_label;
428 len -= 4;
430 file_label = view->filename != NULL ? view->filename :
431 view->command != NULL ? view->command : "";
432 file_label = str_term_trim (file_label, len - str_term_width1 (_("View: ")));
434 return g_strconcat (_("View: "), modified, file_label, (char *) NULL);
437 /* --------------------------------------------------------------------------------------------- */
439 gboolean
440 mcview_lock_file (mcview_t * view)
442 char *fullpath;
443 gboolean ret;
445 fullpath = mc_build_filename (view->workdir, view->filename, (char *) NULL);
446 ret = lock_file (fullpath);
447 g_free (fullpath);
449 return ret;
452 /* --------------------------------------------------------------------------------------------- */
454 gboolean
455 mcview_unlock_file (mcview_t * view)
457 char *fullpath;
458 gboolean ret;
460 fullpath = mc_build_filename (view->workdir, view->filename, (char *) NULL);
461 ret = unlock_file (fullpath);
462 g_free (fullpath);
464 return ret;
467 /* --------------------------------------------------------------------------------------------- */