root/src/viewer/lib.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. mcview_toggle_magic_mode
  2. mcview_toggle_wrap_mode
  3. mcview_toggle_nroff_mode
  4. mcview_toggle_hex_mode
  5. mcview_init
  6. mcview_done
  7. mcview_set_codeset
  8. mcview_select_encoding
  9. mcview_show_error
  10. mcview_bol
  11. mcview_eol
  12. mcview_get_title
  13. mcview_calc_percent
  14. mcview_clear_mode_flags

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Common finctions (used from some other mcviewer functions)
   4 
   5    Copyright (C) 1994-2025
   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-2022
  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 <https://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
  72 mcview_toggle_magic_mode (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  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
 103 mcview_toggle_wrap_mode (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 114 mcview_toggle_nroff_mode (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 126 mcview_toggle_hex_mode (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 152 mcview_init (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 204 mcview_done (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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     if (view->coord_cache != NULL)
 230     {
 231         g_ptr_array_free (view->coord_cache, TRUE);
 232         view->coord_cache = NULL;
 233     }
 234 
 235     if (view->converter == INVALID_CONV)
 236         view->converter = str_cnv_from_term;
 237 
 238     if (view->converter != str_cnv_from_term)
 239     {
 240         str_close_conv (view->converter);
 241         view->converter = str_cnv_from_term;
 242     }
 243 
 244     mcview_search_deinit (view);
 245     view->search = NULL;
 246     view->last_search_string = NULL;
 247     mcview_hexedit_free_change_list (view);
 248 
 249     if (mc_global.mc_run_mode == MC_RUN_VIEWER && view->dir != NULL)
 250     {
 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);
 255     }
 256 
 257     view->dir = NULL;
 258 }
 259 
 260 /* --------------------------------------------------------------------------------------------- */
 261 
 262 #ifdef HAVE_CHARSET
 263 void
 264 mcview_set_codeset (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 265 {
 266     const char *cp_id = NULL;
 267 
 268     view->utf8 = TRUE;
 269     cp_id = get_codepage_id (mc_global.source_codepage >= 0 ? mc_global.source_codepage
 270                                                             : mc_global.display_codepage);
 271     if (cp_id != NULL)
 272     {
 273         GIConv conv;
 274         conv = str_crt_conv_from (cp_id);
 275         if (conv != INVALID_CONV)
 276         {
 277             if (view->converter != str_cnv_from_term)
 278                 str_close_conv (view->converter);
 279             view->converter = conv;
 280         }
 281         view->utf8 = (gboolean) str_isutf8 (cp_id);
 282         view->dpy_wrap_dirty = TRUE;
 283     }
 284 }
 285 
 286 /* --------------------------------------------------------------------------------------------- */
 287 
 288 void
 289 mcview_select_encoding (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 290 {
 291     if (do_select_codepage ())
 292         mcview_set_codeset (view);
 293 }
 294 #endif
 295 
 296 /* --------------------------------------------------------------------------------------------- */
 297 
 298 void
 299 mcview_show_error (WView *view, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 300 {
 301     if (mcview_is_in_panel (view))
 302         mcview_set_datasource_string (view, msg);
 303     else
 304         message (D_ERROR, MSG_ERROR, "%s", msg);
 305 }
 306 
 307 /* --------------------------------------------------------------------------------------------- */
 308 /** returns index of the first char in the line
 309  * it is constant for all line characters
 310  */
 311 
 312 off_t
 313 mcview_bol (WView *view, off_t current, off_t limit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 314 {
 315     int c;
 316 
 317     if (current <= 0)
 318         return 0;
 319 
 320     const off_t filesize = mcview_get_filesize (view);
 321 
 322     if (current > filesize)
 323         return filesize;
 324 
 325     if (!mcview_get_byte (view, current, &c))
 326         return current;
 327 
 328     if (c == '\n')
 329     {
 330         if (!mcview_get_byte (view, current - 1, &c))
 331             return current;
 332 
 333         if (c == '\r')
 334             current--;
 335     }
 336 
 337     for (; current > 0 && current > limit; current--)
 338     {
 339         if (!mcview_get_byte (view, current - 1, &c))
 340             break;
 341         if (c == '\r' || c == '\n')
 342             break;
 343     }
 344 
 345     return current;
 346 }
 347 
 348 /* --------------------------------------------------------------------------------------------- */
 349 /** returns index of last char on line + width EOL
 350  * mcview_eol of the current line == mcview_bol next line
 351  */
 352 
 353 off_t
 354 mcview_eol (WView *view, off_t current)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356     int c;
 357     int prev_ch = 0;
 358 
 359     if (current < 0)
 360         return 0;
 361 
 362     for (; mcview_get_byte (view, current, &c); current++)
 363     {
 364         if (c == '\n')
 365         {
 366             current++;
 367             break;
 368         }
 369         if (prev_ch == '\r')
 370             break;
 371 
 372         prev_ch = c;
 373     }
 374 
 375     return current;
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 char *
 381 mcview_get_title (const WDialog *h, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     const WView *view;
 384     const char *modified;
 385     const char *file_label;
 386     const char *view_filename;
 387     char *ret_str;
 388 
 389     view = (const WView *) widget_find_by_type (CONST_WIDGET (h), mcview_callback);
 390     modified = view->hexedit_mode && (view->change_list != NULL) ? "(*) " : "    ";
 391     view_filename = vfs_path_as_str (view->filename_vpath);
 392 
 393     len -= 4;
 394 
 395     file_label = view_filename != NULL ? view_filename : view->command != NULL ? view->command : "";
 396     file_label = str_term_trim (file_label, len - str_term_width1 (_ ("View: ")));
 397 
 398     ret_str = g_strconcat (_ ("View: "), modified, file_label, (char *) NULL);
 399     return ret_str;
 400 }
 401 
 402 /* --------------------------------------------------------------------------------------------- */
 403 
 404 int
 405 mcview_calc_percent (WView *view, off_t p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 406 {
 407     off_t filesize;
 408     int percent;
 409 
 410     if (view->status_area.cols < 1 || (view->status_area.x + view->status_area.cols) < 4)
 411         return (-1);
 412     if (mcview_may_still_grow (view))
 413         return (-1);
 414 
 415     filesize = mcview_get_filesize (view);
 416     if (view->mode_flags.hex && filesize > 0)
 417     {
 418         // p can't be beyond the last char, only over that. Compensate for this.
 419         filesize--;
 420     }
 421 
 422     if (filesize == 0 || p >= filesize)
 423         percent = 100;
 424     else if (p > (INT_MAX / 100))
 425         percent = p / (filesize / 100);
 426     else
 427         percent = p * 100 / filesize;
 428 
 429     return percent;
 430 }
 431 
 432 /* --------------------------------------------------------------------------------------------- */
 433 
 434 void
 435 mcview_clear_mode_flags (mcview_mode_flags_t *flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 436 {
 437     memset (flags, 0, sizeof (*flags));
 438 }
 439 
 440 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */