fixed man and help
[free-mc.git] / lib / vfs / mc-vfs / vfs.c
blob64af44a0cf99d08c150a112323d77e5d6ca88caf
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 <stdio.h>
41 #include <stdlib.h> /* For atol() */
42 #include <stdarg.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <signal.h>
47 #include <ctype.h> /* is_digit() */
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51 #include <dirent.h>
53 #include "lib/global.h"
54 #include "lib/strutil.h"
56 #include "src/wtools.h" /* message() */
57 #include "src/main.h" /* print_vfs_message */
59 #include "utilvfs.h"
60 #include "gc.h"
61 #include "vfs.h"
62 #ifdef USE_NETCODE
63 # include "netutil.h"
64 #endif
65 #include "ftpfs.h"
66 #include "mcfs.h"
67 #include "smbfs.h"
68 #include "local.h"
70 #if defined(_AIX) && !defined(NAME_MAX)
71 # define NAME_MAX FILENAME_MAX
72 #endif
74 /** They keep track of the current directory */
75 static struct vfs_class *current_vfs;
76 static char *current_dir;
78 struct vfs_openfile
80 int handle;
81 struct vfs_class *vclass;
82 void *fsinfo;
85 struct vfs_dirinfo
87 DIR *info;
88 GIConv converter;
92 static GPtrArray *vfs_openfiles;
93 static long vfs_free_handle_list = -1;
94 #define VFS_FIRST_HANDLE 100
96 static struct vfs_class *localfs_class;
97 static GString *vfs_str_buffer;
99 static const char *supported_encodings[] = {
100 "UTF8",
101 "UTF-8",
102 "BIG5",
103 "ASCII",
104 "ISO8859",
105 "ISO-8859",
106 "ISO_8859",
107 "KOI8",
108 "CP852",
109 "CP866",
110 "CP125",
111 NULL
114 /** Create new VFS handle and put it to the list */
115 static int
116 vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
118 struct vfs_openfile *h;
120 h = g_new (struct vfs_openfile, 1);
121 h->fsinfo = fsinfo;
122 h->vclass = vclass;
124 /* Allocate the first free handle */
125 h->handle = vfs_free_handle_list;
126 if (h->handle == -1)
128 /* No free allocated handles, allocate one */
129 h->handle = vfs_openfiles->len;
130 g_ptr_array_add (vfs_openfiles, h);
132 else
134 vfs_free_handle_list = (long) g_ptr_array_index (vfs_openfiles, vfs_free_handle_list);
135 g_ptr_array_index (vfs_openfiles, h->handle) = h;
138 h->handle += VFS_FIRST_HANDLE;
139 return h->handle;
142 /** Find VFS class by file handle */
143 static struct vfs_class *
144 vfs_op (int handle)
146 struct vfs_openfile *h;
148 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
149 return NULL;
151 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
152 if (!h)
153 return NULL;
155 g_assert (h->handle == handle);
157 return h->vclass;
160 /** Find private file data by file handle */
161 static void *
162 vfs_info (int handle)
164 struct vfs_openfile *h;
166 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
167 return NULL;
169 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
170 if (!h)
171 return NULL;
173 g_assert (h->handle == handle);
175 return h->fsinfo;
178 /** Free open file data for given file handle */
179 static void
180 vfs_free_handle (int handle)
182 const int idx = handle - VFS_FIRST_HANDLE;
184 if (handle >= VFS_FIRST_HANDLE && (guint) idx < vfs_openfiles->len)
186 struct vfs_openfile *h;
188 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, idx);
189 g_free (h);
190 g_ptr_array_index (vfs_openfiles, idx) = (void *) vfs_free_handle_list;
191 vfs_free_handle_list = idx;
195 static struct vfs_class *vfs_list;
198 vfs_register_class (struct vfs_class *vfs)
200 if (vfs->init != NULL) /* vfs has own initialization function */
201 if (!(*vfs->init) (vfs)) /* but it failed */
202 return 0;
204 vfs->next = vfs_list;
205 vfs_list = vfs;
207 return 1;
210 /** Return VFS class for the given prefix */
211 static struct vfs_class *
212 vfs_prefix_to_class (char *prefix)
214 struct vfs_class *vfs;
216 /* Avoid last class (localfs) that would accept any prefix */
217 for (vfs = vfs_list; vfs->next != NULL; vfs = vfs->next)
219 if (vfs->which != NULL)
221 if ((*vfs->which) (vfs, prefix) == -1)
222 continue;
223 return vfs;
225 if (vfs->prefix != NULL && strncmp (prefix, vfs->prefix, strlen (vfs->prefix)) == 0)
226 return vfs;
228 return NULL;
231 /** Strip known vfs suffixes from a filename (possible improvement: strip
232 * suffix from last path component).
233 * \return a malloced string which has to be freed.
235 char *
236 vfs_strip_suffix_from_filename (const char *filename)
238 struct vfs_class *vfs;
239 char *semi;
240 char *p;
242 if (filename == NULL)
243 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
245 p = g_strdup (filename);
246 semi = strrchr (p, '#');
247 if (semi == NULL)
248 return p;
250 /* Avoid last class (localfs) that would accept any prefix */
251 for (vfs = vfs_list; vfs->next != NULL; vfs = vfs->next)
253 if (vfs->which != NULL)
255 if ((*vfs->which) (vfs, semi + 1) == -1)
256 continue;
257 *semi = '\0'; /* Found valid suffix */
258 return p;
260 if (vfs->prefix != NULL && strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix)) == 0)
262 *semi = '\0'; /* Found valid suffix */
263 return p;
266 return p;
269 static gboolean
270 path_magic (const char *path)
272 struct stat buf;
274 return (stat (path, &buf) != 0);
278 * Splits path extracting vfs part.
280 * Splits path
281 * \verbatim /p1#op/inpath \endverbatim
282 * into
283 * \verbatim inpath,op; \endverbatim
284 * returns which vfs it is.
285 * What is left in path is p1. You still want to g_free(path), you DON'T
286 * want to free neither *inpath nor *op
288 struct vfs_class *
289 vfs_split (char *path, char **inpath, char **op)
291 char *semi;
292 char *slash;
293 struct vfs_class *ret;
295 if (path == NULL)
296 vfs_die ("Cannot split NULL");
298 semi = strrchr (path, '#');
299 if (semi == NULL || !path_magic (path))
300 return NULL;
302 slash = strchr (semi, PATH_SEP);
303 *semi = '\0';
305 if (op != NULL)
306 *op = NULL;
308 if (inpath != NULL)
309 *inpath = NULL;
311 if (slash != NULL)
312 *slash = '\0';
314 ret = vfs_prefix_to_class (semi + 1);
315 if (ret != NULL)
317 if (op != NULL)
318 *op = semi + 1;
319 if (inpath != NULL)
320 *inpath = slash != NULL ? slash + 1 : NULL;
321 return ret;
324 if (slash != NULL)
325 *slash = PATH_SEP;
327 ret = vfs_split (path, inpath, op);
328 *semi = '#';
329 return ret;
332 static struct vfs_class *
333 _vfs_get_class (char *path)
335 char *semi;
336 char *slash;
337 struct vfs_class *ret;
339 g_return_val_if_fail (path, NULL);
341 semi = strrchr (path, '#');
342 if (semi == NULL || !path_magic (path))
343 return NULL;
345 slash = strchr (semi, PATH_SEP);
346 *semi = '\0';
347 if (slash != NULL)
348 *slash = '\0';
350 ret = vfs_prefix_to_class (semi + 1);
352 if (slash != NULL)
353 *slash = PATH_SEP;
354 if (ret == NULL)
355 ret = _vfs_get_class (path);
357 *semi = '#';
358 return ret;
361 struct vfs_class *
362 vfs_get_class (const char *pathname)
364 struct vfs_class *vfs;
365 char *path = g_strdup (pathname);
367 vfs = _vfs_get_class (path);
368 g_free (path);
370 if (!vfs)
371 vfs = localfs_class;
373 return vfs;
376 const char *
377 vfs_get_encoding (const char *path)
379 static char result[16];
380 char *work;
381 char *semi;
382 char *slash;
384 work = g_strdup (path);
385 semi = g_strrstr (work, "#enc:");
387 if (semi != NULL)
389 semi += 5 * sizeof (char);
390 slash = strchr (semi, PATH_SEP);
391 if (slash != NULL)
392 slash[0] = '\0';
394 g_strlcpy (result, semi, sizeof (result));
395 g_free (work);
396 return result;
398 else
400 g_free (work);
401 return NULL;
405 /* return if encoding can by used in vfs (is ascci full compactible) */
406 /* contains only a few encoding now */
407 static int
408 vfs_supported_enconding (const char *encoding)
410 int t;
411 int result = 0;
413 for (t = 0; supported_encodings[t] != NULL; t++)
415 result += (g_ascii_strncasecmp (encoding, supported_encodings[t],
416 strlen (supported_encodings[t])) == 0);
419 return result;
422 /* now used only by vfs_translate_path, but could be used in other vfs
423 * plugin to automatic detect encoding
424 * path - path to translate
425 * size - how many bytes from path translate
426 * defcnv - convertor, that is used as default, when path does not contain any
427 * #enc: subtring
428 * buffer - used to store result of translation
430 static estr_t
431 _vfs_translate_path (const char *path, int size, GIConv defcnv, GString * buffer)
433 const char *semi;
434 const char *ps;
435 const char *slash;
436 estr_t state = ESTR_SUCCESS;
437 static char encoding[16];
438 GIConv coder;
439 int ms;
441 if (size == 0)
442 return 0;
443 size = (size > 0) ? size : (signed int) strlen (path);
445 /* try found #end: */
446 semi = g_strrstr_len (path, size, "#enc:");
447 if (semi != NULL)
449 /* first must be translated part before #enc: */
450 ms = semi - path;
452 /* remove '/' before #enc */
453 ps = str_cget_prev_char (semi);
454 if (ps[0] == PATH_SEP)
455 ms = ps - path;
457 state = _vfs_translate_path (path, ms, defcnv, buffer);
459 if (state != ESTR_SUCCESS)
460 return state;
461 /* now can be translated part after #enc: */
463 semi += 5;
464 slash = strchr (semi, PATH_SEP);
465 /* ignore slashes after size; */
466 if (slash - path >= size)
467 slash = NULL;
469 ms = (slash != NULL) ? slash - semi : (int) strlen (semi);
470 ms = min ((unsigned int) ms, sizeof (encoding) - 1);
471 /* limit encoding size (ms) to path size (size) */
472 if (semi + ms > path + size)
473 ms = path + size - semi;
474 memcpy (encoding, semi, ms);
475 encoding[ms] = '\0';
477 switch (vfs_supported_enconding (encoding))
479 case 1:
480 coder = str_crt_conv_to (encoding);
481 if (coder != INVALID_CONV)
483 if (slash != NULL)
485 state = str_vfs_convert_to (coder, slash, path + size - slash, buffer);
487 else if (buffer->str[0] == '\0')
489 /* exmaple "/#enc:utf-8" */
490 g_string_append_c (buffer, PATH_SEP);
492 str_close_conv (coder);
493 return state;
495 else
497 errno = EINVAL;
498 return ESTR_FAILURE;
500 default:
501 errno = EINVAL;
502 return ESTR_FAILURE;
505 else
507 /* path can be translated whole at once */
508 state = str_vfs_convert_to (defcnv, path, size, buffer);
509 return state;
512 return ESTR_SUCCESS;
515 char *
516 vfs_translate_path (const char *path)
518 estr_t state;
520 g_string_set_size (vfs_str_buffer, 0);
521 state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
523 strict version
524 return (state == 0) ? vfs_str_buffer->data : NULL;
526 return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
529 char *
530 vfs_translate_path_n (const char *path)
532 char *result;
534 result = vfs_translate_path (path);
535 return (result != NULL) ? g_strdup (result) : NULL;
538 char *
539 vfs_canon_and_translate (const char *path)
541 char *canon;
542 char *result;
543 if (path == NULL)
544 canon = g_strdup ("");
545 else
546 canon = vfs_canon (path);
547 result = vfs_translate_path_n (canon);
548 g_free (canon);
549 return result;
552 static int
553 ferrno (struct vfs_class *vfs)
555 return vfs->ferrno ? (*vfs->ferrno) (vfs) : E_UNKNOWN;
556 /* Hope that error message is obscure enough ;-) */
560 mc_open (const char *filename, int flags, ...)
562 int mode = 0;
563 void *info;
564 va_list ap;
565 char *file;
566 struct vfs_class *vfs;
568 file = vfs_canon_and_translate (filename);
569 if (file == NULL)
570 return -1;
572 vfs = vfs_get_class (file);
574 /* Get the mode flag */
575 if (flags & O_CREAT)
577 va_start (ap, flags);
578 mode = va_arg (ap, int);
579 va_end (ap);
582 if (vfs->open == NULL)
584 g_free (file);
585 errno = -EOPNOTSUPP;
586 return -1;
589 info = vfs->open (vfs, file, flags, mode); /* open must be supported */
590 g_free (file);
591 if (info == NULL)
593 errno = ferrno (vfs);
594 return -1;
597 return vfs_new_handle (vfs, info);
601 #define MC_NAMEOP(name, inarg, callarg) \
602 int mc_##name inarg \
604 struct vfs_class *vfs; \
605 int result; \
606 char *mpath = vfs_canon_and_translate (path); \
607 if (mpath == NULL) \
608 return -1; \
609 vfs = vfs_get_class (mpath); \
610 if (vfs == NULL){ \
611 g_free (mpath); \
612 return -1; \
614 result = vfs->name != NULL ? vfs->name callarg : -1; \
615 g_free (mpath); \
616 if (result == -1) \
617 errno = vfs->name != NULL ? ferrno (vfs) : E_NOTSUPP; \
618 return result; \
621 MC_NAMEOP (chmod, (const char *path, mode_t mode), (vfs, mpath, mode))
622 MC_NAMEOP (chown, (const char *path, uid_t owner, gid_t group), (vfs, mpath, owner, group))
623 MC_NAMEOP (utime, (const char *path, struct utimbuf * times), (vfs, mpath, times))
624 MC_NAMEOP (readlink, (const char *path, char *buf, int bufsiz), (vfs, mpath, buf, bufsiz))
625 MC_NAMEOP (unlink, (const char *path), (vfs, mpath))
626 MC_NAMEOP (mkdir, (const char *path, mode_t mode), (vfs, mpath, mode))
627 MC_NAMEOP (rmdir, (const char *path), (vfs, mpath))
628 MC_NAMEOP (mknod, (const char *path, mode_t mode, dev_t dev), (vfs, mpath, mode, dev))
631 mc_symlink (const char *name1, const char *path)
633 struct vfs_class *vfs;
634 int result;
635 char *mpath;
636 char *lpath;
637 char *tmp;
639 mpath = vfs_canon_and_translate (path);
640 if (mpath == NULL)
641 return -1;
643 tmp = g_strdup (name1);
644 lpath = vfs_translate_path_n (tmp);
645 g_free (tmp);
647 if (lpath != NULL)
649 vfs = vfs_get_class (mpath);
650 result = vfs->symlink != NULL ? vfs->symlink (vfs, lpath, mpath) : -1;
651 g_free (lpath);
652 g_free (mpath);
654 if (result == -1)
655 errno = vfs->symlink != NULL ? ferrno (vfs) : E_NOTSUPP;
656 return result;
659 g_free (mpath);
661 return -1;
664 #define MC_HANDLEOP(name, inarg, callarg) \
665 ssize_t mc_##name inarg \
667 struct vfs_class *vfs; \
668 int result; \
669 if (handle == -1) \
670 return -1; \
671 vfs = vfs_op (handle); \
672 if (vfs == NULL) \
673 return -1; \
674 result = vfs->name != NULL ? vfs->name callarg : -1; \
675 if (result == -1) \
676 errno = vfs->name != NULL ? ferrno (vfs) : E_NOTSUPP; \
677 return result; \
680 MC_HANDLEOP (read, (int handle, void *buffer, int count), (vfs_info (handle), buffer, count))
681 MC_HANDLEOP (write, (int handle, const void *buf, int nbyte), (vfs_info (handle), buf, nbyte))
683 #define MC_RENAMEOP(name) \
684 int mc_##name (const char *fname1, const char *fname2) \
686 struct vfs_class *vfs; \
687 int result; \
688 char *name2, *name1; \
689 name1 = vfs_canon_and_translate (fname1); \
690 if (name1 == NULL) \
691 return -1; \
692 name2 = vfs_canon_and_translate (fname2); \
693 if (name2 == NULL) { \
694 g_free (name1); \
695 return -1; \
697 vfs = vfs_get_class (name1); \
698 if (vfs != vfs_get_class (name2)) \
700 errno = EXDEV; \
701 g_free (name1); \
702 g_free (name2); \
703 return -1; \
705 result = vfs->name != NULL ? vfs->name (vfs, name1, name2) : -1; \
706 g_free (name1); \
707 g_free (name2); \
708 if (result == -1) \
709 errno = vfs->name != NULL ? ferrno (vfs) : E_NOTSUPP; \
710 return result; \
713 MC_RENAMEOP (link) MC_RENAMEOP (rename)
716 mc_ctl (int handle, int ctlop, void *arg)
718 struct vfs_class *vfs = vfs_op (handle);
720 if (vfs == NULL)
721 return 0;
723 return vfs->ctl ? (*vfs->ctl) (vfs_info (handle), ctlop, arg) : 0;
727 mc_setctl (const char *path, int ctlop, void *arg)
729 struct vfs_class *vfs;
730 int result = -1;
731 char *mpath;
733 if (path == NULL)
734 vfs_die ("You don't want to pass NULL to mc_setctl.");
736 mpath = vfs_canon_and_translate (path);
737 if (mpath != NULL)
739 vfs = vfs_get_class (mpath);
740 result = vfs->setctl != NULL ? vfs->setctl (vfs, mpath, ctlop, arg) : 0;
741 g_free (mpath);
744 return result;
748 mc_close (int handle)
750 struct vfs_class *vfs;
751 int result;
753 if (handle == -1 || !vfs_info (handle))
754 return -1;
756 vfs = vfs_op (handle);
757 if (vfs == NULL)
758 return -1;
760 if (handle < 3)
761 return close (handle);
763 if (!vfs->close)
764 vfs_die ("VFS must support close.\n");
765 result = (*vfs->close) (vfs_info (handle));
766 vfs_free_handle (handle);
767 if (result == -1)
768 errno = ferrno (vfs);
770 return result;
773 DIR *
774 mc_opendir (const char *dirname)
776 int handle, *handlep;
777 void *info;
778 struct vfs_class *vfs;
779 char *canon;
780 char *dname;
781 struct vfs_dirinfo *dirinfo;
782 const char *encoding;
784 canon = vfs_canon (dirname);
785 dname = vfs_translate_path_n (canon);
787 if (dname != NULL)
789 vfs = vfs_get_class (dname);
790 info = vfs->opendir ? (*vfs->opendir) (vfs, dname) : NULL;
791 g_free (dname);
793 if (info == NULL)
795 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
796 g_free (canon);
797 return NULL;
800 dirinfo = g_new (struct vfs_dirinfo, 1);
801 dirinfo->info = info;
803 encoding = vfs_get_encoding (canon);
804 g_free (canon);
805 dirinfo->converter = (encoding != NULL) ? str_crt_conv_from (encoding) : str_cnv_from_term;
806 if (dirinfo->converter == INVALID_CONV)
807 dirinfo->converter = str_cnv_from_term;
809 handle = vfs_new_handle (vfs, dirinfo);
811 handlep = g_new (int, 1);
812 *handlep = handle;
813 return (DIR *) handlep;
815 else
817 g_free (canon);
818 return NULL;
822 static struct dirent *mc_readdir_result = NULL;
824 struct dirent *
825 mc_readdir (DIR * dirp)
827 int handle;
828 struct vfs_class *vfs;
829 struct dirent *entry = NULL;
830 struct vfs_dirinfo *dirinfo;
831 estr_t state;
833 if (!mc_readdir_result)
835 /* We can't just allocate struct dirent as (see man dirent.h)
836 * struct dirent has VERY nonnaive semantics of allocating
837 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
838 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
839 * heap corrupter. So, allocate longliving dirent with at least
840 * (MAXNAMLEN + 1) for d_name in it.
841 * Strictly saying resulting dirent is unusable as we don't adjust internal
842 * structures, holding dirent size. But we don't use it in libc infrastructure.
843 * TODO: to make simpler homemade dirent-alike structure.
845 mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1);
848 if (!dirp)
850 errno = EFAULT;
851 return NULL;
853 handle = *(int *) dirp;
855 vfs = vfs_op (handle);
856 if (vfs == NULL)
857 return NULL;
859 dirinfo = vfs_info (handle);
860 if (vfs->readdir)
862 entry = (*vfs->readdir) (dirinfo->info);
863 if (entry == NULL)
864 return NULL;
865 g_string_set_size (vfs_str_buffer, 0);
866 state = str_vfs_convert_from (dirinfo->converter, entry->d_name, vfs_str_buffer);
867 mc_readdir_result->d_ino = entry->d_ino;
868 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1);
870 if (entry == NULL)
871 errno = vfs->readdir ? ferrno (vfs) : E_NOTSUPP;
872 return (entry != NULL) ? mc_readdir_result : NULL;
876 mc_closedir (DIR * dirp)
878 int handle = *(int *) dirp;
879 struct vfs_class *vfs;
880 int result = -1;
882 vfs = vfs_op (handle);
883 if (vfs != NULL)
885 struct vfs_dirinfo *dirinfo;
887 dirinfo = vfs_info (handle);
888 if (dirinfo->converter != str_cnv_from_term)
889 str_close_conv (dirinfo->converter);
891 result = vfs->closedir ? (*vfs->closedir) (dirinfo->info) : -1;
892 vfs_free_handle (handle);
893 g_free (dirinfo);
895 g_free (dirp);
896 return result;
900 mc_stat (const char *filename, struct stat *buf)
902 struct vfs_class *vfs;
903 int result;
904 char *path;
906 path = vfs_canon_and_translate (filename);
908 if (path == NULL)
909 return -1;
911 vfs = vfs_get_class (path);
913 if (vfs == NULL)
915 g_free (path);
916 return -1;
919 result = vfs->stat ? (*vfs->stat) (vfs, path, buf) : -1;
921 g_free (path);
923 if (result == -1)
924 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
925 return result;
929 mc_lstat (const char *filename, struct stat *buf)
931 struct vfs_class *vfs;
932 int result;
933 char *path;
935 path = vfs_canon_and_translate (filename);
937 if (path == NULL)
938 return -1;
940 vfs = vfs_get_class (path);
941 if (vfs == NULL)
943 g_free (path);
944 return -1;
947 result = vfs->lstat ? (*vfs->lstat) (vfs, path, buf) : -1;
948 g_free (path);
949 if (result == -1)
950 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
951 return result;
955 mc_fstat (int handle, struct stat *buf)
957 struct vfs_class *vfs;
958 int result;
960 if (handle == -1)
961 return -1;
963 vfs = vfs_op (handle);
964 if (vfs == NULL)
965 return -1;
967 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
968 if (result == -1)
969 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
970 return result;
974 * Return current directory. If it's local, reread the current directory
975 * from the OS. You must g_strdup() whatever this function returns.
977 static const char *
978 _vfs_get_cwd (void)
980 char *trans;
982 trans = vfs_translate_path_n (current_dir);
984 if (_vfs_get_class (trans) == NULL)
986 const char *encoding = vfs_get_encoding (current_dir);
988 if (encoding == NULL)
990 char *tmp;
992 tmp = g_get_current_dir ();
993 if (tmp != NULL)
994 { /* One of the directories in the path is not readable */
995 estr_t state;
996 char *sys_cwd;
998 g_string_set_size (vfs_str_buffer, 0);
999 state = str_vfs_convert_from (str_cnv_from_term, tmp, vfs_str_buffer);
1000 g_free (tmp);
1002 sys_cwd = (state == ESTR_SUCCESS) ? g_strdup (vfs_str_buffer->str) : NULL;
1003 if (sys_cwd != NULL)
1005 struct stat my_stat, my_stat2;
1006 /* Check if it is O.K. to use the current_dir */
1007 if (cd_symlinks
1008 && mc_stat (sys_cwd, &my_stat) == 0
1009 && mc_stat (current_dir, &my_stat2) == 0
1010 && my_stat.st_ino == my_stat2.st_ino && my_stat.st_dev == my_stat2.st_dev)
1011 g_free (sys_cwd);
1012 else
1014 g_free (current_dir);
1015 current_dir = sys_cwd;
1022 g_free (trans);
1023 return current_dir;
1026 static void
1027 vfs_setup_wd (void)
1029 current_dir = g_strdup (PATH_SEP_STR);
1030 _vfs_get_cwd ();
1032 if (strlen (current_dir) > MC_MAXPATHLEN - 2)
1033 vfs_die ("Current dir too long.\n");
1035 current_vfs = vfs_get_class (current_dir);
1039 * Return current directory. If it's local, reread the current directory
1040 * from the OS. Put directory to the provided buffer.
1042 char *
1043 mc_get_current_wd (char *buffer, int size)
1045 const char *cwd = _vfs_get_cwd ();
1047 g_strlcpy (buffer, cwd, size);
1048 return buffer;
1052 * Return current directory without any OS calls.
1054 char *
1055 vfs_get_current_dir (void)
1057 return current_dir;
1060 off_t
1061 mc_lseek (int fd, off_t offset, int whence)
1063 struct vfs_class *vfs;
1064 int result;
1066 if (fd == -1)
1067 return -1;
1069 vfs = vfs_op (fd);
1070 if (vfs == NULL)
1071 return -1;
1073 result = vfs->lseek ? (*vfs->lseek) (vfs_info (fd), offset, whence) : -1;
1074 if (result == -1)
1075 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
1076 return result;
1080 * remove //, /./ and /../
1083 #define ISSLASH(a) (!a || (a == '/'))
1085 char *
1086 vfs_canon (const char *path)
1088 if (!path)
1089 vfs_die ("Cannot canonicalize NULL");
1091 /* Relative to current directory */
1092 if (*path != PATH_SEP)
1094 char *local, *result;
1096 local = concat_dir_and_file (current_dir, path);
1098 result = vfs_canon (local);
1099 g_free (local);
1100 return result;
1104 * So we have path of following form:
1105 * /p1/p2#op/.././././p3#op/p4. Good luck.
1108 char *result = g_strdup (path);
1109 canonicalize_pathname (result);
1110 return result;
1115 * VFS chdir.
1116 * Return 0 on success, -1 on failure.
1119 mc_chdir (const char *path)
1121 char *new_dir;
1122 char *trans_dir;
1123 struct vfs_class *old_vfs, *new_vfs;
1124 vfsid old_vfsid;
1125 int result;
1127 new_dir = vfs_canon (path);
1128 trans_dir = vfs_translate_path_n (new_dir);
1129 if (trans_dir != NULL)
1131 new_vfs = vfs_get_class (trans_dir);
1132 if (!new_vfs->chdir)
1134 g_free (new_dir);
1135 g_free (trans_dir);
1136 return -1;
1139 result = (*new_vfs->chdir) (new_vfs, trans_dir);
1141 if (result == -1)
1143 errno = ferrno (new_vfs);
1144 g_free (new_dir);
1145 g_free (trans_dir);
1146 return -1;
1149 old_vfsid = vfs_getid (current_vfs, current_dir);
1150 old_vfs = current_vfs;
1152 /* Actually change directory */
1153 g_free (current_dir);
1154 current_dir = new_dir;
1155 current_vfs = new_vfs;
1157 /* This function uses the new current_dir implicitly */
1158 vfs_stamp_create (old_vfs, old_vfsid);
1160 /* Sometimes we assume no trailing slash on cwd */
1161 if (*current_dir)
1163 char *p;
1164 p = strchr (current_dir, 0) - 1;
1165 if (*p == PATH_SEP && p > current_dir)
1166 *p = 0;
1169 g_free (trans_dir);
1170 return 0;
1172 else
1174 g_free (new_dir);
1175 return -1;
1179 /* Return 1 is the current VFS class is local */
1181 vfs_current_is_local (void)
1183 return (current_vfs->flags & VFSF_LOCAL) != 0;
1186 /* Return flags of the VFS class of the given filename */
1188 vfs_file_class_flags (const char *filename)
1190 struct vfs_class *vfs;
1191 char *fname;
1193 fname = vfs_canon_and_translate (filename);
1194 if (fname == NULL)
1195 return -1;
1197 vfs = vfs_get_class (fname);
1198 g_free (fname);
1199 return vfs->flags;
1202 static char *
1203 mc_def_getlocalcopy (const char *filename)
1205 char *tmp;
1206 int fdin, fdout;
1207 ssize_t i;
1208 char buffer[8192];
1209 struct stat mystat;
1211 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
1212 if (fdin == -1)
1213 return NULL;
1215 fdout = vfs_mkstemps (&tmp, "vfs", filename);
1217 if (fdout == -1)
1218 goto fail;
1219 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
1221 if (write (fdout, buffer, i) != i)
1222 goto fail;
1224 if (i == -1)
1225 goto fail;
1226 i = mc_close (fdin);
1227 fdin = -1;
1228 if (i == -1)
1229 goto fail;
1230 if (close (fdout) == -1)
1232 fdout = -1;
1233 goto fail;
1236 if (mc_stat (filename, &mystat) != -1)
1237 chmod (tmp, mystat.st_mode);
1239 return tmp;
1241 fail:
1242 if (fdout != -1)
1243 close (fdout);
1244 if (fdin != -1)
1245 mc_close (fdin);
1246 g_free (tmp);
1247 return NULL;
1250 char *
1251 mc_getlocalcopy (const char *pathname)
1253 char *result = NULL;
1254 char *path;
1256 path = vfs_canon_and_translate (pathname);
1257 if (path != NULL)
1259 struct vfs_class *vfs = vfs_get_class (path);
1261 result = vfs->getlocalcopy != NULL ?
1262 vfs->getlocalcopy (vfs, path) :
1263 mc_def_getlocalcopy (path);
1264 g_free (path);
1265 if (result == NULL)
1266 errno = ferrno (vfs);
1269 return result;
1272 static int
1273 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
1274 const char *local, int has_changed)
1276 int fdin = -1, fdout = -1, i;
1277 if (has_changed)
1279 char buffer[8192];
1281 if (!vfs->write)
1282 goto failed;
1284 fdin = open (local, O_RDONLY);
1285 if (fdin == -1)
1286 goto failed;
1287 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1288 if (fdout == -1)
1289 goto failed;
1290 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
1292 if (mc_write (fdout, buffer, i) != i)
1293 goto failed;
1295 if (i == -1)
1296 goto failed;
1298 if (close (fdin) == -1)
1300 fdin = -1;
1301 goto failed;
1303 fdin = -1;
1304 if (mc_close (fdout) == -1)
1306 fdout = -1;
1307 goto failed;
1310 unlink (local);
1311 return 0;
1313 failed:
1314 message (D_ERROR, _("Changes to file lost"), "%s", filename);
1315 if (fdout != -1)
1316 mc_close (fdout);
1317 if (fdin != -1)
1318 close (fdin);
1319 unlink (local);
1320 return -1;
1324 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
1326 int return_value = -1;
1327 char *path;
1329 path = vfs_canon_and_translate (pathname);
1330 if (path != NULL)
1332 struct vfs_class *vfs = vfs_get_class (path);
1334 return_value = vfs->ungetlocalcopy != NULL ?
1335 vfs->ungetlocalcopy (vfs, path, local, has_changed) :
1336 mc_def_ungetlocalcopy (vfs, path, local, has_changed);
1337 g_free (path);
1340 return return_value;
1344 void
1345 vfs_init (void)
1347 /* create the VFS handle array */
1348 vfs_openfiles = g_ptr_array_new ();
1350 vfs_str_buffer = g_string_new ("");
1351 /* localfs needs to be the first one */
1352 init_localfs ();
1353 /* fallback value for vfs_get_class() */
1354 localfs_class = vfs_list;
1356 init_extfs ();
1357 init_sfs ();
1358 init_tarfs ();
1359 init_cpiofs ();
1361 #ifdef USE_EXT2FSLIB
1362 init_undelfs ();
1363 #endif /* USE_EXT2FSLIB */
1365 #ifdef USE_NETCODE
1366 init_ftpfs ();
1367 init_fish ();
1368 #ifdef ENABLE_VFS_SMB
1369 init_smbfs ();
1370 #endif /* ENABLE_VFS_SMB */
1371 #ifdef ENABLE_VFS_MCFS
1372 init_mcfs ();
1373 #endif /* ENABLE_VFS_MCFS */
1374 #endif /* USE_NETCODE */
1376 vfs_setup_wd ();
1379 void
1380 vfs_shut (void)
1382 struct vfs_class *vfs;
1384 vfs_gc_done ();
1386 g_free (current_dir);
1388 for (vfs = vfs_list; vfs; vfs = vfs->next)
1389 if (vfs->done)
1390 (*vfs->done) (vfs);
1392 g_ptr_array_free (vfs_openfiles, TRUE);
1393 g_string_free (vfs_str_buffer, TRUE);
1394 g_free (mc_readdir_result);
1398 * These ones grab information from the VFS
1399 * and handles them to an upper layer
1401 void
1402 vfs_fill_names (fill_names_f func)
1404 struct vfs_class *vfs;
1406 for (vfs = vfs_list; vfs; vfs = vfs->next)
1407 if (vfs->fill_names)
1408 (*vfs->fill_names) (vfs, func);
1412 * Returns vfs path corresponding to given url. If passed string is
1413 * not recognized as url, g_strdup(url) is returned.
1416 static const struct
1418 const char *name;
1419 size_t name_len;
1420 const char *substitute;
1421 } url_table[] =
1423 /* *INDENT-OFF* */
1424 { "ftp://", 6, "/#ftp:" },
1425 { "mc://", 5, "/#mc:" },
1426 { "smb://", 6, "/#smb:" },
1427 { "sh://", 5, "/#sh:" },
1428 { "ssh://", 6, "/#sh:" },
1429 { "a:", 2, "/#a" }
1430 /* *INDENT-ON* */
1433 char *
1434 vfs_translate_url (const char *url)
1436 size_t i;
1438 for (i = 0; i < sizeof (url_table) / sizeof (url_table[0]); i++)
1439 if (strncmp (url, url_table[i].name, url_table[i].name_len) == 0)
1440 return g_strconcat (url_table[i].substitute, url + url_table[i].name_len,
1441 (char *) NULL);
1443 return g_strdup (url);
1447 vfs_file_is_local (const char *filename)
1449 return vfs_file_class_flags (filename) & VFSF_LOCAL;