1 /*
2 Internal file viewer for the Midnight Commander
3 Common finctions (used from some other mcviewer functions)
4
5 Copyright (C) 1994-2021
6 Free Software Foundation, Inc.
7
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, 2013, 2014
18 Ilia Maslakov <il.smind@gmail.com>, 2009
19
20 This file is part of the Midnight Commander.
21
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.
26
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.
31
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/>.
34 */
35
36 #include <config.h>
37
38 #include <string.h> /* memset() */
39 #include <sys/types.h>
40
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
49
50 #ifdef HAVE_CHARSET
51 #include "src/selcodepage.h"
52 #endif
53
54 #include "internal.h"
55
56 /*** global variables ****************************************************************************/
57
58 /*** file scope macro definitions ****************************************************************/
59
60 /*** file scope type declarations ****************************************************************/
61
62 /*** file scope variables ************************************************************************/
63
64 /*** file scope functions ************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
66
67 /* --------------------------------------------------------------------------------------------- */
68 /*** public functions ****************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
70
71 void
mcview_toggle_magic_mode(WView * view)72 mcview_toggle_magic_mode (WView * view)
73 {
74 char *filename, *command;
75 dir_list *dir;
76 int *dir_idx;
77
78 mcview_altered_flags.magic = TRUE;
79 view->mode_flags.magic = !view->mode_flags.magic;
80
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);
95
96 view->dpy_bbar_dirty = TRUE;
97 view->dirty++;
98 }
99
100 /* --------------------------------------------------------------------------------------------- */
101
102 void
mcview_toggle_wrap_mode(WView * view)103 mcview_toggle_wrap_mode (WView * view)
104 {
105 view->mode_flags.wrap = !view->mode_flags.wrap;
106 view->dpy_wrap_dirty = TRUE;
107 view->dpy_bbar_dirty = TRUE;
108 view->dirty++;
109 }
110
111 /* --------------------------------------------------------------------------------------------- */
112
113 void
mcview_toggle_nroff_mode(WView * view)114 mcview_toggle_nroff_mode (WView * view)
115 {
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++;
121 }
122
123 /* --------------------------------------------------------------------------------------------- */
124
125 void
mcview_toggle_hex_mode(WView * view)126 mcview_toggle_hex_mode (WView * view)
127 {
128 view->mode_flags.hex = !view->mode_flags.hex;
129
130 if (view->mode_flags.hex)
131 {
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);
135 }
136 else
137 {
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);
141 }
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++;
147 }
148
149 /* --------------------------------------------------------------------------------------------- */
150
151 void
mcview_init(WView * view)152 mcview_init (WView * view)
153 {
154 size_t i;
155
156 view->filename_vpath = NULL;
157 view->workdir_vpath = NULL;
158 view->command = NULL;
159 view->search_nroff_seq = NULL;
160
161 mcview_set_datasource_none (view);
162
163 view->growbuf_in_use = FALSE;
164 /* leave the other growbuf fields uninitialized */
165
166 view->hexedit_lownibble = FALSE;
167 view->locked = FALSE;
168 view->coord_cache = NULL;
169
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;
181
182 /* {status,ruler,data}_area are left uninitialized */
183
184 view->dirty = 0;
185 view->dpy_bbar_dirty = TRUE;
186 view->bytes_per_line = 1;
187
188 view->search_start = 0;
189 view->search_end = 0;
190
191 view->marker = 0;
192 for (i = 0; i < G_N_ELEMENTS (view->marks); i++)
193 view->marks[i] = 0;
194
195 view->update_steps = 0;
196 view->update_activate = 0;
197
198 view->saved_bookmarks = NULL;
199 }
200
201 /* --------------------------------------------------------------------------------------------- */
202
203 void
mcview_done(WView * view)204 mcview_done (WView * view)
205 {
206 /* Save current file position */
207 if (mcview_remember_file_position && view->filename_vpath != NULL)
208 {
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;
213 }
214
215 /* Write back the global viewer mode */
216 mcview_global_flags = view->mode_flags;
217
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);
225
226 mcview_close_datasource (view);
227 /* the growing buffer is freed with the datasource */
228
229 coord_cache_free (view->coord_cache);
230 view->coord_cache = NULL;
231
232 if (view->converter == INVALID_CONV)
233 view->converter = str_cnv_from_term;
234
235 if (view->converter != str_cnv_from_term)
236 {
237 str_close_conv (view->converter);
238 view->converter = str_cnv_from_term;
239 }
240
241 mcview_search_deinit (view);
242 view->search = NULL;
243 view->last_search_string = NULL;
244 mcview_hexedit_free_change_list (view);
245
246 if (mc_global.mc_run_mode == MC_RUN_VIEWER && view->dir != NULL)
247 {
248 /* mcviewer is the owner of file list */
249 dir_list_free_list (view->dir);
250 g_free (view->dir);
251 g_free (view->dir_idx);
252 }
253
254 view->dir = NULL;
255 }
256
257 /* --------------------------------------------------------------------------------------------- */
258
259 #ifdef HAVE_CHARSET
260 void
mcview_set_codeset(WView * view)261 mcview_set_codeset (WView * view)
262 {
263 const char *cp_id = NULL;
264
265 view->utf8 = TRUE;
266 cp_id =
267 get_codepage_id (mc_global.source_codepage >=
268 0 ? mc_global.source_codepage : mc_global.display_codepage);
269 if (cp_id != NULL)
270 {
271 GIConv conv;
272 conv = str_crt_conv_from (cp_id);
273 if (conv != INVALID_CONV)
274 {
275 if (view->converter != str_cnv_from_term)
276 str_close_conv (view->converter);
277 view->converter = conv;
278 }
279 view->utf8 = (gboolean) str_isutf8 (cp_id);
280 view->dpy_wrap_dirty = TRUE;
281 }
282 }
283
284 /* --------------------------------------------------------------------------------------------- */
285
286 void
mcview_select_encoding(WView * view)287 mcview_select_encoding (WView * view)
288 {
289 if (do_select_codepage ())
290 mcview_set_codeset (view);
291 }
292 #endif /* HAVE_CHARSET */
293
294 /* --------------------------------------------------------------------------------------------- */
295
296 void
mcview_show_error(WView * view,const char * msg)297 mcview_show_error (WView * view, const char *msg)
298 {
299 if (mcview_is_in_panel (view))
300 mcview_set_datasource_string (view, msg);
301 else
302 message (D_ERROR, MSG_ERROR, "%s", msg);
303 }
304
305 /* --------------------------------------------------------------------------------------------- */
306 /** returns index of the first char in the line
307 * it is constant for all line characters
308 */
309
310 off_t
mcview_bol(WView * view,off_t current,off_t limit)311 mcview_bol (WView * view, off_t current, off_t limit)
312 {
313 int c;
314 off_t filesize;
315 filesize = mcview_get_filesize (view);
316 if (current <= 0)
317 return 0;
318 if (current > filesize)
319 return filesize;
320 if (!mcview_get_byte (view, current, &c))
321 return current;
322 if (c == '\n')
323 {
324 if (!mcview_get_byte (view, current - 1, &c))
325 return current;
326 if (c == '\r')
327 current--;
328 }
329 while (current > 0 && current > limit)
330 {
331 if (!mcview_get_byte (view, current - 1, &c))
332 break;
333 if (c == '\r' || c == '\n')
334 break;
335 current--;
336 }
337 return current;
338 }
339
340 /* --------------------------------------------------------------------------------------------- */
341 /** returns index of last char on line + width EOL
342 * mcview_eol of the current line == mcview_bol next line
343 */
344
345 off_t
mcview_eol(WView * view,off_t current)346 mcview_eol (WView * view, off_t current)
347 {
348 int c, prev_ch = 0;
349
350 if (current < 0)
351 return 0;
352
353 while (TRUE)
354 {
355 if (!mcview_get_byte (view, current, &c))
356 break;
357 if (c == '\n')
358 {
359 current++;
360 break;
361 }
362 else if (prev_ch == '\r')
363 {
364 break;
365 }
366 current++;
367 prev_ch = c;
368 }
369 return current;
370 }
371
372 /* --------------------------------------------------------------------------------------------- */
373
374 char *
mcview_get_title(const WDialog * h,size_t len)375 mcview_get_title (const WDialog * h, size_t len)
376 {
377 const WView *view;
378 const char *modified;
379 const char *file_label;
380 const char *view_filename;
381 char *ret_str;
382
383 view = (const WView *) widget_find_by_type (CONST_WIDGET (h), mcview_callback);
384 modified = view->hexedit_mode && (view->change_list != NULL) ? "(*) " : " ";
385 view_filename = vfs_path_as_str (view->filename_vpath);
386
387 len -= 4;
388
389 file_label = view_filename != NULL ? view_filename : view->command != NULL ? view->command : "";
390 file_label = str_term_trim (file_label, len - str_term_width1 (_("View: ")));
391
392 ret_str = g_strconcat (_("View: "), modified, file_label, (char *) NULL);
393 return ret_str;
394 }
395
396 /* --------------------------------------------------------------------------------------------- */
397
398 int
mcview_calc_percent(WView * view,off_t p)399 mcview_calc_percent (WView * view, off_t p)
400 {
401 const screen_dimen right = view->status_area.left + view->status_area.width;
402 const screen_dimen height = view->status_area.height;
403 off_t filesize;
404 int percent;
405
406 if (height < 1 || right < 4)
407 return (-1);
408 if (mcview_may_still_grow (view))
409 return (-1);
410
411 filesize = mcview_get_filesize (view);
412 if (view->mode_flags.hex && filesize > 0)
413 {
414 /* p can't be beyond the last char, only over that. Compensate for this. */
415 filesize--;
416 }
417
418 if (filesize == 0 || p >= filesize)
419 percent = 100;
420 else if (p > (INT_MAX / 100))
421 percent = p / (filesize / 100);
422 else
423 percent = p * 100 / filesize;
424
425 return percent;
426 }
427
428 /* --------------------------------------------------------------------------------------------- */
429
430 void
mcview_clear_mode_flags(mcview_mode_flags_t * flags)431 mcview_clear_mode_flags (mcview_mode_flags_t * flags)
432 {
433 memset (flags, 0, sizeof (*flags));
434 }
435
436 /* --------------------------------------------------------------------------------------------- */
437