root/src/viewer/datasource.c

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

DEFINITIONS

This source file includes following definitions.
  1. mcview_set_datasource_stdio_pipe
  2. mcview_set_datasource_none
  3. mcview_get_filesize
  4. mcview_update_filesize
  5. mcview_get_ptr_file
  6. mcview_get_utf
  7. mcview_get_ptr_string
  8. mcview_get_byte_string
  9. mcview_get_byte_none
  10. mcview_set_byte
  11. mcview_file_load_data
  12. mcview_close_datasource
  13. mcview_set_datasource_file
  14. mcview_load_command_output
  15. mcview_set_datasource_vfs_pipe
  16. mcview_set_datasource_string

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Functions for datasources
   4 
   5    Copyright (C) 1994-2024
   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
  17    Andrew Borodin <aborodin@vmail.ru>, 2009
  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 /*
  37    The data source provides the viewer with data from either a file, a
  38    string or the output of a command. The mcview_get_byte() function can be
  39    used to get the value of a byte at a specific offset. If the offset
  40    is out of range, -1 is returned. The function mcview_get_byte_indexed(a,b)
  41    returns the byte at the offset a+b, or -1 if a+b is out of range.
  42 
  43    The mcview_set_byte() function has the effect that later calls to
  44    mcview_get_byte() will return the specified byte for this offset. This
  45    function is designed only for use by the hexedit component after
  46    saving its changes. Inspect the source before you want to use it for
  47    other purposes.
  48 
  49    The mcview_get_filesize() function returns the current size of the
  50    data source. If the growing buffer is used, this size may increase
  51    later on. Use the mcview_may_still_grow() function when you want to
  52    know if the size can change later.
  53  */
  54 
  55 #include <config.h>
  56 
  57 #include "lib/global.h"
  58 #include "lib/vfs/vfs.h"
  59 #include "lib/util.h"
  60 #include "lib/widget.h"         /* D_NORMAL, D_ERROR */
  61 
  62 #include "internal.h"
  63 
  64 /*** global variables ****************************************************************************/
  65 
  66 /*** file scope macro definitions ****************************************************************/
  67 
  68 /*** file scope type declarations ****************************************************************/
  69 
  70 /*** forward declarations (file scope functions) *************************************************/
  71 
  72 /*** file scope variables ************************************************************************/
  73 
  74 /* --------------------------------------------------------------------------------------------- */
  75 /*** file scope functions ************************************************************************/
  76 /* --------------------------------------------------------------------------------------------- */
  77 
  78 static void
  79 mcview_set_datasource_stdio_pipe (WView *view, mc_pipe_t *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
  80 {
  81     p->out.len = MC_PIPE_BUFSIZE;
  82     p->out.null_term = FALSE;
  83     p->err.len = MC_PIPE_BUFSIZE;
  84     p->err.null_term = TRUE;
  85     view->datasource = DS_STDIO_PIPE;
  86     view->ds_stdio_pipe = p;
  87     view->pipe_first_err_msg = TRUE;
  88 
  89     mcview_growbuf_init (view);
  90 }
  91 
  92 /* --------------------------------------------------------------------------------------------- */
  93 /*** public functions ****************************************************************************/
  94 /* --------------------------------------------------------------------------------------------- */
  95 
  96 void
  97 mcview_set_datasource_none (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  98 {
  99     view->datasource = DS_NONE;
 100 }
 101 
 102 /* --------------------------------------------------------------------------------------------- */
 103 
 104 off_t
 105 mcview_get_filesize (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 106 {
 107     switch (view->datasource)
 108     {
 109     case DS_STDIO_PIPE:
 110     case DS_VFS_PIPE:
 111         return mcview_growbuf_filesize (view);
 112     case DS_FILE:
 113         return view->ds_file_filesize;
 114     case DS_STRING:
 115         return view->ds_string_len;
 116     default:
 117         return 0;
 118     }
 119 }
 120 
 121 /* --------------------------------------------------------------------------------------------- */
 122 
 123 void
 124 mcview_update_filesize (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     if (view->datasource == DS_FILE)
 127     {
 128         struct stat st;
 129         if (mc_fstat (view->ds_file_fd, &st) != -1)
 130             view->ds_file_filesize = st.st_size;
 131     }
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 char *
 137 mcview_get_ptr_file (WView *view, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     g_assert (view->datasource == DS_FILE);
 140 
 141     mcview_file_load_data (view, byte_index);
 142     if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
 143         return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
 144     return NULL;
 145 }
 146 
 147 /* --------------------------------------------------------------------------------------------- */
 148 
 149 /* Invalid UTF-8 is reported as negative integers (one for each byte),
 150  * see ticket 3783. */
 151 gboolean
 152 mcview_get_utf (WView *view, off_t byte_index, int *ch, int *ch_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 153 {
 154     gchar *str = NULL;
 155     int res;
 156     gchar utf8buf[UTF8_CHAR_LEN + 1];
 157 
 158     switch (view->datasource)
 159     {
 160     case DS_STDIO_PIPE:
 161     case DS_VFS_PIPE:
 162         str = mcview_get_ptr_growing_buffer (view, byte_index);
 163         break;
 164     case DS_FILE:
 165         str = mcview_get_ptr_file (view, byte_index);
 166         break;
 167     case DS_STRING:
 168         str = mcview_get_ptr_string (view, byte_index);
 169         break;
 170     case DS_NONE:
 171     default:
 172         break;
 173     }
 174 
 175     *ch = 0;
 176 
 177     if (str == NULL)
 178         return FALSE;
 179 
 180     res = g_utf8_get_char_validated (str, -1);
 181 
 182     if (res < 0)
 183     {
 184         /* Retry with explicit bytes to make sure it's not a buffer boundary */
 185         int i;
 186 
 187         for (i = 0; i < UTF8_CHAR_LEN; i++)
 188         {
 189             if (mcview_get_byte (view, byte_index + i, &res))
 190                 utf8buf[i] = res;
 191             else
 192             {
 193                 utf8buf[i] = '\0';
 194                 break;
 195             }
 196         }
 197         utf8buf[UTF8_CHAR_LEN] = '\0';
 198         str = utf8buf;
 199         res = g_utf8_get_char_validated (str, -1);
 200     }
 201 
 202     if (res < 0)
 203     {
 204         /* Implicit conversion from signed char to signed int keeps negative values. */
 205         *ch = *str;
 206         *ch_len = 1;
 207     }
 208     else
 209     {
 210         gchar *next_ch = NULL;
 211 
 212         *ch = res;
 213         /* Calculate UTF-8 char length */
 214         next_ch = g_utf8_next_char (str);
 215         *ch_len = next_ch - str;
 216     }
 217 
 218     return TRUE;
 219 }
 220 
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 char *
 224 mcview_get_ptr_string (WView *view, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     g_assert (view->datasource == DS_STRING);
 227 
 228     if (byte_index >= 0 && byte_index < (off_t) view->ds_string_len)
 229         return (char *) (view->ds_string_data + byte_index);
 230     return NULL;
 231 }
 232 
 233 /* --------------------------------------------------------------------------------------------- */
 234 
 235 gboolean
 236 mcview_get_byte_string (WView *view, off_t byte_index, int *retval)
     /* [previous][next][first][last][top][bottom][index][help]  */
 237 {
 238     char *p;
 239 
 240     if (retval != NULL)
 241         *retval = -1;
 242 
 243     p = mcview_get_ptr_string (view, byte_index);
 244     if (p == NULL)
 245         return FALSE;
 246 
 247     if (retval != NULL)
 248         *retval = (unsigned char) (*p);
 249     return TRUE;
 250 }
 251 
 252 /* --------------------------------------------------------------------------------------------- */
 253 
 254 gboolean
 255 mcview_get_byte_none (WView *view, off_t byte_index, int *retval)
     /* [previous][next][first][last][top][bottom][index][help]  */
 256 {
 257     (void) &view;
 258     (void) byte_index;
 259 
 260     g_assert (view->datasource == DS_NONE);
 261 
 262     if (retval != NULL)
 263         *retval = -1;
 264     return FALSE;
 265 }
 266 
 267 /* --------------------------------------------------------------------------------------------- */
 268 
 269 void
 270 mcview_set_byte (WView *view, off_t offset, byte b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272     (void) &b;
 273     (void) offset;
 274 
 275     g_assert (offset < mcview_get_filesize (view));
 276     g_assert (view->datasource == DS_FILE);
 277 
 278     view->ds_file_datalen = 0;  /* just force reloading */
 279 }
 280 
 281 /* --------------------------------------------------------------------------------------------- */
 282 
 283 /*static */
 284 void
 285 mcview_file_load_data (WView *view, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 286 {
 287     off_t blockoffset;
 288     ssize_t res;
 289     size_t bytes_read;
 290 
 291     g_assert (view->datasource == DS_FILE);
 292 
 293     if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
 294         return;
 295 
 296     if (byte_index >= view->ds_file_filesize)
 297         return;
 298 
 299     blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
 300     if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
 301         goto error;
 302 
 303     bytes_read = 0;
 304     while (bytes_read < view->ds_file_datasize)
 305     {
 306         res =
 307             mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
 308                      view->ds_file_datasize - bytes_read);
 309         if (res == -1)
 310             goto error;
 311         if (res == 0)
 312             break;
 313         bytes_read += (size_t) res;
 314     }
 315     view->ds_file_offset = blockoffset;
 316     if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
 317     {
 318         /* the file has grown in the meantime -- stick to the old size */
 319         view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
 320     }
 321     else
 322     {
 323         view->ds_file_datalen = bytes_read;
 324     }
 325     return;
 326 
 327   error:
 328     view->ds_file_datalen = 0;
 329 }
 330 
 331 /* --------------------------------------------------------------------------------------------- */
 332 
 333 void
 334 mcview_close_datasource (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 335 {
 336     switch (view->datasource)
 337     {
 338     case DS_NONE:
 339         break;
 340     case DS_STDIO_PIPE:
 341         if (view->ds_stdio_pipe != NULL)
 342         {
 343             mcview_growbuf_done (view);
 344             mcview_display (view);
 345         }
 346         mcview_growbuf_free (view);
 347         break;
 348     case DS_VFS_PIPE:
 349         if (view->ds_vfs_pipe != -1)
 350             mcview_growbuf_done (view);
 351         mcview_growbuf_free (view);
 352         break;
 353     case DS_FILE:
 354         (void) mc_close (view->ds_file_fd);
 355         view->ds_file_fd = -1;
 356         MC_PTR_FREE (view->ds_file_data);
 357         break;
 358     case DS_STRING:
 359         MC_PTR_FREE (view->ds_string_data);
 360         break;
 361     default:
 362         break;
 363     }
 364     view->datasource = DS_NONE;
 365 }
 366 
 367 /* --------------------------------------------------------------------------------------------- */
 368 
 369 void
 370 mcview_set_datasource_file (WView *view, int fd, const struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 371 {
 372     view->datasource = DS_FILE;
 373     view->ds_file_fd = fd;
 374     view->ds_file_filesize = st->st_size;
 375     view->ds_file_offset = 0;
 376     view->ds_file_data = g_malloc (4096);
 377     view->ds_file_datalen = 0;
 378     view->ds_file_datasize = 4096;
 379 }
 380 
 381 /* --------------------------------------------------------------------------------------------- */
 382 
 383 gboolean
 384 mcview_load_command_output (WView *view, const char *command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     mc_pipe_t *p;
 387     GError *error = NULL;
 388 
 389     mcview_close_datasource (view);
 390 
 391     p = mc_popen (command, TRUE, TRUE, &error);
 392     if (p == NULL)
 393     {
 394         mcview_display (view);
 395         mcview_show_error (view, error->message);
 396         g_error_free (error);
 397         return FALSE;
 398     }
 399 
 400     /* Check if filter produced any output */
 401     mcview_set_datasource_stdio_pipe (view, p);
 402     if (!mcview_get_byte (view, 0, NULL))
 403     {
 404         mcview_close_datasource (view);
 405         mcview_display (view);
 406         return FALSE;
 407     }
 408 
 409     return TRUE;
 410 }
 411 
 412 /* --------------------------------------------------------------------------------------------- */
 413 
 414 void
 415 mcview_set_datasource_vfs_pipe (WView *view, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 416 {
 417     g_assert (fd != -1);
 418 
 419     view->datasource = DS_VFS_PIPE;
 420     view->ds_vfs_pipe = fd;
 421 
 422     mcview_growbuf_init (view);
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */
 426 
 427 void
 428 mcview_set_datasource_string (WView *view, const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 429 {
 430     view->datasource = DS_STRING;
 431     view->ds_string_len = strlen (s);
 432     view->ds_string_data = (byte *) g_strndup (s, view->ds_string_len);
 433 }
 434 
 435 /* --------------------------------------------------------------------------------------------- */

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