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