De-inline a few functions which are large
[midnight-commander.git] / src / viewer / datasource.c
blob15abc9389e816589ad836787b821042f0c8b7c62
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 "../src/global.h"
60 #include "../src/wtools.h"
61 #include "internal.h"
63 /*** global variables ****************************************************************************/
65 /*** file scope macro definitions ****************************************************************/
67 /*** file scope type declarations ****************************************************************/
69 /*** file scope variables ************************************************************************/
71 /*** file scope functions ************************************************************************/
73 /*** public functions ****************************************************************************/
75 /* --------------------------------------------------------------------------------------------- */
77 static void
78 mcview_set_datasource_stdio_pipe (mcview_t * view, FILE * fp)
80 assert (fp != NULL);
81 view->datasource = DS_STDIO_PIPE;
82 view->ds_stdio_pipe = fp;
84 mcview_growbuf_init (view);
87 /* --------------------------------------------------------------------------------------------- */
89 void
90 mcview_set_datasource_none (mcview_t * view)
92 view->datasource = DS_NONE;
95 /* --------------------------------------------------------------------------------------------- */
97 off_t
98 mcview_get_filesize (mcview_t * view)
100 switch (view->datasource) {
101 case DS_NONE:
102 return 0;
103 case DS_STDIO_PIPE:
104 case DS_VFS_PIPE:
105 return mcview_growbuf_filesize (view);
106 case DS_FILE:
107 return view->ds_file_filesize;
108 case DS_STRING:
109 return view->ds_string_len;
110 default:
111 assert (!"Unknown datasource type");
112 return 0;
116 /* --------------------------------------------------------------------------------------------- */
118 char *
119 mcview_get_ptr_file (mcview_t * view, off_t byte_index)
121 assert (view->datasource == DS_FILE);
123 mcview_file_load_data (view, byte_index);
124 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
125 return (char *) (view->ds_file_data + (byte_index - view->ds_file_offset));
126 return NULL;
129 /* --------------------------------------------------------------------------------------------- */
131 char *
132 mcview_get_ptr_string (mcview_t * view, off_t byte_index)
134 assert (view->datasource == DS_STRING);
135 if (byte_index < view->ds_string_len)
136 return (char *) (view->ds_string_data + byte_index);
137 return NULL;
140 /* --------------------------------------------------------------------------------------------- */
143 mcview_get_utf (mcview_t * view, off_t byte_index, int *char_width, gboolean * result)
145 gchar *str = NULL;
146 int res = -1;
147 gunichar ch;
148 gchar *next_ch = NULL;
149 int width = 0;
151 *result = TRUE;
153 switch (view->datasource) {
154 case DS_STDIO_PIPE:
155 case DS_VFS_PIPE:
156 str = mcview_get_ptr_growing_buffer (view, byte_index);
157 break;
158 case DS_FILE:
159 str = mcview_get_ptr_file (view, byte_index);
160 break;
161 case DS_STRING:
162 str = mcview_get_ptr_string (view, byte_index);
163 break;
164 case DS_NONE:
165 break;
168 if (str == NULL) {
169 *result = FALSE;
170 width = 0;
171 return 0;
174 res = g_utf8_get_char_validated (str, -1);
176 if (res < 0) {
177 ch = *str;
178 width = 0;
179 } else {
180 ch = res;
181 /* Calculate UTF-8 char width */
182 next_ch = g_utf8_next_char (str);
183 if (next_ch) {
184 width = next_ch - str;
185 } else {
186 ch = 0;
187 width = 0;
190 *char_width = width;
191 return ch;
194 /* --------------------------------------------------------------------------------------------- */
196 gboolean
197 mcview_get_byte_string (mcview_t * view, off_t byte_index, int *retval)
199 assert (view->datasource == DS_STRING);
200 if (byte_index < view->ds_string_len) {
201 if (retval)
202 *retval = view->ds_string_data[byte_index];
203 return TRUE;
205 if (retval)
206 *retval = -1;
207 return FALSE;
210 /* --------------------------------------------------------------------------------------------- */
212 gboolean
213 mcview_get_byte_none (mcview_t * view, off_t byte_index, int *retval)
215 assert (view->datasource == DS_NONE);
216 (void) &view;
217 (void) byte_index;
218 if (retval)
219 *retval = -1;
220 return FALSE;
223 /* --------------------------------------------------------------------------------------------- */
225 void
226 mcview_set_byte (mcview_t * view, off_t offset, byte b)
228 (void) &b;
229 assert (offset < mcview_get_filesize (view));
230 assert (view->datasource == DS_FILE);
231 view->ds_file_datalen = 0; /* just force reloading */
234 /* --------------------------------------------------------------------------------------------- */
236 /*static*/
237 void
238 mcview_file_load_data (mcview_t * view, off_t byte_index)
240 off_t blockoffset;
241 ssize_t res;
242 size_t bytes_read;
244 assert (view->datasource == DS_FILE);
246 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
247 return;
249 if (byte_index >= view->ds_file_filesize)
250 return;
252 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
253 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
254 goto error;
256 bytes_read = 0;
257 while (bytes_read < view->ds_file_datasize) {
258 res =
259 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
260 view->ds_file_datasize - bytes_read);
261 if (res == -1)
262 goto error;
263 if (res == 0)
264 break;
265 bytes_read += (size_t) res;
267 view->ds_file_offset = blockoffset;
268 if (bytes_read > view->ds_file_filesize - view->ds_file_offset) {
269 /* the file has grown in the meantime -- stick to the old size */
270 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
271 } else {
272 view->ds_file_datalen = bytes_read;
274 return;
276 error:
277 view->ds_file_datalen = 0;
280 /* --------------------------------------------------------------------------------------------- */
282 void
283 mcview_close_datasource (mcview_t * view)
285 switch (view->datasource) {
286 case DS_NONE:
287 break;
288 case DS_STDIO_PIPE:
289 if (view->ds_stdio_pipe != NULL) {
290 (void) pclose (view->ds_stdio_pipe);
291 mcview_display (view);
292 close_error_pipe (D_NORMAL, NULL);
293 view->ds_stdio_pipe = NULL;
295 mcview_growbuf_free (view);
296 break;
297 case DS_VFS_PIPE:
298 if (view->ds_vfs_pipe != -1) {
299 (void) mc_close (view->ds_vfs_pipe);
300 view->ds_vfs_pipe = -1;
302 mcview_growbuf_free (view);
303 break;
304 case DS_FILE:
305 (void) mc_close (view->ds_file_fd);
306 view->ds_file_fd = -1;
307 g_free (view->ds_file_data);
308 view->ds_file_data = NULL;
309 break;
310 case DS_STRING:
311 g_free (view->ds_string_data);
312 view->ds_string_data = NULL;
313 break;
314 default:
315 assert (!"Unknown datasource type");
317 view->datasource = DS_NONE;
320 /* --------------------------------------------------------------------------------------------- */
322 void
323 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
325 view->datasource = DS_FILE;
326 view->ds_file_fd = fd;
327 view->ds_file_filesize = st->st_size;
328 view->ds_file_offset = 0;
329 view->ds_file_data = g_malloc (4096);
330 view->ds_file_datalen = 0;
331 view->ds_file_datasize = 4096;
334 /* --------------------------------------------------------------------------------------------- */
336 gboolean
337 mcview_load_command_output (mcview_t * view, const char *command)
339 FILE *fp;
341 mcview_close_datasource (view);
343 open_error_pipe ();
344 if ((fp = popen (command, "r")) == NULL) {
345 /* Avoid two messages. Message from stderr has priority. */
346 mcview_display (view);
347 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
348 mcview_show_error (view, _(" Cannot spawn child process "));
349 return FALSE;
352 /* First, check if filter produced any output */
353 mcview_set_datasource_stdio_pipe (view, fp);
354 if (! mcview_get_byte (view, 0, NULL)) {
355 mcview_close_datasource (view);
357 /* Avoid two messages. Message from stderr has priority. */
358 mcview_display (view);
359 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
360 mcview_show_error (view, _("Empty output from child filter"));
361 return FALSE;
362 } else {
364 * At least something was read correctly. Close stderr and let
365 * program die if it will try to write something there.
367 * Ideally stderr should be read asynchronously to prevent programs
368 * from blocking (poll/select multiplexor).
370 close_error_pipe (D_NORMAL, NULL);
372 return TRUE;
375 /* --------------------------------------------------------------------------------------------- */
377 void
378 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
380 assert (fd != -1);
381 view->datasource = DS_VFS_PIPE;
382 view->ds_vfs_pipe = fd;
384 mcview_growbuf_init (view);
387 /* --------------------------------------------------------------------------------------------- */
389 void
390 mcview_set_datasource_string (mcview_t * view, const char *s)
392 view->datasource = DS_STRING;
393 view->ds_string_data = (byte *) g_strdup (s);
394 view->ds_string_len = strlen (s);
397 /* --------------------------------------------------------------------------------------------- */