Ticket #1648: implemented single-line boxes.
[kaloumi3.git] / src / viewer / datasource.c
blobcbee2223c9bb9a11a52a28a404a14ff75b67f03d
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 "src/wtools.h"
61 #include "lib/vfs/mc-vfs/vfs.h"
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 ************************************************************************/
75 /*** public functions ****************************************************************************/
77 /* --------------------------------------------------------------------------------------------- */
79 static void
80 mcview_set_datasource_stdio_pipe (mcview_t * view, FILE * fp)
82 assert (fp != NULL);
83 view->datasource = DS_STDIO_PIPE;
84 view->ds_stdio_pipe = fp;
86 mcview_growbuf_init (view);
89 /* --------------------------------------------------------------------------------------------- */
91 void
92 mcview_set_datasource_none (mcview_t * view)
94 view->datasource = DS_NONE;
97 /* --------------------------------------------------------------------------------------------- */
99 off_t
100 mcview_get_filesize (mcview_t * view)
102 switch (view->datasource)
104 case DS_NONE:
105 return 0;
106 case DS_STDIO_PIPE:
107 case DS_VFS_PIPE:
108 return mcview_growbuf_filesize (view);
109 case DS_FILE:
110 return view->ds_file_filesize;
111 case DS_STRING:
112 return view->ds_string_len;
113 default:
114 assert (!"Unknown datasource type");
115 return 0;
119 /* --------------------------------------------------------------------------------------------- */
121 void
122 mcview_update_filesize (mcview_t * view)
124 if (view->datasource == DS_FILE)
126 struct stat st;
127 if (mc_fstat (view->ds_file_fd, &st) != -1)
128 view->ds_file_filesize = st.st_size;
132 /* --------------------------------------------------------------------------------------------- */
134 char *
135 mcview_get_ptr_file (mcview_t * view, off_t byte_index)
137 assert (view->datasource == DS_FILE);
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;
145 /* --------------------------------------------------------------------------------------------- */
147 char *
148 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
150 assert (view->datasource == DS_STRING);
151 if (byte_index < (off_t) view->ds_string_len)
152 return (char *) (view->ds_string_data + byte_index);
153 return NULL;
156 /* --------------------------------------------------------------------------------------------- */
159 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
161 gchar *str = NULL;
162 int res = -1;
163 gunichar ch;
164 gchar *next_ch = NULL;
165 int width = 0;
167 *result = TRUE;
169 switch (view->datasource)
171 case DS_STDIO_PIPE:
172 case DS_VFS_PIPE:
173 str = mcview_get_ptr_growing_buffer (view, byte_index);
174 break;
175 case DS_FILE:
176 str = mcview_get_ptr_file (view, byte_index);
177 break;
178 case DS_STRING:
179 str = mcview_get_ptr_string (view, byte_index);
180 break;
181 case DS_NONE:
182 break;
185 if (str == NULL)
187 *result = FALSE;
188 width = 0;
189 return 0;
192 res = g_utf8_get_char_validated (str, -1);
194 if (res < 0)
196 ch = *str;
197 width = 0;
199 else
201 ch = res;
202 /* Calculate UTF-8 char width */
203 next_ch = g_utf8_next_char (str);
204 if (next_ch)
206 width = next_ch - str;
208 else
210 ch = 0;
211 width = 0;
214 *char_width = width;
215 return ch;
218 /* --------------------------------------------------------------------------------------------- */
220 gboolean
221 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
223 assert (view->datasource == DS_STRING);
224 if (byte_index < (off_t) view->ds_string_len)
226 if (retval)
227 *retval = view->ds_string_data[byte_index];
228 return TRUE;
230 if (retval)
231 *retval = -1;
232 return FALSE;
235 /* --------------------------------------------------------------------------------------------- */
237 gboolean
238 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
240 assert (view->datasource == DS_NONE);
241 (void) &view;
242 (void) byte_index;
243 if (retval)
244 *retval = -1;
245 return FALSE;
248 /* --------------------------------------------------------------------------------------------- */
250 void
251 mcview_set_byte (mcview_t * view, off_t offset, byte b)
253 (void) &b;
254 assert (offset < mcview_get_filesize (view));
255 assert (view->datasource == DS_FILE);
256 view->ds_file_datalen = 0; /* just force reloading */
259 /* --------------------------------------------------------------------------------------------- */
261 /*static */
262 void
263 mcview_file_load_data (mcview_t * view, off_t byte_index)
265 off_t blockoffset;
266 ssize_t res;
267 size_t bytes_read;
269 assert (view->datasource == DS_FILE);
271 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
272 return;
274 if (byte_index >= view->ds_file_filesize)
275 return;
277 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
278 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
279 goto error;
281 bytes_read = 0;
282 while (bytes_read < view->ds_file_datasize)
284 res =
285 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
286 view->ds_file_datasize - bytes_read);
287 if (res == -1)
288 goto error;
289 if (res == 0)
290 break;
291 bytes_read += (size_t) res;
293 view->ds_file_offset = blockoffset;
294 if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
296 /* the file has grown in the meantime -- stick to the old size */
297 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
299 else
301 view->ds_file_datalen = bytes_read;
303 return;
305 error:
306 view->ds_file_datalen = 0;
309 /* --------------------------------------------------------------------------------------------- */
311 void
312 mcview_close_datasource (mcview_t * view)
314 switch (view->datasource)
316 case DS_NONE:
317 break;
318 case DS_STDIO_PIPE:
319 if (view->ds_stdio_pipe != NULL)
321 (void) pclose (view->ds_stdio_pipe);
322 mcview_display (view);
323 close_error_pipe (D_NORMAL, NULL);
324 view->ds_stdio_pipe = NULL;
326 mcview_growbuf_free (view);
327 break;
328 case DS_VFS_PIPE:
329 if (view->ds_vfs_pipe != -1)
331 (void) mc_close (view->ds_vfs_pipe);
332 view->ds_vfs_pipe = -1;
334 mcview_growbuf_free (view);
335 break;
336 case DS_FILE:
337 (void) mc_close (view->ds_file_fd);
338 view->ds_file_fd = -1;
339 g_free (view->ds_file_data);
340 view->ds_file_data = NULL;
341 break;
342 case DS_STRING:
343 g_free (view->ds_string_data);
344 view->ds_string_data = NULL;
345 break;
346 default:
347 assert (!"Unknown datasource type");
349 view->datasource = DS_NONE;
352 /* --------------------------------------------------------------------------------------------- */
354 void
355 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
357 view->datasource = DS_FILE;
358 view->ds_file_fd = fd;
359 view->ds_file_filesize = st->st_size;
360 view->ds_file_offset = 0;
361 view->ds_file_data = g_malloc (4096);
362 view->ds_file_datalen = 0;
363 view->ds_file_datasize = 4096;
366 /* --------------------------------------------------------------------------------------------- */
368 gboolean
369 mcview_load_command_output (mcview_t * view, const char *command)
371 FILE *fp;
373 mcview_close_datasource (view);
375 open_error_pipe ();
376 if ((fp = popen (command, "r")) == NULL)
378 /* Avoid two messages. Message from stderr has priority. */
379 mcview_display (view);
380 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
381 mcview_show_error (view, _(" Cannot spawn child process "));
382 return FALSE;
385 /* First, check if filter produced any output */
386 mcview_set_datasource_stdio_pipe (view, fp);
387 if (!mcview_get_byte (view, 0, NULL))
389 mcview_close_datasource (view);
391 /* Avoid two messages. Message from stderr has priority. */
392 mcview_display (view);
393 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
394 mcview_show_error (view, _("Empty output from child filter"));
395 return FALSE;
397 else
400 * At least something was read correctly. Close stderr and let
401 * program die if it will try to write something there.
403 * Ideally stderr should be read asynchronously to prevent programs
404 * from blocking (poll/select multiplexor).
406 close_error_pipe (D_NORMAL, NULL);
408 return TRUE;
411 /* --------------------------------------------------------------------------------------------- */
413 void
414 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
416 assert (fd != -1);
417 view->datasource = DS_VFS_PIPE;
418 view->ds_vfs_pipe = fd;
420 mcview_growbuf_init (view);
423 /* --------------------------------------------------------------------------------------------- */
425 void
426 mcview_set_datasource_string (mcview_t * view, const char *s)
428 view->datasource = DS_STRING;
429 view->ds_string_data = (byte *) g_strdup (s);
430 view->ds_string_len = strlen (s);
433 /* --------------------------------------------------------------------------------------------- */