1 /*
2 Internal file viewer for the Midnight Commander
3 Function for work with growing bufers
4
5 Copyright (C) 1994-2021
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
mcview_growbuf_init(WView * view)65 mcview_growbuf_init (WView * view)
66 {
67 view->growbuf_in_use = TRUE;
68 view->growbuf_blockptr = g_ptr_array_new ();
69 view->growbuf_lastindex = VIEW_PAGE_SIZE;
70 view->growbuf_finished = FALSE;
71 }
72
73 /* --------------------------------------------------------------------------------------------- */
74
75 void
mcview_growbuf_done(WView * view)76 mcview_growbuf_done (WView * view)
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
mcview_growbuf_free(WView * view)95 mcview_growbuf_free (WView * view)
96 {
97 g_assert (view->growbuf_in_use);
98
99 g_ptr_array_foreach (view->growbuf_blockptr, (GFunc) g_free, NULL);
100
101 (void) g_ptr_array_free (view->growbuf_blockptr, TRUE);
102
103 view->growbuf_blockptr = NULL;
104 view->growbuf_in_use = FALSE;
105 }
106
107 /* --------------------------------------------------------------------------------------------- */
108
109 off_t
mcview_growbuf_filesize(WView * view)110 mcview_growbuf_filesize (WView * view)
111 {
112 g_assert (view->growbuf_in_use);
113
114 if (view->growbuf_blockptr->len == 0)
115 return 0;
116 else
117 return ((off_t) view->growbuf_blockptr->len - 1) * VIEW_PAGE_SIZE + view->growbuf_lastindex;
118 }
119
120 /* --------------------------------------------------------------------------------------------- */
121 /** Copies the output from the pipe to the growing buffer, until either
122 * the end-of-pipe is reached or the interval [0..ofs) of the growing
123 * buffer is completely filled.
124 */
125
126 void
mcview_growbuf_read_until(WView * view,off_t ofs)127 mcview_growbuf_read_until (WView * view, off_t ofs)
128 {
129 gboolean short_read = FALSE;
130
131 g_assert (view->growbuf_in_use);
132
133 if (view->growbuf_finished)
134 return;
135
136 while (mcview_growbuf_filesize (view) < ofs || short_read)
137 {
138 ssize_t nread = 0;
139 byte *p;
140 size_t bytesfree;
141
142 if (view->growbuf_lastindex == VIEW_PAGE_SIZE)
143 {
144 /* Append a new block to the growing buffer */
145 byte *newblock = g_try_malloc (VIEW_PAGE_SIZE);
146 if (newblock == NULL)
147 return;
148
149 g_ptr_array_add (view->growbuf_blockptr, newblock);
150 view->growbuf_lastindex = 0;
151 }
152
153 p = (byte *) g_ptr_array_index (view->growbuf_blockptr,
154 view->growbuf_blockptr->len - 1) + view->growbuf_lastindex;
155
156 bytesfree = VIEW_PAGE_SIZE - view->growbuf_lastindex;
157
158 if (view->datasource == DS_STDIO_PIPE)
159 {
160 mc_pipe_t *sp = view->ds_stdio_pipe;
161 GError *error = NULL;
162
163 if (bytesfree > MC_PIPE_BUFSIZE)
164 bytesfree = MC_PIPE_BUFSIZE;
165
166 sp->out.len = bytesfree;
167 sp->err.len = MC_PIPE_BUFSIZE;
168
169 mc_pread (sp, &error);
170
171 if (error != NULL)
172 {
173 mcview_show_error (view, error->message);
174 g_error_free (error);
175 mcview_growbuf_done (view);
176 return;
177 }
178
179 if (view->pipe_first_err_msg && sp->err.len > 0)
180 {
181 /* ignore possible following errors */
182 /* reset this flag before call of mcview_show_error() to break
183 * endless recursion: mcview_growbuf_read_until() -> mcview_show_error() ->
184 * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until()
185 */
186 view->pipe_first_err_msg = FALSE;
187
188 mcview_show_error (view, sp->err.buf);
189 }
190
191 if (sp->out.len > 0)
192 {
193 memmove (p, sp->out.buf, sp->out.len);
194 nread = sp->out.len;
195 }
196 else if (sp->out.len == MC_PIPE_STREAM_EOF || sp->out.len == MC_PIPE_ERROR_READ)
197 {
198 if (sp->out.len == MC_PIPE_ERROR_READ)
199 {
200 char *err_msg;
201
202 err_msg = g_strdup_printf (_("Failed to read data from child stdout:\n%s"),
203 unix_error_string (sp->out.error));
204 mcview_show_error (view, err_msg);
205 g_free (err_msg);
206 }
207
208 /* when switch from parse to raw mode and back,
209 * do not close the already closed pipe after following loop:
210 * mcview_growbuf_read_until() -> mcview_show_error() ->
211 * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until()
212 */
213 mcview_growbuf_done (view);
214
215 mcview_display (view);
216 return;
217 }
218 }
219 else
220 {
221 g_assert (view->datasource == DS_VFS_PIPE);
222 do
223 {
224 nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
225 }
226 while (nread == -1 && errno == EINTR);
227
228 if (nread <= 0)
229 {
230 mcview_growbuf_done (view);
231 return;
232 }
233 }
234 short_read = ((size_t) nread < bytesfree);
235 view->growbuf_lastindex += nread;
236 }
237 }
238
239 /* --------------------------------------------------------------------------------------------- */
240
241 gboolean
mcview_get_byte_growing_buffer(WView * view,off_t byte_index,int * retval)242 mcview_get_byte_growing_buffer (WView * view, off_t byte_index, int *retval)
243 {
244 char *p;
245
246 g_assert (view->growbuf_in_use);
247
248 if (retval != NULL)
249 *retval = -1;
250
251 if (byte_index < 0)
252 return FALSE;
253
254 p = mcview_get_ptr_growing_buffer (view, byte_index);
255 if (p == NULL)
256 return FALSE;
257
258 if (retval != NULL)
259 *retval = (unsigned char) (*p);
260
261 return TRUE;
262 }
263
264 /* --------------------------------------------------------------------------------------------- */
265
266 char *
mcview_get_ptr_growing_buffer(WView * view,off_t byte_index)267 mcview_get_ptr_growing_buffer (WView * view, off_t byte_index)
268 {
269 off_t pageno, pageindex;
270
271 g_assert (view->growbuf_in_use);
272
273 if (byte_index < 0)
274 return NULL;
275
276 pageno = byte_index / VIEW_PAGE_SIZE;
277 pageindex = byte_index % VIEW_PAGE_SIZE;
278
279 mcview_growbuf_read_until (view, byte_index + 1);
280 if (view->growbuf_blockptr->len == 0)
281 return NULL;
282 if (pageno < (off_t) view->growbuf_blockptr->len - 1)
283 return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
284 if (pageno == (off_t) view->growbuf_blockptr->len - 1
285 && pageindex < (off_t) view->growbuf_lastindex)
286 return ((char *) g_ptr_array_index (view->growbuf_blockptr, pageno) + pageindex);
287 return NULL;
288 }
289
290 /* --------------------------------------------------------------------------------------------- */
291