Merge branch '4292_autocomplete_slashes'
[midnight-commander.git] / src / viewer / lib.c
blobb427987ef682d0379e4482e9874824d73c0d695e
1 /*
2 Internal file viewer for the Midnight Commander
3 Common finctions (used from some other mcviewer functions)
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Joseph M. Hinkle, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2009-2022
18 Ilia Maslakov <il.smind@gmail.com>, 2009
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 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU 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, see <http://www.gnu.org/licenses/>.
36 #include <config.h>
38 #include <string.h> /* memset() */
39 #include <sys/types.h>
41 #include "lib/global.h"
42 #include "lib/vfs/vfs.h"
43 #include "lib/strutil.h"
44 #include "lib/util.h" /* save_file_position() */
45 #include "lib/widget.h"
46 #ifdef HAVE_CHARSET
47 #include "lib/charsets.h"
48 #endif
50 #ifdef HAVE_CHARSET
51 #include "src/selcodepage.h"
52 #endif
54 #include "internal.h"
56 /*** global variables ****************************************************************************/
58 /*** file scope macro definitions ****************************************************************/
60 /*** file scope type declarations ****************************************************************/
62 /*** file scope variables ************************************************************************/
64 /*** file scope functions ************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
67 /* --------------------------------------------------------------------------------------------- */
68 /*** public functions ****************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
71 void
72 mcview_toggle_magic_mode (WView * view)
74 char *filename, *command;
75 dir_list *dir;
76 int *dir_idx;
78 mcview_altered_flags.magic = TRUE;
79 view->mode_flags.magic = !view->mode_flags.magic;
81 /* reinit view */
82 filename = g_strdup (vfs_path_as_str (view->filename_vpath));
83 command = g_strdup (view->command);
84 dir = view->dir;
85 dir_idx = view->dir_idx;
86 view->dir = NULL;
87 view->dir_idx = NULL;
88 mcview_done (view);
89 mcview_init (view);
90 mcview_load (view, command, filename, 0, 0, 0);
91 view->dir = dir;
92 view->dir_idx = dir_idx;
93 g_free (filename);
94 g_free (command);
96 view->dpy_bbar_dirty = TRUE;
97 view->dirty++;
100 /* --------------------------------------------------------------------------------------------- */
102 void
103 mcview_toggle_wrap_mode (WView * view)
105 view->mode_flags.wrap = !view->mode_flags.wrap;
106 view->dpy_wrap_dirty = TRUE;
107 view->dpy_bbar_dirty = TRUE;
108 view->dirty++;
111 /* --------------------------------------------------------------------------------------------- */
113 void
114 mcview_toggle_nroff_mode (WView * view)
116 view->mode_flags.nroff = !view->mode_flags.nroff;
117 mcview_altered_flags.nroff = TRUE;
118 view->dpy_wrap_dirty = TRUE;
119 view->dpy_bbar_dirty = TRUE;
120 view->dirty++;
123 /* --------------------------------------------------------------------------------------------- */
125 void
126 mcview_toggle_hex_mode (WView * view)
128 view->mode_flags.hex = !view->mode_flags.hex;
130 if (view->mode_flags.hex)
132 view->hex_cursor = view->dpy_start;
133 view->dpy_start = mcview_offset_rounddown (view->dpy_start, view->bytes_per_line);
134 widget_want_cursor (WIDGET (view), TRUE);
136 else
138 view->dpy_start = mcview_bol (view, view->hex_cursor, 0);
139 view->hex_cursor = view->dpy_start;
140 widget_want_cursor (WIDGET (view), FALSE);
142 mcview_altered_flags.hex = TRUE;
143 view->dpy_paragraph_skip_lines = 0;
144 view->dpy_wrap_dirty = TRUE;
145 view->dpy_bbar_dirty = TRUE;
146 view->dirty++;
149 /* --------------------------------------------------------------------------------------------- */
151 void
152 mcview_init (WView * view)
154 size_t i;
156 view->filename_vpath = NULL;
157 view->workdir_vpath = NULL;
158 view->command = NULL;
159 view->search_nroff_seq = NULL;
161 mcview_set_datasource_none (view);
163 view->growbuf_in_use = FALSE;
164 /* leave the other growbuf fields uninitialized */
166 view->hexedit_lownibble = FALSE;
167 view->locked = FALSE;
168 view->coord_cache = NULL;
170 view->dpy_start = 0;
171 view->dpy_paragraph_skip_lines = 0;
172 mcview_state_machine_init (&view->dpy_state_top, 0);
173 view->dpy_wrap_dirty = FALSE;
174 view->force_max = -1;
175 view->dpy_text_column = 0;
176 view->dpy_end = 0;
177 view->hex_cursor = 0;
178 view->cursor_col = 0;
179 view->cursor_row = 0;
180 view->change_list = NULL;
182 /* {status,ruler,data}_area are left uninitialized */
184 view->dirty = 0;
185 view->dpy_bbar_dirty = TRUE;
186 view->bytes_per_line = 1;
188 view->search_start = 0;
189 view->search_end = 0;
191 view->marker = 0;
192 for (i = 0; i < G_N_ELEMENTS (view->marks); i++)
193 view->marks[i] = 0;
195 view->update_steps = 0;
196 view->update_activate = 0;
198 view->saved_bookmarks = NULL;
201 /* --------------------------------------------------------------------------------------------- */
203 void
204 mcview_done (WView * view)
206 /* Save current file position */
207 if (mcview_remember_file_position && view->filename_vpath != NULL)
209 save_file_position (view->filename_vpath, -1, 0,
210 view->mode_flags.hex ? view->hex_cursor : view->dpy_start,
211 view->saved_bookmarks);
212 view->saved_bookmarks = NULL;
215 /* Write back the global viewer mode */
216 mcview_global_flags = view->mode_flags;
218 /* Free memory used by the viewer */
219 /* view->widget needs no destructor */
220 vfs_path_free (view->filename_vpath, TRUE);
221 view->filename_vpath = NULL;
222 vfs_path_free (view->workdir_vpath, TRUE);
223 view->workdir_vpath = NULL;
224 MC_PTR_FREE (view->command);
226 mcview_close_datasource (view);
227 /* the growing buffer is freed with the datasource */
229 if (view->coord_cache != NULL)
231 g_ptr_array_free (view->coord_cache, TRUE);
232 view->coord_cache = NULL;
235 if (view->converter == INVALID_CONV)
236 view->converter = str_cnv_from_term;
238 if (view->converter != str_cnv_from_term)
240 str_close_conv (view->converter);
241 view->converter = str_cnv_from_term;
244 mcview_search_deinit (view);
245 view->search = NULL;
246 view->last_search_string = NULL;
247 mcview_hexedit_free_change_list (view);
249 if (mc_global.mc_run_mode == MC_RUN_VIEWER && view->dir != NULL)
251 /* mcviewer is the owner of file list */
252 dir_list_free_list (view->dir);
253 g_free (view->dir);
254 g_free (view->dir_idx);
257 view->dir = NULL;
260 /* --------------------------------------------------------------------------------------------- */
262 #ifdef HAVE_CHARSET
263 void
264 mcview_set_codeset (WView * view)
266 const char *cp_id = NULL;
268 view->utf8 = TRUE;
269 cp_id =
270 get_codepage_id (mc_global.source_codepage >=
271 0 ? mc_global.source_codepage : mc_global.display_codepage);
272 if (cp_id != NULL)
274 GIConv conv;
275 conv = str_crt_conv_from (cp_id);
276 if (conv != INVALID_CONV)
278 if (view->converter != str_cnv_from_term)
279 str_close_conv (view->converter);
280 view->converter = conv;
282 view->utf8 = (gboolean) str_isutf8 (cp_id);
283 view->dpy_wrap_dirty = TRUE;
287 /* --------------------------------------------------------------------------------------------- */
289 void
290 mcview_select_encoding (WView * view)
292 if (do_select_codepage ())
293 mcview_set_codeset (view);
295 #endif /* HAVE_CHARSET */
297 /* --------------------------------------------------------------------------------------------- */
299 void
300 mcview_show_error (WView * view, const char *msg)
302 if (mcview_is_in_panel (view))
303 mcview_set_datasource_string (view, msg);
304 else
305 message (D_ERROR, MSG_ERROR, "%s", msg);
308 /* --------------------------------------------------------------------------------------------- */
309 /** returns index of the first char in the line
310 * it is constant for all line characters
313 off_t
314 mcview_bol (WView * view, off_t current, off_t limit)
316 int c;
317 off_t filesize;
318 filesize = mcview_get_filesize (view);
319 if (current <= 0)
320 return 0;
321 if (current > filesize)
322 return filesize;
323 if (!mcview_get_byte (view, current, &c))
324 return current;
325 if (c == '\n')
327 if (!mcview_get_byte (view, current - 1, &c))
328 return current;
329 if (c == '\r')
330 current--;
332 while (current > 0 && current > limit)
334 if (!mcview_get_byte (view, current - 1, &c))
335 break;
336 if (c == '\r' || c == '\n')
337 break;
338 current--;
340 return current;
343 /* --------------------------------------------------------------------------------------------- */
344 /** returns index of last char on line + width EOL
345 * mcview_eol of the current line == mcview_bol next line
348 off_t
349 mcview_eol (WView * view, off_t current)
351 int c, prev_ch = 0;
353 if (current < 0)
354 return 0;
356 while (TRUE)
358 if (!mcview_get_byte (view, current, &c))
359 break;
360 if (c == '\n')
362 current++;
363 break;
365 else if (prev_ch == '\r')
367 break;
369 current++;
370 prev_ch = c;
372 return current;
375 /* --------------------------------------------------------------------------------------------- */
377 char *
378 mcview_get_title (const WDialog * h, size_t len)
380 const WView *view;
381 const char *modified;
382 const char *file_label;
383 const char *view_filename;
384 char *ret_str;
386 view = (const WView *) widget_find_by_type (CONST_WIDGET (h), mcview_callback);
387 modified = view->hexedit_mode && (view->change_list != NULL) ? "(*) " : " ";
388 view_filename = vfs_path_as_str (view->filename_vpath);
390 len -= 4;
392 file_label = view_filename != NULL ? view_filename : view->command != NULL ? view->command : "";
393 file_label = str_term_trim (file_label, len - str_term_width1 (_("View: ")));
395 ret_str = g_strconcat (_("View: "), modified, file_label, (char *) NULL);
396 return ret_str;
399 /* --------------------------------------------------------------------------------------------- */
402 mcview_calc_percent (WView * view, off_t p)
404 off_t filesize;
405 int percent;
407 if (view->status_area.cols < 1 || (view->status_area.x + view->status_area.cols) < 4)
408 return (-1);
409 if (mcview_may_still_grow (view))
410 return (-1);
412 filesize = mcview_get_filesize (view);
413 if (view->mode_flags.hex && filesize > 0)
415 /* p can't be beyond the last char, only over that. Compensate for this. */
416 filesize--;
419 if (filesize == 0 || p >= filesize)
420 percent = 100;
421 else if (p > (INT_MAX / 100))
422 percent = p / (filesize / 100);
423 else
424 percent = p * 100 / filesize;
426 return percent;
429 /* --------------------------------------------------------------------------------------------- */
431 void
432 mcview_clear_mode_flags (mcview_mode_flags_t * flags)
434 memset (flags, 0, sizeof (*flags));
437 /* --------------------------------------------------------------------------------------------- */