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-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, 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 #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,
 151                                         view->growbuf_blockptr->len - 1) + 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, 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, 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                     char *err_msg;
 208 
 209                     err_msg = g_strdup_printf (_("Failed to read data from child stdout:\n%s"),
 210                                                unix_error_string (sp->out.error));
 211                     mcview_show_error (view, err_msg);
 212                     g_free (err_msg);
 213                 }
 214 
 215                 /* when switch from parse to raw mode and back,
 216                  * do not close the already closed pipe after following loop:
 217                  * mcview_growbuf_read_until() -> mcview_show_error() ->
 218                  * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until()
 219                  */
 220                 mcview_growbuf_done (view);
 221 
 222                 mcview_display (view);
 223                 return;
 224             }
 225         }
 226         else
 227         {
 228             g_assert (view->datasource == DS_VFS_PIPE);
 229             do
 230             {
 231                 nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
 232             }
 233             while (nread == -1 && errno == EINTR);
 234 
 235             if (nread <= 0)
 236             {
 237                 mcview_growbuf_done (view);
 238                 return;
 239             }
 240         }
 241         short_read = ((size_t) nread < bytesfree);
 242         view->growbuf_lastindex += nread;
 243     }
 244 }
 245 
 246 /* --------------------------------------------------------------------------------------------- */
 247 
 248 gboolean
 249 mcview_get_byte_growing_buffer (WView *view, off_t byte_index, int *retval)
     /* [previous][next][first][last][top][bottom][index][help]  */
 250 {
 251     char *p;
 252 
 253     g_assert (view->growbuf_in_use);
 254 
 255     if (retval != NULL)
 256         *retval = -1;
 257 
 258     if (byte_index < 0)
 259         return FALSE;
 260 
 261     p = mcview_get_ptr_growing_buffer (view, byte_index);
 262     if (p == NULL)
 263         return FALSE;
 264 
 265     if (retval != NULL)
 266         *retval = (unsigned char) (*p);
 267 
 268     return TRUE;
 269 }
 270 
 271 /* --------------------------------------------------------------------------------------------- */
 272 
 273 char *
 274 mcview_get_ptr_growing_buffer (WView *view, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 275 {
 276     off_t pageno, pageindex;
 277 
 278     g_assert (view->growbuf_in_use);
 279 
 280     if (byte_index < 0)
 281         return NULL;
 282 
 283     pageno = byte_index / VIEW_PAGE_SIZE;
 284     pageindex = byte_index % VIEW_PAGE_SIZE;
 285 
 286     mcview_growbuf_read_until (view, byte_index + 1);
 287     if (view->growbuf_blockptr->len == 0)
 288         return NULL;
 289     if (pageno < (off_t) view->growbuf_blockptr->len - 1)
 290         return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
 291     if (pageno == (off_t) view->growbuf_blockptr->len - 1
 292         && pageindex < (off_t) view->growbuf_lastindex)
 293         return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
 294     return NULL;
 295 }
 296 
 297 /* --------------------------------------------------------------------------------------------- */

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