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