doc/FAQ: fix typo.
[midnight-commander.git] / src / viewer / growbuf.c
blobe2a5db7e752a87bd9ed5b1a8615575a62aab30ff
1 /*
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.
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
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/>.
36 #include <config.h>
37 #include <errno.h>
39 #include "lib/global.h"
40 #include "lib/vfs/vfs.h"
41 #include "lib/util.h"
42 #include "lib/widget.h" /* D_NORMAL */
44 #include "internal.h"
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 /* --------------------------------------------------------------------------------------------- */
64 void
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 /* --------------------------------------------------------------------------------------------- */
75 void
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 /* --------------------------------------------------------------------------------------------- */
94 void
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 /* --------------------------------------------------------------------------------------------- */
106 off_t
107 mcview_growbuf_filesize (WView *view)
109 g_assert (view->growbuf_in_use);
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;
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.
123 void
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)
131 return;
133 while (mcview_growbuf_filesize (view) < ofs || short_read)
135 ssize_t nread = 0;
136 byte *p;
137 size_t bytesfree;
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)
144 return;
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);
168 if (error != NULL)
170 mcview_show_error (view, error->message);
171 g_error_free (error);
172 mcview_growbuf_done (view);
173 return;
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)
195 return;
198 if (sp->out.len > 0)
200 memmove (p, sp->out.buf, sp->out.len);
201 nread = 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)
207 char *err_msg;
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);
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);
223 return;
226 else
228 g_assert (view->datasource == DS_VFS_PIPE);
231 nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
233 while (nread == -1 && errno == EINTR);
235 if (nread <= 0)
237 mcview_growbuf_done (view);
238 return;
241 short_read = ((size_t) nread < bytesfree);
242 view->growbuf_lastindex += nread;
246 /* --------------------------------------------------------------------------------------------- */
248 gboolean
249 mcview_get_byte_growing_buffer (WView *view, off_t byte_index, int *retval)
251 char *p;
253 g_assert (view->growbuf_in_use);
255 if (retval != NULL)
256 *retval = -1;
258 if (byte_index < 0)
259 return FALSE;
261 p = mcview_get_ptr_growing_buffer (view, byte_index);
262 if (p == NULL)
263 return FALSE;
265 if (retval != NULL)
266 *retval = (unsigned char) (*p);
268 return TRUE;
271 /* --------------------------------------------------------------------------------------------- */
273 char *
274 mcview_get_ptr_growing_buffer (WView *view, off_t byte_index)
276 off_t pageno, pageindex;
278 g_assert (view->growbuf_in_use);
280 if (byte_index < 0)
281 return NULL;
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)
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;
297 /* --------------------------------------------------------------------------------------------- */