Manual pages: mcmcdiffmceditmcview

root/src/viewer/growbuf.c

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

DEFINITIONS

This source file includes following definitions.
  1. mcview_growbuf_init
  2. mcview_growbuf_done
  3. mcview_growbuf_free
  4. mcview_growbuf_filesize
  5. mcview_growbuf_read_until
  6. mcview_get_byte_growing_buffer
  7. mcview_get_ptr_growing_buffer

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Function for work with growing buffers
   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
  17    Andrew Borodin <aborodin@vmail.ru>, 2009, 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 <https://www.gnu.org/licenses/>.
  34  */
  35 
  36 #include <config.h>
  37 #include <errno.h>
  38 
  39 #include "lib/global.h"
  40 #include "lib/vfs/vfs.h"
  41 #include "lib/util.h"
  42 #include "lib/widget.h"  // D_NORMAL
  43 
  44 #include "internal.h"
  45 
  46 /* Block size for reading files in parts */
  47 #define VIEW_PAGE_SIZE ((size_t) 8192)
  48 
  49 /*** global variables ****************************************************************************/
  50 
  51 /*** file scope macro definitions ****************************************************************/
  52 
  53 /*** file scope type declarations ****************************************************************/
  54 
  55 /*** file scope variables ************************************************************************/
  56 
  57 /*** file scope functions ************************************************************************/
  58 /* --------------------------------------------------------------------------------------------- */
  59 
  60 /* --------------------------------------------------------------------------------------------- */
  61 /*** public functions ****************************************************************************/
  62 /* --------------------------------------------------------------------------------------------- */
  63 
  64 void
  65 mcview_growbuf_init (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  66 {
  67     view->growbuf_in_use = TRUE;
  68     view->growbuf_blockptr = g_ptr_array_new_with_free_func (g_free);
  69     view->growbuf_lastindex = VIEW_PAGE_SIZE;
  70     view->growbuf_finished = FALSE;
  71 }
  72 
  73 /* --------------------------------------------------------------------------------------------- */
  74 
  75 void
  76 mcview_growbuf_done (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  77 {
  78     view->growbuf_finished = TRUE;
  79 
  80     if (view->datasource == DS_STDIO_PIPE)
  81     {
  82         mc_pclose (view->ds_stdio_pipe, NULL);
  83         view->ds_stdio_pipe = NULL;
  84     }
  85     else  // view->datasource == DS_VFS_PIPE
  86     {
  87         (void) mc_close (view->ds_vfs_pipe);
  88         view->ds_vfs_pipe = -1;
  89     }
  90 }
  91 
  92 /* --------------------------------------------------------------------------------------------- */
  93 
  94 void
  95 mcview_growbuf_free (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  96 {
  97     g_assert (view->growbuf_in_use);
  98 
  99     g_ptr_array_free (view->growbuf_blockptr, TRUE);
 100     view->growbuf_blockptr = NULL;
 101     view->growbuf_in_use = FALSE;
 102 }
 103 
 104 /* --------------------------------------------------------------------------------------------- */
 105 
 106 off_t
 107 mcview_growbuf_filesize (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 108 {
 109     g_assert (view->growbuf_in_use);
 110 
 111     if (view->growbuf_blockptr->len == 0)
 112         return 0;
 113     else
 114         return ((off_t) view->growbuf_blockptr->len - 1) * VIEW_PAGE_SIZE + view->growbuf_lastindex;
 115 }
 116 
 117 /* --------------------------------------------------------------------------------------------- */
 118 /** Copies the output from the pipe to the growing buffer, until either
 119  * the end-of-pipe is reached or the interval [0..ofs) of the growing
 120  * buffer is completely filled.
 121  */
 122 
 123 void
 124 mcview_growbuf_read_until (WView *view, off_t ofs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     gboolean short_read = FALSE;
 127 
 128     g_assert (view->growbuf_in_use);
 129 
 130     if (view->growbuf_finished)
 131         return;
 132 
 133     while (mcview_growbuf_filesize (view) < ofs || short_read)
 134     {
 135         ssize_t nread = 0;
 136         byte *p;
 137         size_t bytesfree;
 138 
 139         if (view->growbuf_lastindex == VIEW_PAGE_SIZE)
 140         {
 141             // Append a new block to the growing buffer
 142             byte *newblock = g_try_malloc (VIEW_PAGE_SIZE);
 143             if (newblock == NULL)
 144                 return;
 145 
 146             g_ptr_array_add (view->growbuf_blockptr, newblock);
 147             view->growbuf_lastindex = 0;
 148         }
 149 
 150         p = (byte *) g_ptr_array_index (view->growbuf_blockptr, view->growbuf_blockptr->len - 1)
 151             + view->growbuf_lastindex;
 152 
 153         bytesfree = VIEW_PAGE_SIZE - view->growbuf_lastindex;
 154 
 155         if (view->datasource == DS_STDIO_PIPE)
 156         {
 157             mc_pipe_t *sp = view->ds_stdio_pipe;
 158             GError *error = NULL;
 159 
 160             if (bytesfree > MC_PIPE_BUFSIZE)
 161                 bytesfree = MC_PIPE_BUFSIZE;
 162 
 163             sp->out.len = bytesfree;
 164             sp->err.len = MC_PIPE_BUFSIZE;
 165 
 166             mc_pread (sp, &error);
 167 
 168             if (error != NULL)
 169             {
 170                 mcview_show_error (view, NULL, error->message);
 171                 g_error_free (error);
 172                 mcview_growbuf_done (view);
 173                 return;
 174             }
 175 
 176             if (view->pipe_first_err_msg && sp->err.len > 0)
 177             {
 178                 // ignore possible following errors
 179                 /* reset this flag before call of mcview_show_error() to break
 180                  * endless recursion: mcview_growbuf_read_until() -> mcview_show_error() ->
 181                  * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until()
 182                  */
 183                 view->pipe_first_err_msg = FALSE;
 184 
 185                 mcview_show_error (view, NULL, sp->err.buf);
 186 
 187                 /* when switch from parse to raw mode and back,
 188                  * do not close the already closed pipe (see call to mcview_growbuf_done below).
 189                  * return from here since (sp == view->ds_stdio_pipe) would now be invalid.
 190                  * NOTE: this check was removed by ticket #4103 but the above call to
 191                  *       mcview_show_error triggers the stdio pipe handle to be closed:
 192                  *       mcview_close_datasource -> mcview_growbuf_done
 193                  */
 194                 if (view->ds_stdio_pipe == NULL)
 195                     return;
 196             }
 197 
 198             if (sp->out.len > 0)
 199             {
 200                 memmove (p, sp->out.buf, sp->out.len);
 201                 nread = sp->out.len;
 202             }
 203             else if (sp->out.len == MC_PIPE_STREAM_EOF || sp->out.len == MC_PIPE_ERROR_READ)
 204             {
 205                 if (sp->out.len == MC_PIPE_ERROR_READ)
 206                 {
 207                     errno = sp->out.error;
 208                     mcview_show_error (view, _ ("Failed to read data from child stdout"), "");
 209                 }
 210 
 211                 /* when switch from parse to raw mode and back,
 212                  * do not close the already closed pipe after following loop:
 213                  * mcview_growbuf_read_until() -> mcview_show_error() ->
 214                  * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until()
 215                  */
 216                 mcview_growbuf_done (view);
 217 
 218                 mcview_display (view);
 219                 return;
 220             }
 221         }
 222         else
 223         {
 224             g_assert (view->datasource == DS_VFS_PIPE);
 225             do
 226             {
 227                 nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
 228             }
 229             while (nread == -1 && errno == EINTR);
 230 
 231             if (nread <= 0)
 232             {
 233                 mcview_growbuf_done (view);
 234                 return;
 235             }
 236         }
 237         short_read = ((size_t) nread < bytesfree);
 238         view->growbuf_lastindex += nread;
 239     }
 240 }
 241 
 242 /* --------------------------------------------------------------------------------------------- */
 243 
 244 gboolean
 245 mcview_get_byte_growing_buffer (WView *view, off_t byte_index, int *retval)
     /* [previous][next][first][last][top][bottom][index][help]  */
 246 {
 247     char *p;
 248 
 249     g_assert (view->growbuf_in_use);
 250 
 251     if (retval != NULL)
 252         *retval = -1;
 253 
 254     if (byte_index < 0)
 255         return FALSE;
 256 
 257     p = mcview_get_ptr_growing_buffer (view, byte_index);
 258     if (p == NULL)
 259         return FALSE;
 260 
 261     if (retval != NULL)
 262         *retval = (unsigned char) (*p);
 263 
 264     return TRUE;
 265 }
 266 
 267 /* --------------------------------------------------------------------------------------------- */
 268 
 269 char *
 270 mcview_get_ptr_growing_buffer (WView *view, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272     off_t pageno, pageindex;
 273 
 274     g_assert (view->growbuf_in_use);
 275 
 276     if (byte_index < 0)
 277         return NULL;
 278 
 279     pageno = byte_index / VIEW_PAGE_SIZE;
 280     pageindex = byte_index % VIEW_PAGE_SIZE;
 281 
 282     mcview_growbuf_read_until (view, byte_index + 1);
 283     if (view->growbuf_blockptr->len == 0)
 284         return NULL;
 285     if (pageno < (off_t) view->growbuf_blockptr->len - 1)
 286         return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
 287     if (pageno == (off_t) view->growbuf_blockptr->len - 1
 288         && pageindex < (off_t) view->growbuf_lastindex)
 289         return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
 290     return NULL;
 291 }
 292 
 293 /* --------------------------------------------------------------------------------------------- */

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