Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / viewer / datasource.c
blob2af8368bc0aee11fcadd54ecdba367edeefcb095
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 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
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 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 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, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
39 The data source provides the viewer with data from either a file, a
40 string or the output of a command. The mcview_get_byte() function can be
41 used to get the value of a byte at a specific offset. If the offset
42 is out of range, -1 is returned. The function mcview_get_byte_indexed(a,b)
43 returns the byte at the offset a+b, or -1 if a+b is out of range.
45 The mcview_set_byte() function has the effect that later calls to
46 mcview_get_byte() will return the specified byte for this offset. This
47 function is designed only for use by the hexedit component after
48 saving its changes. Inspect the source before you want to use it for
49 other purposes.
51 The mcview_get_filesize() function returns the current size of the
52 data source. If the growing buffer is used, this size may increase
53 later on. Use the mcview_may_still_grow() function when you want to
54 know if the size can change later.
57 #include <config.h>
59 #include "lib/global.h"
60 #include "lib/vfs/mc-vfs/vfs.h"
61 #include "lib/util.h"
62 #include "lib/widget.h" /* D_NORMAL, D_ERROR */
64 #include "internal.h"
66 /*** global variables ****************************************************************************/
68 /*** file scope macro definitions ****************************************************************/
70 /*** file scope type declarations ****************************************************************/
72 /*** file scope variables ************************************************************************/
74 /*** file scope functions ************************************************************************/
75 /* --------------------------------------------------------------------------------------------- */
77 /* --------------------------------------------------------------------------------------------- */
78 /*** public functions ****************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
81 static void
82 mcview_set_datasource_stdio_pipe (mcview_t * view, FILE * fp)
84 assert (fp != NULL);
85 view->datasource = DS_STDIO_PIPE;
86 view->ds_stdio_pipe = fp;
88 mcview_growbuf_init (view);
91 /* --------------------------------------------------------------------------------------------- */
93 void
94 mcview_set_datasource_none (mcview_t * view)
96 view->datasource = DS_NONE;
99 /* --------------------------------------------------------------------------------------------- */
101 off_t
102 mcview_get_filesize (mcview_t * view)
104 switch (view->datasource)
106 case DS_NONE:
107 return 0;
108 case DS_STDIO_PIPE:
109 case DS_VFS_PIPE:
110 return mcview_growbuf_filesize (view);
111 case DS_FILE:
112 return view->ds_file_filesize;
113 case DS_STRING:
114 return view->ds_string_len;
115 default:
116 assert (!"Unknown datasource type");
117 return 0;
121 /* --------------------------------------------------------------------------------------------- */
123 void
124 mcview_update_filesize (mcview_t * view)
126 if (view->datasource == DS_FILE)
128 struct stat st;
129 if (mc_fstat (view->ds_file_fd, &st) != -1)
130 view->ds_file_filesize = st.st_size;
134 /* --------------------------------------------------------------------------------------------- */
136 char *
137 mcview_get_ptr_file (mcview_t * view, off_t byte_index)
139 assert (view->datasource == DS_FILE);
141 mcview_file_load_data (view, byte_index);
142 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
143 return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
144 return NULL;
147 /* --------------------------------------------------------------------------------------------- */
149 char *
150 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
152 assert (view->datasource == DS_STRING);
153 if (byte_index < (off_t) view->ds_string_len)
154 return (char *) (view->ds_string_data + byte_index);
155 return NULL;
158 /* --------------------------------------------------------------------------------------------- */
161 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
163 gchar *str = NULL;
164 int res = -1;
165 gunichar ch;
166 gchar *next_ch = NULL;
167 int width = 0;
169 *result = TRUE;
171 switch (view->datasource)
173 case DS_STDIO_PIPE:
174 case DS_VFS_PIPE:
175 str = mcview_get_ptr_growing_buffer (view, byte_index);
176 break;
177 case DS_FILE:
178 str = mcview_get_ptr_file (view, byte_index);
179 break;
180 case DS_STRING:
181 str = mcview_get_ptr_string (view, byte_index);
182 break;
183 case DS_NONE:
184 break;
187 if (str == NULL)
189 *result = FALSE;
190 width = 0;
191 return 0;
194 res = g_utf8_get_char_validated (str, -1);
196 if (res < 0)
198 ch = *str;
199 width = 0;
201 else
203 ch = res;
204 /* Calculate UTF-8 char width */
205 next_ch = g_utf8_next_char (str);
206 if (next_ch)
208 width = next_ch - str;
210 else
212 ch = 0;
213 width = 0;
216 *char_width = width;
217 return ch;
220 /* --------------------------------------------------------------------------------------------- */
222 gboolean
223 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
225 assert (view->datasource == DS_STRING);
226 if (byte_index < (off_t) view->ds_string_len)
228 if (retval)
229 *retval = view->ds_string_data[byte_index];
230 return TRUE;
232 if (retval)
233 *retval = -1;
234 return FALSE;
237 /* --------------------------------------------------------------------------------------------- */
239 gboolean
240 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
242 assert (view->datasource == DS_NONE);
243 (void) &view;
244 (void) byte_index;
245 if (retval)
246 *retval = -1;
247 return FALSE;
250 /* --------------------------------------------------------------------------------------------- */
252 void
253 mcview_set_byte (mcview_t * view, off_t offset, byte b)
255 (void) &b;
256 assert (offset < mcview_get_filesize (view));
257 assert (view->datasource == DS_FILE);
258 view->ds_file_datalen = 0; /* just force reloading */
261 /* --------------------------------------------------------------------------------------------- */
263 /*static */
264 void
265 mcview_file_load_data (mcview_t * view, off_t byte_index)
267 off_t blockoffset;
268 ssize_t res;
269 size_t bytes_read;
271 assert (view->datasource == DS_FILE);
273 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
274 return;
276 if (byte_index >= view->ds_file_filesize)
277 return;
279 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
280 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
281 goto error;
283 bytes_read = 0;
284 while (bytes_read < view->ds_file_datasize)
286 res =
287 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
288 view->ds_file_datasize - bytes_read);
289 if (res == -1)
290 goto error;
291 if (res == 0)
292 break;
293 bytes_read += (size_t) res;
295 view->ds_file_offset = blockoffset;
296 if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
298 /* the file has grown in the meantime -- stick to the old size */
299 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
301 else
303 view->ds_file_datalen = bytes_read;
305 return;
307 error:
308 view->ds_file_datalen = 0;
311 /* --------------------------------------------------------------------------------------------- */
313 void
314 mcview_close_datasource (mcview_t * view)
316 switch (view->datasource)
318 case DS_NONE:
319 break;
320 case DS_STDIO_PIPE:
321 if (view->ds_stdio_pipe != NULL)
323 (void) pclose (view->ds_stdio_pipe);
324 mcview_display (view);
325 close_error_pipe (D_NORMAL, NULL);
326 view->ds_stdio_pipe = NULL;
328 mcview_growbuf_free (view);
329 break;
330 case DS_VFS_PIPE:
331 if (view->ds_vfs_pipe != -1)
333 (void) mc_close (view->ds_vfs_pipe);
334 view->ds_vfs_pipe = -1;
336 mcview_growbuf_free (view);
337 break;
338 case DS_FILE:
339 (void) mc_close (view->ds_file_fd);
340 view->ds_file_fd = -1;
341 g_free (view->ds_file_data);
342 view->ds_file_data = NULL;
343 break;
344 case DS_STRING:
345 g_free (view->ds_string_data);
346 view->ds_string_data = NULL;
347 break;
348 default:
349 assert (!"Unknown datasource type");
351 view->datasource = DS_NONE;
354 /* --------------------------------------------------------------------------------------------- */
356 void
357 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
359 view->datasource = DS_FILE;
360 view->ds_file_fd = fd;
361 view->ds_file_filesize = st->st_size;
362 view->ds_file_offset = 0;
363 view->ds_file_data = g_malloc (4096);
364 view->ds_file_datalen = 0;
365 view->ds_file_datasize = 4096;
368 /* --------------------------------------------------------------------------------------------- */
370 gboolean
371 mcview_load_command_output (mcview_t * view, const char *command)
373 FILE *fp;
375 mcview_close_datasource (view);
377 open_error_pipe ();
378 fp = popen (command, "r");
379 if (fp == NULL)
381 /* Avoid two messages. Message from stderr has priority. */
382 mcview_display (view);
383 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
384 mcview_show_error (view, _("Cannot spawn child process"));
385 return FALSE;
388 /* First, check if filter produced any output */
389 mcview_set_datasource_stdio_pipe (view, fp);
390 if (!mcview_get_byte (view, 0, NULL))
392 mcview_close_datasource (view);
394 /* Avoid two messages. Message from stderr has priority. */
395 mcview_display (view);
396 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
397 mcview_show_error (view, _("Empty output from child filter"));
398 return FALSE;
400 else
403 * At least something was read correctly. Close stderr and let
404 * program die if it will try to write something there.
406 * Ideally stderr should be read asynchronously to prevent programs
407 * from blocking (poll/select multiplexor).
409 close_error_pipe (D_NORMAL, NULL);
411 return TRUE;
414 /* --------------------------------------------------------------------------------------------- */
416 void
417 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
419 assert (fd != -1);
420 view->datasource = DS_VFS_PIPE;
421 view->ds_vfs_pipe = fd;
423 mcview_growbuf_init (view);
426 /* --------------------------------------------------------------------------------------------- */
428 void
429 mcview_set_datasource_string (mcview_t * view, const char *s)
431 view->datasource = DS_STRING;
432 view->ds_string_data = (byte *) g_strdup (s);
433 view->ds_string_len = strlen (s);
436 /* --------------------------------------------------------------------------------------------- */