1 /*
2    Internal file viewer for the Midnight Commander
3    Functions for datasources
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
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 /*
37    The data source provides the viewer with data from either a file, a
38    string or the output of a command. The mcview_get_byte() function can be
39    used to get the value of a byte at a specific offset. If the offset
40    is out of range, -1 is returned. The function mcview_get_byte_indexed(a,b)
41    returns the byte at the offset a+b, or -1 if a+b is out of range.
42 
43    The mcview_set_byte() function has the effect that later calls to
44    mcview_get_byte() will return the specified byte for this offset. This
45    function is designed only for use by the hexedit component after
46    saving its changes. Inspect the source before you want to use it for
47    other purposes.
48 
49    The mcview_get_filesize() function returns the current size of the
50    data source. If the growing buffer is used, this size may increase
51    later on. Use the mcview_may_still_grow() function when you want to
52    know if the size can change later.
53  */
54 
55 #include <config.h>
56 
57 #include "lib/global.h"
58 #include "lib/vfs/vfs.h"
59 #include "lib/util.h"
60 #include "lib/widget.h"         /* D_NORMAL, D_ERROR */
61 
62 #include "internal.h"
63 
64 /*** global variables ****************************************************************************/
65 
66 /*** file scope macro definitions ****************************************************************/
67 
68 /*** file scope type declarations ****************************************************************/
69 
70 /*** file scope variables ************************************************************************/
71 
72 /* --------------------------------------------------------------------------------------------- */
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
75 
76 static void
mcview_set_datasource_stdio_pipe(WView * view,mc_pipe_t * p)77 mcview_set_datasource_stdio_pipe (WView * view, mc_pipe_t * p)
78 {
79     p->out.len = MC_PIPE_BUFSIZE;
80     p->out.null_term = FALSE;
81     p->err.len = MC_PIPE_BUFSIZE;
82     p->err.null_term = TRUE;
83     view->datasource = DS_STDIO_PIPE;
84     view->ds_stdio_pipe = p;
85     view->pipe_first_err_msg = TRUE;
86 
87     mcview_growbuf_init (view);
88 }
89 
90 /* --------------------------------------------------------------------------------------------- */
91 /*** public functions ****************************************************************************/
92 /* --------------------------------------------------------------------------------------------- */
93 
94 void
mcview_set_datasource_none(WView * view)95 mcview_set_datasource_none (WView * view)
96 {
97     view->datasource = DS_NONE;
98 }
99 
100 /* --------------------------------------------------------------------------------------------- */
101 
102 off_t
mcview_get_filesize(WView * view)103 mcview_get_filesize (WView * view)
104 {
105     switch (view->datasource)
106     {
107     case DS_STDIO_PIPE:
108     case DS_VFS_PIPE:
109         return mcview_growbuf_filesize (view);
110     case DS_FILE:
111         return view->ds_file_filesize;
112     case DS_STRING:
113         return view->ds_string_len;
114     default:
115         return 0;
116     }
117 }
118 
119 /* --------------------------------------------------------------------------------------------- */
120 
121 void
mcview_update_filesize(WView * view)122 mcview_update_filesize (WView * view)
123 {
124     if (view->datasource == DS_FILE)
125     {
126         struct stat st;
127         if (mc_fstat (view->ds_file_fd, &st) != -1)
128             view->ds_file_filesize = st.st_size;
129     }
130 }
131 
132 /* --------------------------------------------------------------------------------------------- */
133 
134 char *
mcview_get_ptr_file(WView * view,off_t byte_index)135 mcview_get_ptr_file (WView * view, off_t byte_index)
136 {
137     g_assert (view->datasource == DS_FILE);
138 
139     mcview_file_load_data (view, byte_index);
140     if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
141         return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
142     return NULL;
143 }
144 
145 /* --------------------------------------------------------------------------------------------- */
146 
147 /* Invalid UTF-8 is reported as negative integers (one for each byte),
148  * see ticket 3783. */
149 gboolean
mcview_get_utf(WView * view,off_t byte_index,int * ch,int * ch_len)150 mcview_get_utf (WView * view, off_t byte_index, int *ch, int *ch_len)
151 {
152     gchar *str = NULL;
153     int res;
154     gchar utf8buf[UTF8_CHAR_LEN + 1];
155 
156     switch (view->datasource)
157     {
158     case DS_STDIO_PIPE:
159     case DS_VFS_PIPE:
160         str = mcview_get_ptr_growing_buffer (view, byte_index);
161         break;
162     case DS_FILE:
163         str = mcview_get_ptr_file (view, byte_index);
164         break;
165     case DS_STRING:
166         str = mcview_get_ptr_string (view, byte_index);
167         break;
168     case DS_NONE:
169     default:
170         break;
171     }
172 
173     *ch = 0;
174 
175     if (str == NULL)
176         return FALSE;
177 
178     res = g_utf8_get_char_validated (str, -1);
179 
180     if (res < 0)
181     {
182         /* Retry with explicit bytes to make sure it's not a buffer boundary */
183         int i;
184 
185         for (i = 0; i < UTF8_CHAR_LEN; i++)
186         {
187             if (mcview_get_byte (view, byte_index + i, &res))
188                 utf8buf[i] = res;
189             else
190             {
191                 utf8buf[i] = '\0';
192                 break;
193             }
194         }
195         utf8buf[UTF8_CHAR_LEN] = '\0';
196         str = utf8buf;
197         res = g_utf8_get_char_validated (str, -1);
198     }
199 
200     if (res < 0)
201     {
202         /* Implicit conversion from signed char to signed int keeps negative values. */
203         *ch = *str;
204         *ch_len = 1;
205     }
206     else
207     {
208         gchar *next_ch = NULL;
209 
210         *ch = res;
211         /* Calculate UTF-8 char length */
212         next_ch = g_utf8_next_char (str);
213         *ch_len = next_ch - str;
214     }
215 
216     return TRUE;
217 }
218 
219 /* --------------------------------------------------------------------------------------------- */
220 
221 char *
mcview_get_ptr_string(WView * view,off_t byte_index)222 mcview_get_ptr_string (WView * view, off_t byte_index)
223 {
224     g_assert (view->datasource == DS_STRING);
225 
226     if (byte_index >= 0 && byte_index < (off_t) view->ds_string_len)
227         return (char *) (view->ds_string_data + byte_index);
228     return NULL;
229 }
230 
231 /* --------------------------------------------------------------------------------------------- */
232 
233 gboolean
mcview_get_byte_string(WView * view,off_t byte_index,int * retval)234 mcview_get_byte_string (WView * view, off_t byte_index, int *retval)
235 {
236     char *p;
237 
238     if (retval != NULL)
239         *retval = -1;
240 
241     p = mcview_get_ptr_string (view, byte_index);
242     if (p == NULL)
243         return FALSE;
244 
245     if (retval != NULL)
246         *retval = (unsigned char) (*p);
247     return TRUE;
248 }
249 
250 /* --------------------------------------------------------------------------------------------- */
251 
252 gboolean
mcview_get_byte_none(WView * view,off_t byte_index,int * retval)253 mcview_get_byte_none (WView * view, off_t byte_index, int *retval)
254 {
255     (void) &view;
256     (void) byte_index;
257 
258     g_assert (view->datasource == DS_NONE);
259 
260     if (retval != NULL)
261         *retval = -1;
262     return FALSE;
263 }
264 
265 /* --------------------------------------------------------------------------------------------- */
266 
267 void
mcview_set_byte(WView * view,off_t offset,byte b)268 mcview_set_byte (WView * view, off_t offset, byte b)
269 {
270     (void) &b;
271 
272     g_assert (offset < mcview_get_filesize (view));
273     g_assert (view->datasource == DS_FILE);
274 
275     view->ds_file_datalen = 0;  /* just force reloading */
276 }
277 
278 /* --------------------------------------------------------------------------------------------- */
279 
280 /*static */
281 void
mcview_file_load_data(WView * view,off_t byte_index)282 mcview_file_load_data (WView * view, off_t byte_index)
283 {
284     off_t blockoffset;
285     ssize_t res;
286     size_t bytes_read;
287 
288     g_assert (view->datasource == DS_FILE);
289 
290     if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
291         return;
292 
293     if (byte_index >= view->ds_file_filesize)
294         return;
295 
296     blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
297     if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
298         goto error;
299 
300     bytes_read = 0;
301     while (bytes_read < view->ds_file_datasize)
302     {
303         res =
304             mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
305                      view->ds_file_datasize - bytes_read);
306         if (res == -1)
307             goto error;
308         if (res == 0)
309             break;
310         bytes_read += (size_t) res;
311     }
312     view->ds_file_offset = blockoffset;
313     if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
314     {
315         /* the file has grown in the meantime -- stick to the old size */
316         view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
317     }
318     else
319     {
320         view->ds_file_datalen = bytes_read;
321     }
322     return;
323 
324   error:
325     view->ds_file_datalen = 0;
326 }
327 
328 /* --------------------------------------------------------------------------------------------- */
329 
330 void
mcview_close_datasource(WView * view)331 mcview_close_datasource (WView * view)
332 {
333     switch (view->datasource)
334     {
335     case DS_NONE:
336         break;
337     case DS_STDIO_PIPE:
338         if (view->ds_stdio_pipe != NULL)
339         {
340             mcview_growbuf_done (view);
341             mcview_display (view);
342         }
343         mcview_growbuf_free (view);
344         break;
345     case DS_VFS_PIPE:
346         if (view->ds_vfs_pipe != -1)
347             mcview_growbuf_done (view);
348         mcview_growbuf_free (view);
349         break;
350     case DS_FILE:
351         (void) mc_close (view->ds_file_fd);
352         view->ds_file_fd = -1;
353         MC_PTR_FREE (view->ds_file_data);
354         break;
355     case DS_STRING:
356         MC_PTR_FREE (view->ds_string_data);
357         break;
358     default:
359         break;
360     }
361     view->datasource = DS_NONE;
362 }
363 
364 /* --------------------------------------------------------------------------------------------- */
365 
366 void
mcview_set_datasource_file(WView * view,int fd,const struct stat * st)367 mcview_set_datasource_file (WView * view, int fd, const struct stat *st)
368 {
369     view->datasource = DS_FILE;
370     view->ds_file_fd = fd;
371     view->ds_file_filesize = st->st_size;
372     view->ds_file_offset = 0;
373     view->ds_file_data = g_malloc (4096);
374     view->ds_file_datalen = 0;
375     view->ds_file_datasize = 4096;
376 }
377 
378 /* --------------------------------------------------------------------------------------------- */
379 
380 gboolean
mcview_load_command_output(WView * view,const char * command)381 mcview_load_command_output (WView * view, const char *command)
382 {
383     mc_pipe_t *p;
384     GError *error = NULL;
385 
386     mcview_close_datasource (view);
387 
388     p = mc_popen (command, TRUE, TRUE, &error);
389     if (p == NULL)
390     {
391         mcview_display (view);
392         mcview_show_error (view, error->message);
393         g_error_free (error);
394         return FALSE;
395     }
396 
397     /* Check if filter produced any output */
398     mcview_set_datasource_stdio_pipe (view, p);
399     if (!mcview_get_byte (view, 0, NULL))
400     {
401         mcview_close_datasource (view);
402         mcview_display (view);
403         return FALSE;
404     }
405 
406     return TRUE;
407 }
408 
409 /* --------------------------------------------------------------------------------------------- */
410 
411 void
mcview_set_datasource_vfs_pipe(WView * view,int fd)412 mcview_set_datasource_vfs_pipe (WView * view, int fd)
413 {
414     g_assert (fd != -1);
415 
416     view->datasource = DS_VFS_PIPE;
417     view->ds_vfs_pipe = fd;
418 
419     mcview_growbuf_init (view);
420 }
421 
422 /* --------------------------------------------------------------------------------------------- */
423 
424 void
mcview_set_datasource_string(WView * view,const char * s)425 mcview_set_datasource_string (WView * view, const char *s)
426 {
427     view->datasource = DS_STRING;
428     view->ds_string_len = strlen (s);
429     view->ds_string_data = (byte *) g_strndup (s, view->ds_string_len);
430 }
431 
432 /* --------------------------------------------------------------------------------------------- */
433