lib/widget/input_complete.c: minor refactoring and optimization.
[midnight-commander.git] / src / viewer / datasource.c
blobac4362d7daba8605b01357c1ffddf5cf90a60fb6
1 /*
2 Internal file viewer for the Midnight Commander
3 Functions for datasources
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009, 2011
7 The Free Software Foundation, Inc.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009
19 Ilia Maslakov <il.smind@gmail.com>, 2009
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
38 The data source provides the viewer with data from either a file, a
39 string or the output of a command. The mcview_get_byte() function can be
40 used to get the value of a byte at a specific offset. If the offset
41 is out of range, -1 is returned. The function mcview_get_byte_indexed(a,b)
42 returns the byte at the offset a+b, or -1 if a+b is out of range.
44 The mcview_set_byte() function has the effect that later calls to
45 mcview_get_byte() will return the specified byte for this offset. This
46 function is designed only for use by the hexedit component after
47 saving its changes. Inspect the source before you want to use it for
48 other purposes.
50 The mcview_get_filesize() function returns the current size of the
51 data source. If the growing buffer is used, this size may increase
52 later on. Use the mcview_may_still_grow() function when you want to
53 know if the size can change later.
56 #include <config.h>
58 #include "lib/global.h"
59 #include "lib/vfs/vfs.h"
60 #include "lib/util.h"
61 #include "lib/widget.h" /* D_NORMAL, D_ERROR */
63 #include "internal.h"
65 /*** global variables ****************************************************************************/
67 /*** file scope macro definitions ****************************************************************/
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 /* --------------------------------------------------------------------------------------------- */
77 /*** public functions ****************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
80 static void
81 mcview_set_datasource_stdio_pipe (mcview_t * view, FILE * fp)
83 #ifdef HAVE_ASSERT_H
84 assert (fp != NULL);
85 #endif
86 view->datasource = DS_STDIO_PIPE;
87 view->ds_stdio_pipe = fp;
89 mcview_growbuf_init (view);
92 /* --------------------------------------------------------------------------------------------- */
94 void
95 mcview_set_datasource_none (mcview_t * view)
97 view->datasource = DS_NONE;
100 /* --------------------------------------------------------------------------------------------- */
102 off_t
103 mcview_get_filesize (mcview_t * view)
105 switch (view->datasource)
107 case DS_NONE:
108 return 0;
109 case DS_STDIO_PIPE:
110 case DS_VFS_PIPE:
111 return mcview_growbuf_filesize (view);
112 case DS_FILE:
113 return view->ds_file_filesize;
114 case DS_STRING:
115 return view->ds_string_len;
116 default:
117 #ifdef HAVE_ASSERT_H
118 assert (!"Unknown datasource type");
119 #endif
120 return 0;
124 /* --------------------------------------------------------------------------------------------- */
126 void
127 mcview_update_filesize (mcview_t * view)
129 if (view->datasource == DS_FILE)
131 struct stat st;
132 if (mc_fstat (view->ds_file_fd, &st) != -1)
133 view->ds_file_filesize = st.st_size;
137 /* --------------------------------------------------------------------------------------------- */
139 char *
140 mcview_get_ptr_file (mcview_t * view, off_t byte_index)
142 #ifdef HAVE_ASSERT_H
143 assert (view->datasource == DS_FILE);
144 #endif
146 mcview_file_load_data (view, byte_index);
147 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
148 return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
149 return NULL;
152 /* --------------------------------------------------------------------------------------------- */
154 char *
155 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
157 #ifdef HAVE_ASSERT_H
158 assert (view->datasource == DS_STRING);
159 #endif
160 if (byte_index < (off_t) view->ds_string_len)
161 return (char *) (view->ds_string_data + byte_index);
162 return NULL;
165 /* --------------------------------------------------------------------------------------------- */
168 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
170 gchar *str = NULL;
171 int res = -1;
172 gunichar ch;
173 gchar *next_ch = NULL;
174 gchar utf8buf[UTF8_CHAR_LEN + 1];
176 *char_width = 0;
177 *result = FALSE;
179 switch (view->datasource)
181 case DS_STDIO_PIPE:
182 case DS_VFS_PIPE:
183 str = mcview_get_ptr_growing_buffer (view, byte_index);
184 break;
185 case DS_FILE:
186 str = mcview_get_ptr_file (view, byte_index);
187 break;
188 case DS_STRING:
189 str = mcview_get_ptr_string (view, byte_index);
190 break;
191 case DS_NONE:
192 break;
195 if (str == NULL)
196 return 0;
198 res = g_utf8_get_char_validated (str, -1);
200 if (res < 0)
202 /* Retry with explicit bytes to make sure it's not a buffer boundary */
203 int i;
204 for (i = 0; i < UTF8_CHAR_LEN; i++)
206 if (mcview_get_byte (view, byte_index + i, &res))
207 utf8buf[i] = res;
208 else
210 utf8buf[i] = '\0';
211 break;
214 utf8buf[UTF8_CHAR_LEN] = '\0';
215 str = utf8buf;
216 res = g_utf8_get_char_validated (str, -1);
219 if (res < 0)
221 ch = *str;
222 *char_width = 1;
224 else
226 ch = res;
227 /* Calculate UTF-8 char width */
228 next_ch = g_utf8_next_char (str);
229 if (next_ch)
230 *char_width = next_ch - str;
231 else
232 return 0;
234 *result = TRUE;
235 return ch;
238 /* --------------------------------------------------------------------------------------------- */
240 gboolean
241 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
243 #ifdef HAVE_ASSERT_H
244 assert (view->datasource == DS_STRING);
245 #endif
246 if (byte_index < (off_t) view->ds_string_len)
248 if (retval)
249 *retval = view->ds_string_data[byte_index];
250 return TRUE;
252 if (retval)
253 *retval = -1;
254 return FALSE;
257 /* --------------------------------------------------------------------------------------------- */
259 gboolean
260 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
262 (void) &view;
263 (void) byte_index;
265 #ifdef HAVE_ASSERT_H
266 assert (view->datasource == DS_NONE);
267 #endif
269 if (retval != NULL)
270 *retval = -1;
271 return FALSE;
274 /* --------------------------------------------------------------------------------------------- */
276 void
277 mcview_set_byte (mcview_t * view, off_t offset, byte b)
279 (void) &b;
280 #ifndef HAVE_ASSERT_H
281 (void) offset;
282 #else
283 assert (offset < mcview_get_filesize (view));
284 assert (view->datasource == DS_FILE);
286 #endif
287 view->ds_file_datalen = 0; /* just force reloading */
290 /* --------------------------------------------------------------------------------------------- */
292 /*static */
293 void
294 mcview_file_load_data (mcview_t * view, off_t byte_index)
296 off_t blockoffset;
297 ssize_t res;
298 size_t bytes_read;
300 #ifdef HAVE_ASSERT_H
301 assert (view->datasource == DS_FILE);
302 #endif
304 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
305 return;
307 if (byte_index >= view->ds_file_filesize)
308 return;
310 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
311 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
312 goto error;
314 bytes_read = 0;
315 while (bytes_read < view->ds_file_datasize)
317 res =
318 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
319 view->ds_file_datasize - bytes_read);
320 if (res == -1)
321 goto error;
322 if (res == 0)
323 break;
324 bytes_read += (size_t) res;
326 view->ds_file_offset = blockoffset;
327 if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
329 /* the file has grown in the meantime -- stick to the old size */
330 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
332 else
334 view->ds_file_datalen = bytes_read;
336 return;
338 error:
339 view->ds_file_datalen = 0;
342 /* --------------------------------------------------------------------------------------------- */
344 void
345 mcview_close_datasource (mcview_t * view)
347 switch (view->datasource)
349 case DS_NONE:
350 break;
351 case DS_STDIO_PIPE:
352 if (view->ds_stdio_pipe != NULL)
354 (void) pclose (view->ds_stdio_pipe);
355 mcview_display (view);
356 close_error_pipe (D_NORMAL, NULL);
357 view->ds_stdio_pipe = NULL;
359 mcview_growbuf_free (view);
360 break;
361 case DS_VFS_PIPE:
362 if (view->ds_vfs_pipe != -1)
364 (void) mc_close (view->ds_vfs_pipe);
365 view->ds_vfs_pipe = -1;
367 mcview_growbuf_free (view);
368 break;
369 case DS_FILE:
370 (void) mc_close (view->ds_file_fd);
371 view->ds_file_fd = -1;
372 g_free (view->ds_file_data);
373 view->ds_file_data = NULL;
374 break;
375 case DS_STRING:
376 g_free (view->ds_string_data);
377 view->ds_string_data = NULL;
378 break;
379 default:
380 #ifdef HAVE_ASSERT_H
381 assert (!"Unknown datasource type")
382 #endif
385 view->datasource = DS_NONE;
388 /* --------------------------------------------------------------------------------------------- */
390 void
391 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
393 view->datasource = DS_FILE;
394 view->ds_file_fd = fd;
395 view->ds_file_filesize = st->st_size;
396 view->ds_file_offset = 0;
397 view->ds_file_data = g_malloc (4096);
398 view->ds_file_datalen = 0;
399 view->ds_file_datasize = 4096;
402 /* --------------------------------------------------------------------------------------------- */
404 gboolean
405 mcview_load_command_output (mcview_t * view, const char *command)
407 FILE *fp;
409 mcview_close_datasource (view);
411 open_error_pipe ();
412 fp = popen (command, "r");
413 if (fp == NULL)
415 /* Avoid two messages. Message from stderr has priority. */
416 mcview_display (view);
417 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
418 mcview_show_error (view, _("Cannot spawn child process"));
419 return FALSE;
422 /* First, check if filter produced any output */
423 mcview_set_datasource_stdio_pipe (view, fp);
424 if (!mcview_get_byte (view, 0, NULL))
426 mcview_close_datasource (view);
428 /* Avoid two messages. Message from stderr has priority. */
429 mcview_display (view);
430 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
431 mcview_show_error (view, _("Empty output from child filter"));
432 return FALSE;
434 else
437 * At least something was read correctly. Close stderr and let
438 * program die if it will try to write something there.
440 * Ideally stderr should be read asynchronously to prevent programs
441 * from blocking (poll/select multiplexor).
443 close_error_pipe (D_NORMAL, NULL);
445 return TRUE;
448 /* --------------------------------------------------------------------------------------------- */
450 void
451 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
453 #ifdef HAVE_ASSERT_H
454 assert (fd != -1);
455 #endif
456 view->datasource = DS_VFS_PIPE;
457 view->ds_vfs_pipe = fd;
459 mcview_growbuf_init (view);
462 /* --------------------------------------------------------------------------------------------- */
464 void
465 mcview_set_datasource_string (mcview_t * view, const char *s)
467 view->datasource = DS_STRING;
468 view->ds_string_data = (byte *) g_strdup (s);
469 view->ds_string_len = strlen (s);
472 /* --------------------------------------------------------------------------------------------- */