Split src/view.c into small files for better support of code
[midnight-commander.git] / src / viewer / datasource.c
blob552bdf8f419d917cec06015897beb3c5d4c97bf0
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 "../src/viewer/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)
145 gchar *str = NULL;
146 int res = -1;
147 gunichar ch;
148 gchar *next_ch = NULL;
149 int width = 0;
151 switch (view->datasource) {
152 case DS_STDIO_PIPE:
153 case DS_VFS_PIPE:
154 str = mcview_get_ptr_growing_buffer (view, byte_index);
155 break;
156 case DS_FILE:
157 str = mcview_get_ptr_file (view, byte_index);
158 break;
159 case DS_STRING:
160 str = mcview_get_ptr_string (view, byte_index);
161 break;
162 case DS_NONE:
163 break;
166 if (str == NULL) {
167 width = 0;
168 return -1;
171 res = g_utf8_get_char_validated (str, -1);
173 if (res < 0) {
174 ch = *str;
175 width = 0;
176 } else {
177 ch = res;
178 /* Calculate UTF-8 char width */
179 next_ch = g_utf8_next_char (str);
180 if (next_ch) {
181 if (next_ch != str) {
182 width = next_ch - str;
183 } else {
184 width = 0;
186 } else {
187 ch = 0;
188 width = 0;
191 *char_width = width;
192 return ch;
195 /* --------------------------------------------------------------------------------------------- */
198 mcview_get_byte_string (mcview_t * view, off_t byte_index)
200 assert (view->datasource == DS_STRING);
201 if (byte_index < view->ds_string_len)
202 return view->ds_string_data[byte_index];
203 return -1;
206 /* --------------------------------------------------------------------------------------------- */
209 mcview_get_byte_none (mcview_t * view, off_t byte_index)
211 assert (view->datasource == DS_NONE);
212 (void) &view;
213 (void) byte_index;
214 return -1;
217 /* --------------------------------------------------------------------------------------------- */
219 void
220 mcview_set_byte (mcview_t * view, off_t offset, byte b)
222 (void) &b;
223 assert (offset < mcview_get_filesize (view));
224 assert (view->datasource == DS_FILE);
225 view->ds_file_datalen = 0; /* just force reloading */
228 /* --------------------------------------------------------------------------------------------- */
230 /*static inline*/
231 void
232 mcview_file_load_data (mcview_t * view, off_t byte_index)
234 off_t blockoffset;
235 ssize_t res;
236 size_t bytes_read;
238 assert (view->datasource == DS_FILE);
240 if (mcview_already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
241 return;
243 if (byte_index >= view->ds_file_filesize)
244 return;
246 blockoffset = mcview_offset_rounddown (byte_index, view->ds_file_datasize);
247 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
248 goto error;
250 bytes_read = 0;
251 while (bytes_read < view->ds_file_datasize) {
252 res =
253 mc_read (view->ds_file_fd, view->ds_file_data + bytes_read,
254 view->ds_file_datasize - bytes_read);
255 if (res == -1)
256 goto error;
257 if (res == 0)
258 break;
259 bytes_read += (size_t) res;
261 view->ds_file_offset = blockoffset;
262 if (bytes_read > view->ds_file_filesize - view->ds_file_offset) {
263 /* the file has grown in the meantime -- stick to the old size */
264 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
265 } else {
266 view->ds_file_datalen = bytes_read;
268 return;
270 error:
271 view->ds_file_datalen = 0;
274 /* --------------------------------------------------------------------------------------------- */
276 void
277 mcview_close_datasource (mcview_t * view)
279 switch (view->datasource) {
280 case DS_NONE:
281 break;
282 case DS_STDIO_PIPE:
283 if (view->ds_stdio_pipe != NULL) {
284 (void) pclose (view->ds_stdio_pipe);
285 mcview_display (view);
286 close_error_pipe (D_NORMAL, NULL);
287 view->ds_stdio_pipe = NULL;
289 mcview_growbuf_free (view);
290 break;
291 case DS_VFS_PIPE:
292 if (view->ds_vfs_pipe != -1) {
293 (void) mc_close (view->ds_vfs_pipe);
294 view->ds_vfs_pipe = -1;
296 mcview_growbuf_free (view);
297 break;
298 case DS_FILE:
299 (void) mc_close (view->ds_file_fd);
300 view->ds_file_fd = -1;
301 g_free (view->ds_file_data);
302 view->ds_file_data = NULL;
303 break;
304 case DS_STRING:
305 g_free (view->ds_string_data);
306 view->ds_string_data = NULL;
307 break;
308 default:
309 assert (!"Unknown datasource type");
311 view->datasource = DS_NONE;
314 /* --------------------------------------------------------------------------------------------- */
316 void
317 mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st)
319 view->datasource = DS_FILE;
320 view->ds_file_fd = fd;
321 view->ds_file_filesize = st->st_size;
322 view->ds_file_offset = 0;
323 view->ds_file_data = g_malloc (4096);
324 view->ds_file_datalen = 0;
325 view->ds_file_datasize = 4096;
328 /* --------------------------------------------------------------------------------------------- */
330 gboolean
331 mcview_load_command_output (mcview_t * view, const char *command)
333 FILE *fp;
335 mcview_close_datasource (view);
337 open_error_pipe ();
338 if ((fp = popen (command, "r")) == NULL) {
339 /* Avoid two messages. Message from stderr has priority. */
340 mcview_display (view);
341 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
342 mcview_show_error (view, _(" Cannot spawn child process "));
343 return FALSE;
346 /* First, check if filter produced any output */
347 mcview_set_datasource_stdio_pipe (view, fp);
348 if (mcview_get_byte (view, 0) == -1) {
349 mcview_close_datasource (view);
351 /* Avoid two messages. Message from stderr has priority. */
352 mcview_display (view);
353 if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL))
354 mcview_show_error (view, _("Empty output from child filter"));
355 return FALSE;
356 } else {
358 * At least something was read correctly. Close stderr and let
359 * program die if it will try to write something there.
361 * Ideally stderr should be read asynchronously to prevent programs
362 * from blocking (poll/select multiplexor).
364 close_error_pipe (D_NORMAL, NULL);
366 return TRUE;
369 /* --------------------------------------------------------------------------------------------- */
371 void
372 mcview_set_datasource_vfs_pipe (mcview_t * view, int fd)
374 assert (fd != -1);
375 view->datasource = DS_VFS_PIPE;
376 view->ds_vfs_pipe = fd;
378 mcview_growbuf_init (view);
381 /* --------------------------------------------------------------------------------------------- */
383 void
384 mcview_set_datasource_string (mcview_t * view, const char *s)
386 view->datasource = DS_STRING;
387 view->ds_string_data = (byte *) g_strdup (s);
388 view->ds_string_len = strlen (s);
391 /* --------------------------------------------------------------------------------------------- */