Check assert.h header and use it conditionally.
[midnight-commander.git] / src / viewer / datasource.c
blob5cd31e7ea2162d8d1af8d78efdadb0c575e43bc6
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
145 mcview_file_load_data (view, byte_index);
146 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
147 return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
148 return NULL;
151 /* --------------------------------------------------------------------------------------------- */
153 char *
154 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
156 #ifdef HAVE_ASSERT_H
157 assert (view->datasource == DS_STRING);
158 #endif
159 if (byte_index < (off_t) view->ds_string_len)
160 return (char *) (view->ds_string_data + byte_index);
161 return NULL;
164 /* --------------------------------------------------------------------------------------------- */
167 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
169 gchar *str = NULL;
170 int res = -1;
171 gunichar ch;
172 gchar *next_ch = NULL;
173 gchar utf8buf[UTF8_CHAR_LEN + 1];
175 *char_width = 0;
176 *result = FALSE;
178 switch (view->datasource)
180 case DS_STDIO_PIPE:
181 case DS_VFS_PIPE:
182 str = mcview_get_ptr_growing_buffer (view, byte_index);
183 break;
184 case DS_FILE:
185 str = mcview_get_ptr_file (view, byte_index);
186 break;
187 case DS_STRING:
188 str = mcview_get_ptr_string (view, byte_index);
189 break;
190 case DS_NONE:
191 break;
194 if (str == NULL)
195 return 0;
197 res = g_utf8_get_char_validated (str, -1);
199 if (res < 0)
201 /* Retry with explicit bytes to make sure it's not a buffer boundary */
202 int i;
203 for (i = 0; i < UTF8_CHAR_LEN; i++)
205 if (mcview_get_byte (view, byte_index + i, &res))
206 utf8buf[i] = res;
207 else
209 utf8buf[i] = '\0';
210 break;
213 utf8buf[UTF8_CHAR_LEN] = '\0';
214 str = utf8buf;
215 res = g_utf8_get_char_validated (str, -1);
218 if (res < 0)
220 ch = *str;
221 *char_width = 1;
223 else
225 ch = res;
226 /* Calculate UTF-8 char width */
227 next_ch = g_utf8_next_char (str);
228 if (next_ch)
229 *char_width = next_ch - str;
230 else
231 return 0;
233 *result = TRUE;
234 return ch;
237 /* --------------------------------------------------------------------------------------------- */
239 gboolean
240 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
242 #ifdef HAVE_ASSERT_H
243 assert (view->datasource == DS_STRING);
244 #endif
245 if (byte_index < (off_t) view->ds_string_len)
247 if (retval)
248 *retval = view->ds_string_data[byte_index];
249 return TRUE;
251 if (retval)
252 *retval = -1;
253 return FALSE;
256 /* --------------------------------------------------------------------------------------------- */
258 gboolean
259 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
261 (void) &view;
262 (void) byte_index;
264 #ifdef HAVE_ASSERT_H
265 assert (view->datasource == DS_NONE);
266 #endif
268 if (retval != NULL)
269 *retval = -1;
270 return FALSE;
273 /* --------------------------------------------------------------------------------------------- */
275 void
276 mcview_set_byte (mcview_t * view, off_t offset, byte b)
278 (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);
285 #endif
286 view->ds_file_datalen = 0; /* just force reloading */
289 /* --------------------------------------------------------------------------------------------- */
291 /*static */
292 void
293 mcview_file_load_data (mcview_t * view, off_t byte_index)
295 off_t blockoffset;
296 ssize_t res;
297 size_t bytes_read;
299 #ifdef HAVE_ASSERT_H
300 assert (view->datasource == DS_FILE);
301 #endif
303 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
304 return;
306 if (byte_index >= view->ds_file_filesize)
307 return;
309 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
310 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
311 goto error;
313 bytes_read = 0;
314 while (bytes_read < view->ds_file_datasize)
316 res =
317 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
318 view->ds_file_datasize - bytes_read);
319 if (res == -1)
320 goto error;
321 if (res == 0)
322 break;
323 bytes_read += (size_t) res;
325 view->ds_file_offset = blockoffset;
326 if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset)
328 /* the file has grown in the meantime -- stick to the old size */
329 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
331 else
333 view->ds_file_datalen = bytes_read;
335 return;
337 error:
338 view->ds_file_datalen = 0;
341 /* --------------------------------------------------------------------------------------------- */
343 void
344 mcview_close_datasource (mcview_t * view)
346 switch (view->datasource)
348 case DS_NONE:
349 break;
350 case DS_STDIO_PIPE:
351 if (view->ds_stdio_pipe != NULL)
353 (void) pclose (view->ds_stdio_pipe);
354 mcview_display (view);
355 close_error_pipe (D_NORMAL, NULL);
356 view->ds_stdio_pipe = NULL;
358 mcview_growbuf_free (view);
359 break;
360 case DS_VFS_PIPE:
361 if (view->ds_vfs_pipe != -1)
363 (void) mc_close (view->ds_vfs_pipe);
364 view->ds_vfs_pipe = -1;
366 mcview_growbuf_free (view);
367 break;
368 case DS_FILE:
369 (void) mc_close (view->ds_file_fd);
370 view->ds_file_fd = -1;
371 g_free (view->ds_file_data);
372 view->ds_file_data = NULL;
373 break;
374 case DS_STRING:
375 g_free (view->ds_string_data);
376 view->ds_string_data = NULL;
377 break;
378 default:
379 #ifdef HAVE_ASSERT_H
380 assert (!"Unknown datasource type")
381 #endif
384 view->datasource = DS_NONE;
387 /* --------------------------------------------------------------------------------------------- */
389 void
390 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
392 view->datasource = DS_FILE;
393 view->ds_file_fd = fd;
394 view->ds_file_filesize = st->st_size;
395 view->ds_file_offset = 0;
396 view->ds_file_data = g_malloc (4096);
397 view->ds_file_datalen = 0;
398 view->ds_file_datasize = 4096;
401 /* --------------------------------------------------------------------------------------------- */
403 gboolean
404 mcview_load_command_output (mcview_t * view, const char *command)
406 FILE *fp;
408 mcview_close_datasource (view);
410 open_error_pipe ();
411 fp = popen (command, "r");
412 if (fp == NULL)
414 /* Avoid two messages. Message from stderr has priority. */
415 mcview_display (view);
416 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
417 mcview_show_error (view, _("Cannot spawn child process"));
418 return FALSE;
421 /* First, check if filter produced any output */
422 mcview_set_datasource_stdio_pipe (view, fp);
423 if (!mcview_get_byte (view, 0, NULL))
425 mcview_close_datasource (view);
427 /* Avoid two messages. Message from stderr has priority. */
428 mcview_display (view);
429 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
430 mcview_show_error (view, _("Empty output from child filter"));
431 return FALSE;
433 else
436 * At least something was read correctly. Close stderr and let
437 * program die if it will try to write something there.
439 * Ideally stderr should be read asynchronously to prevent programs
440 * from blocking (poll/select multiplexor).
442 close_error_pipe (D_NORMAL, NULL);
444 return TRUE;
447 /* --------------------------------------------------------------------------------------------- */
449 void
450 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
452 #ifdef HAVE_ASSERT_H
453 assert (fd != -1);
454 #endif
455 view->datasource = DS_VFS_PIPE;
456 view->ds_vfs_pipe = fd;
458 mcview_growbuf_init (view);
461 /* --------------------------------------------------------------------------------------------- */
463 void
464 mcview_set_datasource_string (mcview_t * view, const char *s)
466 view->datasource = DS_STRING;
467 view->ds_string_data = (byte *) g_strdup (s);
468 view->ds_string_len = strlen (s);
471 /* --------------------------------------------------------------------------------------------- */