2 Internal file viewer for the Midnight Commander
3 Function for work with growing buffers
5 Copyright (C) 1994-2025
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
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
20 This file is part of the Midnight Commander.
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.
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.
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/>.
39 #include "lib/global.h"
40 #include "lib/vfs/vfs.h"
42 #include "lib/widget.h" /* D_NORMAL */
46 /* Block size for reading files in parts */
47 #define VIEW_PAGE_SIZE ((size_t) 8192)
49 /*** global variables ****************************************************************************/
51 /*** file scope macro definitions ****************************************************************/
53 /*** file scope type declarations ****************************************************************/
55 /*** file scope variables ************************************************************************/
57 /*** file scope functions ************************************************************************/
58 /* --------------------------------------------------------------------------------------------- */
60 /* --------------------------------------------------------------------------------------------- */
61 /*** public functions ****************************************************************************/
62 /* --------------------------------------------------------------------------------------------- */
65 mcview_growbuf_init (WView
*view
)
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
;
73 /* --------------------------------------------------------------------------------------------- */
76 mcview_growbuf_done (WView
*view
)
78 view
->growbuf_finished
= TRUE
;
80 if (view
->datasource
== DS_STDIO_PIPE
)
82 mc_pclose (view
->ds_stdio_pipe
, NULL
);
83 view
->ds_stdio_pipe
= NULL
;
85 else /* view->datasource == DS_VFS_PIPE */
87 (void) mc_close (view
->ds_vfs_pipe
);
88 view
->ds_vfs_pipe
= -1;
92 /* --------------------------------------------------------------------------------------------- */
95 mcview_growbuf_free (WView
*view
)
97 g_assert (view
->growbuf_in_use
);
99 g_ptr_array_free (view
->growbuf_blockptr
, TRUE
);
100 view
->growbuf_blockptr
= NULL
;
101 view
->growbuf_in_use
= FALSE
;
104 /* --------------------------------------------------------------------------------------------- */
107 mcview_growbuf_filesize (WView
*view
)
109 g_assert (view
->growbuf_in_use
);
111 if (view
->growbuf_blockptr
->len
== 0)
114 return ((off_t
) view
->growbuf_blockptr
->len
- 1) * VIEW_PAGE_SIZE
+ view
->growbuf_lastindex
;
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.
124 mcview_growbuf_read_until (WView
*view
, off_t ofs
)
126 gboolean short_read
= FALSE
;
128 g_assert (view
->growbuf_in_use
);
130 if (view
->growbuf_finished
)
133 while (mcview_growbuf_filesize (view
) < ofs
|| short_read
)
139 if (view
->growbuf_lastindex
== VIEW_PAGE_SIZE
)
141 /* Append a new block to the growing buffer */
142 byte
*newblock
= g_try_malloc (VIEW_PAGE_SIZE
);
143 if (newblock
== NULL
)
146 g_ptr_array_add (view
->growbuf_blockptr
, newblock
);
147 view
->growbuf_lastindex
= 0;
150 p
= (byte
*) g_ptr_array_index (view
->growbuf_blockptr
,
151 view
->growbuf_blockptr
->len
- 1) + view
->growbuf_lastindex
;
153 bytesfree
= VIEW_PAGE_SIZE
- view
->growbuf_lastindex
;
155 if (view
->datasource
== DS_STDIO_PIPE
)
157 mc_pipe_t
*sp
= view
->ds_stdio_pipe
;
158 GError
*error
= NULL
;
160 if (bytesfree
> MC_PIPE_BUFSIZE
)
161 bytesfree
= MC_PIPE_BUFSIZE
;
163 sp
->out
.len
= bytesfree
;
164 sp
->err
.len
= MC_PIPE_BUFSIZE
;
166 mc_pread (sp
, &error
);
170 mcview_show_error (view
, error
->message
);
171 g_error_free (error
);
172 mcview_growbuf_done (view
);
176 if (view
->pipe_first_err_msg
&& sp
->err
.len
> 0)
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()
183 view
->pipe_first_err_msg
= FALSE
;
185 mcview_show_error (view
, sp
->err
.buf
);
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
194 if (view
->ds_stdio_pipe
== NULL
)
200 memmove (p
, sp
->out
.buf
, sp
->out
.len
);
203 else if (sp
->out
.len
== MC_PIPE_STREAM_EOF
|| sp
->out
.len
== MC_PIPE_ERROR_READ
)
205 if (sp
->out
.len
== MC_PIPE_ERROR_READ
)
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
);
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()
220 mcview_growbuf_done (view
);
222 mcview_display (view
);
228 g_assert (view
->datasource
== DS_VFS_PIPE
);
231 nread
= mc_read (view
->ds_vfs_pipe
, p
, bytesfree
);
233 while (nread
== -1 && errno
== EINTR
);
237 mcview_growbuf_done (view
);
241 short_read
= ((size_t) nread
< bytesfree
);
242 view
->growbuf_lastindex
+= nread
;
246 /* --------------------------------------------------------------------------------------------- */
249 mcview_get_byte_growing_buffer (WView
*view
, off_t byte_index
, int *retval
)
253 g_assert (view
->growbuf_in_use
);
261 p
= mcview_get_ptr_growing_buffer (view
, byte_index
);
266 *retval
= (unsigned char) (*p
);
271 /* --------------------------------------------------------------------------------------------- */
274 mcview_get_ptr_growing_buffer (WView
*view
, off_t byte_index
)
276 off_t pageno
, pageindex
;
278 g_assert (view
->growbuf_in_use
);
283 pageno
= byte_index
/ VIEW_PAGE_SIZE
;
284 pageindex
= byte_index
% VIEW_PAGE_SIZE
;
286 mcview_growbuf_read_until (view
, byte_index
+ 1);
287 if (view
->growbuf_blockptr
->len
== 0)
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
);
297 /* --------------------------------------------------------------------------------------------- */