x86_64: Cleanup of code for master
[midnight-commander.git] / src / viewer / datasource.c
blobc23491323787034a82044871e520cd69d0a16b35
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) {
103 case DS_NONE:
104 return 0;
105 case DS_STDIO_PIPE:
106 case DS_VFS_PIPE:
107 return mcview_growbuf_filesize (view);
108 case DS_FILE:
109 return view->ds_file_filesize;
110 case DS_STRING:
111 return view->ds_string_len;
112 default:
113 assert (!"Unknown datasource type");
114 return 0;
118 /* --------------------------------------------------------------------------------------------- */
120 void
121 mcview_update_filesize (mcview_t * view)
123 if (view->datasource == DS_FILE) {
124 struct stat st;
125 if (mc_fstat (view->ds_file_fd, &st) != -1)
126 view->ds_file_filesize = st.st_size;
130 /* --------------------------------------------------------------------------------------------- */
132 char *
133 mcview_get_ptr_file (mcview_t * view, off_t byte_index)
135 assert (view->datasource == DS_FILE);
137 mcview_file_load_data (view, byte_index);
138 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
139 return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
140 return NULL;
143 /* --------------------------------------------------------------------------------------------- */
145 char *
146 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
148 assert (view->datasource == DS_STRING);
149 if (byte_index < (off_t) view->ds_string_len)
150 return (char *) (view->ds_string_data + byte_index);
151 return NULL;
154 /* --------------------------------------------------------------------------------------------- */
157 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
159 gchar *str = NULL;
160 int res = -1;
161 gunichar ch;
162 gchar *next_ch = NULL;
163 int width = 0;
165 *result = TRUE;
167 switch (view->datasource) {
168 case DS_STDIO_PIPE:
169 case DS_VFS_PIPE:
170 str = mcview_get_ptr_growing_buffer (view, byte_index);
171 break;
172 case DS_FILE:
173 str = mcview_get_ptr_file (view, byte_index);
174 break;
175 case DS_STRING:
176 str = mcview_get_ptr_string (view, byte_index);
177 break;
178 case DS_NONE:
179 break;
182 if (str == NULL) {
183 *result = FALSE;
184 width = 0;
185 return 0;
188 res = g_utf8_get_char_validated (str, -1);
190 if (res < 0) {
191 ch = *str;
192 width = 0;
193 } else {
194 ch = res;
195 /* Calculate UTF-8 char width */
196 next_ch = g_utf8_next_char (str);
197 if (next_ch) {
198 width = next_ch - str;
199 } else {
200 ch = 0;
201 width = 0;
204 *char_width = width;
205 return ch;
208 /* --------------------------------------------------------------------------------------------- */
210 gboolean
211 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
213 assert (view->datasource == DS_STRING);
214 if (byte_index < (off_t) view->ds_string_len) {
215 if (retval)
216 *retval = view->ds_string_data[byte_index];
217 return TRUE;
219 if (retval)
220 *retval = -1;
221 return FALSE;
224 /* --------------------------------------------------------------------------------------------- */
226 gboolean
227 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
229 assert (view->datasource == DS_NONE);
230 (void) &view;
231 (void) byte_index;
232 if (retval)
233 *retval = -1;
234 return FALSE;
237 /* --------------------------------------------------------------------------------------------- */
239 void
240 mcview_set_byte (mcview_t * view, off_t offset, byte b)
242 (void) &b;
243 assert (offset < mcview_get_filesize (view));
244 assert (view->datasource == DS_FILE);
245 view->ds_file_datalen = 0; /* just force reloading */
248 /* --------------------------------------------------------------------------------------------- */
250 /*static*/
251 void
252 mcview_file_load_data (mcview_t * view, off_t byte_index)
254 off_t blockoffset;
255 ssize_t res;
256 size_t bytes_read;
258 assert (view->datasource == DS_FILE);
260 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
261 return;
263 if (byte_index >= view->ds_file_filesize)
264 return;
266 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
267 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
268 goto error;
270 bytes_read = 0;
271 while (bytes_read < view->ds_file_datasize) {
272 res =
273 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
274 view->ds_file_datasize - bytes_read);
275 if (res == -1)
276 goto error;
277 if (res == 0)
278 break;
279 bytes_read += (size_t) res;
281 view->ds_file_offset = blockoffset;
282 if ((off_t) bytes_read > view->ds_file_filesize - view->ds_file_offset) {
283 /* the file has grown in the meantime -- stick to the old size */
284 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
285 } else {
286 view->ds_file_datalen = bytes_read;
288 return;
290 error:
291 view->ds_file_datalen = 0;
294 /* --------------------------------------------------------------------------------------------- */
296 void
297 mcview_close_datasource (mcview_t * view)
299 switch (view->datasource) {
300 case DS_NONE:
301 break;
302 case DS_STDIO_PIPE:
303 if (view->ds_stdio_pipe != NULL) {
304 (void) pclose (view->ds_stdio_pipe);
305 mcview_display (view);
306 close_error_pipe (D_NORMAL, NULL);
307 view->ds_stdio_pipe = NULL;
309 mcview_growbuf_free (view);
310 break;
311 case DS_VFS_PIPE:
312 if (view->ds_vfs_pipe != -1) {
313 (void) mc_close (view->ds_vfs_pipe);
314 view->ds_vfs_pipe = -1;
316 mcview_growbuf_free (view);
317 break;
318 case DS_FILE:
319 (void) mc_close (view->ds_file_fd);
320 view->ds_file_fd = -1;
321 g_free (view->ds_file_data);
322 view->ds_file_data = NULL;
323 break;
324 case DS_STRING:
325 g_free (view->ds_string_data);
326 view->ds_string_data = NULL;
327 break;
328 default:
329 assert (!"Unknown datasource type");
331 view->datasource = DS_NONE;
334 /* --------------------------------------------------------------------------------------------- */
336 void
337 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
339 view->datasource = DS_FILE;
340 view->ds_file_fd = fd;
341 view->ds_file_filesize = st->st_size;
342 view->ds_file_offset = 0;
343 view->ds_file_data = g_malloc (4096);
344 view->ds_file_datalen = 0;
345 view->ds_file_datasize = 4096;
348 /* --------------------------------------------------------------------------------------------- */
350 gboolean
351 mcview_load_command_output (mcview_t * view, const char *command)
353 FILE *fp;
355 mcview_close_datasource (view);
357 open_error_pipe ();
358 if ((fp = popen (command, "r")) == NULL) {
359 /* Avoid two messages. Message from stderr has priority. */
360 mcview_display (view);
361 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
362 mcview_show_error (view, _(" Cannot spawn child process "));
363 return FALSE;
366 /* First, check if filter produced any output */
367 mcview_set_datasource_stdio_pipe (view, fp);
368 if (! mcview_get_byte (view, 0, NULL)) {
369 mcview_close_datasource (view);
371 /* Avoid two messages. Message from stderr has priority. */
372 mcview_display (view);
373 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
374 mcview_show_error (view, _("Empty output from child filter"));
375 return FALSE;
376 } else {
378 * At least something was read correctly. Close stderr and let
379 * program die if it will try to write something there.
381 * Ideally stderr should be read asynchronously to prevent programs
382 * from blocking (poll/select multiplexor).
384 close_error_pipe (D_NORMAL, NULL);
386 return TRUE;
389 /* --------------------------------------------------------------------------------------------- */
391 void
392 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
394 assert (fd != -1);
395 view->datasource = DS_VFS_PIPE;
396 view->ds_vfs_pipe = fd;
398 mcview_growbuf_init (view);
401 /* --------------------------------------------------------------------------------------------- */
403 void
404 mcview_set_datasource_string (mcview_t * view, const char *s)
406 view->datasource = DS_STRING;
407 view->ds_string_data = (byte *) g_strdup (s);
408 view->ds_string_len = strlen (s);
411 /* --------------------------------------------------------------------------------------------- */