added vfs_path_element_valid() function
[midnight-commander.git] / lib / vfs / vfs.c
blob1a02c7553439177fbb7d4fef374dc48dc7f0fdc4
1 /* Virtual File System switch code
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2007 Free Software Foundation, Inc.
5 Written by: 1995 Miguel de Icaza
6 1995 Jakub Jelinek
7 1998 Pavel Machek
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /**
24 * \file
25 * \brief Source: Virtual File System switch code
26 * \author Miguel de Icaza
27 * \author Jakub Jelinek
28 * \author Pavel Machek
29 * \date 1995, 1998
30 * \warning funtions like extfs_lstat() have right to destroy any
31 * strings you pass to them. This is acutally ok as you g_strdup what
32 * you are passing to them, anyway; still, beware.
34 * Namespace: exports *many* functions with vfs_ prefix; exports
35 * parse_ls_lga and friends which do not have that prefix.
38 #include <config.h>
40 #include <errno.h>
42 #include "lib/global.h"
43 #include "lib/strutil.h"
44 #include "lib/util.h"
45 #include "lib/widget.h" /* message() */
46 #include "lib/event.h"
48 #ifdef HAVE_CHARSET
49 #include "lib/charsets.h"
50 #endif
52 #include "vfs.h"
53 #include "utilvfs.h"
54 #include "gc.h"
56 extern struct dirent *mc_readdir_result;
57 /*** global variables ****************************************************************************/
59 GPtrArray *vfs__classes_list = NULL;
61 GString *vfs_str_buffer = NULL;
62 struct vfs_class *current_vfs = NULL;
65 /*** file scope macro definitions ****************************************************************/
67 #if defined(_AIX) && !defined(NAME_MAX)
68 #define NAME_MAX FILENAME_MAX
69 #endif
71 #define VFS_FIRST_HANDLE 100
73 #define ISSLASH(a) (!a || (a == '/'))
75 /*** file scope type declarations ****************************************************************/
77 struct vfs_openfile
79 int handle;
80 struct vfs_class *vclass;
81 void *fsinfo;
84 /*** file scope variables ************************************************************************/
86 /** They keep track of the current directory */
87 static vfs_path_t *current_path = NULL;
89 static GPtrArray *vfs_openfiles;
90 static long vfs_free_handle_list = -1;
92 /*** file scope functions ************************************************************************/
93 /* --------------------------------------------------------------------------------------------- */
94 /* now used only by vfs_translate_path, but could be used in other vfs
95 * plugin to automatic detect encoding
96 * path - path to translate
97 * size - how many bytes from path translate
98 * defcnv - convertor, that is used as default, when path does not contain any
99 * #enc: subtring
100 * buffer - used to store result of translation
103 static estr_t
104 _vfs_translate_path (const char *path, int size, GIConv defcnv, GString * buffer)
106 const char *semi;
107 const char *slash;
108 estr_t state = ESTR_SUCCESS;
110 if (size == 0)
111 return ESTR_SUCCESS;
113 size = (size > 0) ? size : (signed int) strlen (path);
115 /* try found /#enc: */
116 semi = g_strrstr_len (path, size, VFS_ENCODING_PREFIX);
117 if (semi != NULL && (semi == path || *(semi - 1) == PATH_SEP))
119 char encoding[16];
120 GIConv coder = INVALID_CONV;
121 int ms;
123 /* first must be translated part before #enc: */
124 ms = semi - path;
126 state = _vfs_translate_path (path, ms, defcnv, buffer);
128 if (state != ESTR_SUCCESS)
129 return state;
131 /* now can be translated part after #enc: */
132 semi += strlen (VFS_ENCODING_PREFIX); /* skip "#enc:" */
133 slash = strchr (semi, PATH_SEP);
134 /* ignore slashes after size; */
135 if (slash - path >= size)
136 slash = NULL;
138 ms = (slash != NULL) ? slash - semi : (int) strlen (semi);
139 ms = min ((unsigned int) ms, sizeof (encoding) - 1);
140 /* limit encoding size (ms) to path size (size) */
141 if (semi + ms > path + size)
142 ms = path + size - semi;
143 memcpy (encoding, semi, ms);
144 encoding[ms] = '\0';
146 #if HAVE_CHARSET
147 if (is_supported_encoding (encoding))
148 coder = str_crt_conv_to (encoding);
149 #endif
151 if (coder != INVALID_CONV)
153 if (slash != NULL)
154 state = str_vfs_convert_to (coder, slash + 1, path + size - slash - 1, buffer);
155 str_close_conv (coder);
156 return state;
159 errno = EINVAL;
160 state = ESTR_FAILURE;
162 else
164 /* path can be translated whole at once */
165 state = str_vfs_convert_to (defcnv, path, size, buffer);
168 return state;
171 /* --------------------------------------------------------------------------------------------- */
172 /*** public functions ****************************************************************************/
173 /* --------------------------------------------------------------------------------------------- */
174 /** Free open file data for given file handle */
176 void
177 vfs_free_handle (int handle)
179 const int idx = handle - VFS_FIRST_HANDLE;
181 if (handle >= VFS_FIRST_HANDLE && (guint) idx < vfs_openfiles->len)
183 struct vfs_openfile *h;
185 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, idx);
186 g_free (h);
187 g_ptr_array_index (vfs_openfiles, idx) = (void *) vfs_free_handle_list;
188 vfs_free_handle_list = idx;
193 /* --------------------------------------------------------------------------------------------- */
194 /** Find private file data by file handle */
196 void *
197 vfs_class_data_find_by_handle (int handle)
199 struct vfs_openfile *h;
201 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
202 return NULL;
204 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
205 if (!h)
206 return NULL;
208 g_assert (h->handle == handle);
210 return h->fsinfo;
213 /* --------------------------------------------------------------------------------------------- */
214 /** Find VFS class by file handle */
216 struct vfs_class *
217 vfs_class_find_by_handle (int handle)
219 struct vfs_openfile *h;
221 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
222 return NULL;
224 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
225 if (!h)
226 return NULL;
228 g_assert (h->handle == handle);
230 return h->vclass;
233 /* --------------------------------------------------------------------------------------------- */
236 * Create new VFS handle and put it to the list
240 vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
242 struct vfs_openfile *h;
244 h = g_new (struct vfs_openfile, 1);
245 h->fsinfo = fsinfo;
246 h->vclass = vclass;
248 /* Allocate the first free handle */
249 h->handle = vfs_free_handle_list;
250 if (h->handle == -1)
252 /* No free allocated handles, allocate one */
253 h->handle = vfs_openfiles->len;
254 g_ptr_array_add (vfs_openfiles, h);
256 else
258 vfs_free_handle_list = (long) g_ptr_array_index (vfs_openfiles, vfs_free_handle_list);
259 g_ptr_array_index (vfs_openfiles, h->handle) = h;
262 h->handle += VFS_FIRST_HANDLE;
263 return h->handle;
266 /* --------------------------------------------------------------------------------------------- */
269 vfs_ferrno (struct vfs_class *vfs)
271 return vfs->ferrno ? (*vfs->ferrno) (vfs) : E_UNKNOWN;
272 /* Hope that error message is obscure enough ;-) */
275 /* --------------------------------------------------------------------------------------------- */
277 gboolean
278 vfs_register_class (struct vfs_class * vfs)
280 if (vfs->init != NULL) /* vfs has own initialization function */
281 if (!vfs->init (vfs)) /* but it failed */
282 return FALSE;
284 g_ptr_array_add (vfs__classes_list, vfs);
286 return TRUE;
289 /* --------------------------------------------------------------------------------------------- */
290 /** Strip known vfs suffixes from a filename (possible improvement: strip
291 * suffix from last path component).
292 * \return a malloced string which has to be freed.
295 char *
296 vfs_strip_suffix_from_filename (const char *filename)
298 char *semi, *p, *vfs_prefix;
300 if (filename == NULL)
301 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
303 p = g_strdup (filename);
304 semi = g_strrstr (p, VFS_PATH_URL_DELIMITER);
305 if (semi == NULL)
306 return p;
308 *semi = '\0';
309 vfs_prefix = strrchr (p, PATH_SEP);
310 if (vfs_prefix == NULL)
312 *semi = *VFS_PATH_URL_DELIMITER;
313 return p;
315 *vfs_prefix = '\0';
317 return p;
320 /* --------------------------------------------------------------------------------------------- */
322 char *
323 vfs_translate_path (const char *path)
325 estr_t state;
327 g_string_set_size (vfs_str_buffer, 0);
328 state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
330 strict version
331 return (state == 0) ? vfs_str_buffer->data : NULL;
333 return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
336 /* --------------------------------------------------------------------------------------------- */
338 char *
339 vfs_translate_path_n (const char *path)
341 char *result;
343 result = vfs_translate_path (path);
344 return (result != NULL) ? g_strdup (result) : NULL;
347 /* --------------------------------------------------------------------------------------------- */
349 * Get current directory without any OS calls.
351 * @return string contain current path
354 char *
355 vfs_get_current_dir (void)
357 return vfs_path_to_str (current_path);
360 /* --------------------------------------------------------------------------------------------- */
362 * Get raw current directory object without any OS calls.
364 * @return object contain current path
367 vfs_path_t *
368 vfs_get_raw_current_dir (void)
370 return current_path;
373 /* --------------------------------------------------------------------------------------------- */
375 * Set current directory object.
377 * @param vpath new path
379 void
380 vfs_set_raw_current_dir (const vfs_path_t * vpath)
382 if (current_path != NULL)
383 vfs_path_free (current_path);
384 current_path = (vfs_path_t *) vpath;
387 /* --------------------------------------------------------------------------------------------- */
388 /* Return TRUE is the current VFS class is local */
390 gboolean
391 vfs_current_is_local (void)
393 return (current_vfs->flags & VFSF_LOCAL) != 0;
396 /* --------------------------------------------------------------------------------------------- */
397 /* Return flags of the VFS class of the given filename */
399 vfs_class_flags_t
400 vfs_file_class_flags (const vfs_path_t * vpath)
402 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
404 if (!vfs_path_element_valid (path_element))
405 return VFSF_UNKNOWN;
407 return path_element->class->flags;
410 /* --------------------------------------------------------------------------------------------- */
412 void
413 vfs_init (void)
415 /* create the VFS handle arrays */
416 vfs__classes_list = g_ptr_array_new ();
418 /* create the VFS handle array */
419 vfs_openfiles = g_ptr_array_new ();
421 vfs_str_buffer = g_string_new ("");
425 /* --------------------------------------------------------------------------------------------- */
427 void
428 vfs_setup_work_dir (void)
430 vfs_path_element_t *path_element;
432 g_free (_vfs_get_cwd ());
434 /* FIXME: is we really need for this check? */
436 if (strlen (current_dir) > MC_MAXPATHLEN - 2)
437 vfs_die ("Current dir too long.\n");
440 path_element = vfs_path_get_by_index (current_path, -1);
441 current_vfs = path_element->class;
444 /* --------------------------------------------------------------------------------------------- */
446 void
447 vfs_shut (void)
449 guint i;
451 vfs_gc_done ();
453 vfs_set_raw_current_dir (NULL);
455 for (i = 0; i < vfs__classes_list->len; i++)
457 struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
459 if (vfs->done != NULL)
460 vfs->done (vfs);
463 g_ptr_array_free (vfs_openfiles, TRUE);
464 g_ptr_array_free (vfs__classes_list, TRUE);
465 g_string_free (vfs_str_buffer, TRUE);
466 g_free (mc_readdir_result);
469 /* --------------------------------------------------------------------------------------------- */
471 * These ones grab information from the VFS
472 * and handles them to an upper layer
475 void
476 vfs_fill_names (fill_names_f func)
478 guint i;
480 for (i = 0; i < vfs__classes_list->len; i++)
482 struct vfs_class *vfs = (struct vfs_class *) g_ptr_array_index (vfs__classes_list, i);
484 if (vfs->fill_names != NULL)
485 vfs->fill_names (vfs, func);
489 /* --------------------------------------------------------------------------------------------- */
490 gboolean
491 vfs_file_is_local (const vfs_path_t * vpath)
493 return (vfs_file_class_flags (vpath) & VFSF_LOCAL) != 0;
496 /* --------------------------------------------------------------------------------------------- */
498 void
499 vfs_print_message (const char *msg, ...)
501 ev_vfs_print_message_t event_data;
503 va_start (event_data.ap, msg);
504 event_data.msg = msg;
506 mc_event_raise (MCEVENT_GROUP_CORE, "vfs_print_message", (gpointer) & event_data);
507 va_end (event_data.ap);
510 /* --------------------------------------------------------------------------------------------- */
512 * Return current directory. If it's local, reread the current directory
513 * from the OS. You must g_strdup() whatever this function returns.
516 char *
517 _vfs_get_cwd (void)
519 vfs_path_element_t *path_element;
521 if (vfs_get_raw_current_dir () == NULL)
523 char *tmp;
524 tmp = g_get_current_dir ();
525 vfs_set_raw_current_dir (vfs_path_from_str (tmp));
526 g_free (tmp);
528 path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
530 if (path_element->class->flags & VFSF_LOCAL)
533 if (path_element->encoding == NULL)
535 char *tmp;
537 tmp = g_get_current_dir ();
538 if (tmp != NULL)
539 { /* One of the directories in the path is not readable */
540 estr_t state;
542 g_string_set_size (vfs_str_buffer, 0);
543 state = str_vfs_convert_from (str_cnv_from_term, tmp, vfs_str_buffer);
544 g_free (tmp);
546 if (state == ESTR_SUCCESS)
548 struct stat my_stat, my_stat2;
549 /* Check if it is O.K. to use the current_dir */
550 if (!(mc_global.vfs.cd_symlinks
551 && mc_stat (vfs_str_buffer->str, &my_stat) == 0
552 && mc_stat (path_element->path, &my_stat2) == 0
553 && my_stat.st_ino == my_stat2.st_ino
554 && my_stat.st_dev == my_stat2.st_dev))
556 vfs_set_raw_current_dir (vfs_path_from_str (vfs_str_buffer->str));
563 return vfs_path_to_str (vfs_get_raw_current_dir ());
566 /* --------------------------------------------------------------------------------------------- */
569 * Change encoding for last part (vfs_path_element_t) of vpath
571 * @param vpath pointer to path structure
572 * encoding name of charset
574 * @return pointer to path structure (for use function in anoter functions)
576 vfs_path_t *
577 vfs_change_encoding (vfs_path_t * vpath, const char *encoding)
579 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
581 /* don't add current encoding */
582 if ((path_element->encoding != NULL) && (strcmp (encoding, path_element->encoding) == 0))
583 return vpath;
585 g_free (path_element->encoding);
586 path_element->encoding = g_strdup (encoding);
588 if (vfs_path_element_need_cleanup_converter (path_element))
589 str_close_conv (path_element->dir.converter);
591 path_element->dir.converter = str_crt_conv_from (path_element->encoding);
593 return vpath;
596 /* --------------------------------------------------------------------------------------------- */