Move all stuff from lib to contrib
[midnight-commander.git] / vfs / vfs.c
blob9188a5544fedfacd567352a1f727d06f5180e50d
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() */
49 #include "../src/global.h"
50 #include "../src/tty.h" /* enable/disable interrupt key */
51 #include "../src/wtools.h" /* message() */
52 #include "../src/main.h" /* print_vfs_message */
53 #include "../src/strutil.h"
54 #include "utilvfs.h"
55 #include "gc.h"
57 #include "vfs.h"
58 #ifdef USE_NETCODE
59 # include "tcputil.h"
60 #endif
61 #include "ftpfs.h"
62 #include "mcfs.h"
63 #include "smbfs.h"
64 #include "local.h"
66 /** They keep track of the current directory */
67 static struct vfs_class *current_vfs;
68 static char *current_dir;
70 struct vfs_openfile {
71 int handle;
72 struct vfs_class *vclass;
73 void *fsinfo;
76 struct vfs_dirinfo{
77 DIR *info;
78 GIConv converter;
82 static GSList *vfs_openfiles;
83 #define VFS_FIRST_HANDLE 100
85 static struct vfs_class *localfs_class;
86 static GString *vfs_str_buffer;
88 static const char *supported_encodings[] = {
89 "UTF8",
90 "UTF-8",
91 "BIG5",
92 "ASCII",
93 "ISO8859",
94 "ISO-8859",
95 "ISO_8859",
96 "KOI8",
97 "CP852",
98 "CP866",
99 "CP125",
100 NULL
103 /** Create new VFS handle and put it to the list */
104 static int
105 vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
107 static int vfs_handle_counter = VFS_FIRST_HANDLE;
108 struct vfs_openfile *h;
110 h = g_new (struct vfs_openfile, 1);
111 h->handle = vfs_handle_counter++;
112 h->fsinfo = fsinfo;
113 h->vclass = vclass;
114 vfs_openfiles = g_slist_prepend (vfs_openfiles, h);
115 return h->handle;
118 /** Function to match handle, passed to g_slist_find_custom() */
119 static gint
120 vfs_cmp_handle (gconstpointer a, gconstpointer b)
122 if (!a)
123 return 1;
124 return ((struct vfs_openfile *) a)->handle != (long) b;
127 /** Find VFS class by file handle */
128 static inline struct vfs_class *
129 vfs_op (int handle)
131 GSList *l;
132 struct vfs_openfile *h;
134 l = g_slist_find_custom (vfs_openfiles, (void *) (long) handle,
135 vfs_cmp_handle);
136 if (!l)
137 return NULL;
138 h = (struct vfs_openfile *) l->data;
139 if (!h)
140 return NULL;
141 return h->vclass;
144 /** Find private file data by file handle */
145 static inline void *
146 vfs_info (int handle)
148 GSList *l;
149 struct vfs_openfile *h;
151 l = g_slist_find_custom (vfs_openfiles, (void *) (long) handle,
152 vfs_cmp_handle);
153 if (!l)
154 return NULL;
155 h = (struct vfs_openfile *) l->data;
156 if (!h)
157 return NULL;
158 return h->fsinfo;
161 /** Free open file data for given file handle */
162 static inline void
163 vfs_free_handle (int handle)
165 GSList *l;
167 l = g_slist_find_custom (vfs_openfiles, (void *) (long) handle,
168 vfs_cmp_handle);
169 vfs_openfiles = g_slist_delete_link (vfs_openfiles, l);
172 static struct vfs_class *vfs_list;
175 vfs_register_class (struct vfs_class *vfs)
177 if (vfs->init) /* vfs has own initialization function */
178 if (!(*vfs->init)(vfs)) /* but it failed */
179 return 0;
181 vfs->next = vfs_list;
182 vfs_list = vfs;
184 return 1;
187 /** Return VFS class for the given prefix */
188 static struct vfs_class *
189 vfs_prefix_to_class (char *prefix)
191 struct vfs_class *vfs;
193 /* Avoid last class (localfs) that would accept any prefix */
194 for (vfs = vfs_list; vfs->next; vfs = vfs->next) {
195 if (vfs->which) {
196 if ((*vfs->which) (vfs, prefix) == -1)
197 continue;
198 return vfs;
200 if (vfs->prefix
201 && !strncmp (prefix, vfs->prefix, strlen (vfs->prefix)))
202 return vfs;
204 return NULL;
207 /** Strip known vfs suffixes from a filename (possible improvement: strip
208 * suffix from last path component).
209 * \return a malloced string which has to be freed.
211 char *
212 vfs_strip_suffix_from_filename (const char *filename)
214 struct vfs_class *vfs;
215 char *semi;
216 char *p;
218 if (!filename)
219 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
221 p = g_strdup (filename);
222 if (!(semi = strrchr (p, '#')))
223 return p;
225 /* Avoid last class (localfs) that would accept any prefix */
226 for (vfs = vfs_list; vfs->next; vfs = vfs->next) {
227 if (vfs->which) {
228 if ((*vfs->which) (vfs, semi + 1) == -1)
229 continue;
230 *semi = '\0'; /* Found valid suffix */
231 return p;
233 if (vfs->prefix
234 && !strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
235 *semi = '\0'; /* Found valid suffix */
236 return p;
239 return p;
242 static inline int
243 path_magic (const char *path)
245 struct stat buf;
247 if (!stat(path, &buf))
248 return 0;
250 return 1;
254 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
255 * What is left in path is p1. You still want to g_free(path), you DON'T
256 * want to free neither *inpath nor *op
258 struct vfs_class *
259 vfs_split (char *path, char **inpath, char **op)
261 char *semi;
262 char *slash;
263 struct vfs_class *ret;
265 if (!path)
266 vfs_die("Cannot split NULL");
268 semi = strrchr (path, '#');
269 if (!semi || !path_magic(path))
270 return NULL;
272 slash = strchr (semi, PATH_SEP);
273 *semi = 0;
275 if (op)
276 *op = NULL;
278 if (inpath)
279 *inpath = NULL;
281 if (slash)
282 *slash = 0;
284 if ((ret = vfs_prefix_to_class (semi+1))){
285 if (op)
286 *op = semi + 1;
287 if (inpath)
288 *inpath = slash ? slash + 1 : NULL;
289 return ret;
293 if (slash)
294 *slash = PATH_SEP;
295 ret = vfs_split (path, inpath, op);
296 *semi = '#';
297 return ret;
300 static struct vfs_class *
301 _vfs_get_class (char *path)
303 char *semi;
304 char *slash;
305 struct vfs_class *ret;
307 g_return_val_if_fail(path, NULL);
309 semi = strrchr (path, '#');
310 if (!semi || !path_magic (path))
311 return NULL;
313 slash = strchr (semi, PATH_SEP);
314 *semi = 0;
315 if (slash)
316 *slash = 0;
318 ret = vfs_prefix_to_class (semi+1);
320 if (slash)
321 *slash = PATH_SEP;
322 if (!ret)
323 ret = _vfs_get_class (path);
325 *semi = '#';
326 return ret;
329 struct vfs_class *
330 vfs_get_class (const char *pathname)
332 struct vfs_class *vfs;
333 char *path = g_strdup (pathname);
335 vfs = _vfs_get_class (path);
336 g_free (path);
338 if (!vfs)
339 vfs = localfs_class;
341 return vfs;
344 const char *
345 vfs_get_encoding (const char *path)
347 static char result[16];
348 char *work;
349 char *semi;
350 char *slash;
352 work = g_strdup (path);
353 semi = g_strrstr (work, "#enc:");
355 if (semi != NULL) {
356 semi+= 5 * sizeof (char);
357 slash = strchr (semi, PATH_SEP);
358 if (slash != NULL)
359 slash[0] = '\0';
361 g_strlcpy (result, semi, sizeof(result));
362 g_free (work);
363 return result;
364 } else {
365 g_free (work);
366 return NULL;
370 /* return if encoding can by used in vfs (is ascci full compactible) */
371 /* contains only a few encoding now */
372 static int
373 vfs_supported_enconding (const char *encoding) {
374 int t;
375 int result = 0;
377 for (t = 0; supported_encodings[t] != NULL; t++) {
378 result+= (g_ascii_strncasecmp (encoding, supported_encodings[t],
379 strlen (supported_encodings[t])) == 0);
382 return result;
385 /* now used only by vfs_translate_path, but could be used in other vfs
386 * plugin to automatic detect encoding
387 * path - path to translate
388 * size - how many bytes from path translate
389 * defcnv - convertor, that is used as default, when path does not contain any
390 * #enc: subtring
391 * buffer - used to store result of translation
393 static estr_t
394 _vfs_translate_path (const char *path, int size,
395 GIConv defcnv, GString *buffer)
397 const char *semi;
398 const char *ps;
399 const char *slash;
400 estr_t state = ESTR_SUCCESS;
401 static char encoding[16];
402 GIConv coder;
403 int ms;
405 if (size == 0) return 0;
406 size = ( size > 0) ? size : (signed int)strlen (path);
408 /* try found #end: */
409 semi = g_strrstr_len (path, size, "#enc:");
410 if (semi != NULL) {
411 /* first must be translated part before #enc: */
412 ms = semi - path;
414 /* remove '/' before #enc */
415 ps = str_cget_prev_char (semi);
416 if (ps[0] == PATH_SEP) ms = ps - path;
418 state = _vfs_translate_path (path, ms, defcnv, buffer);
420 if (state != ESTR_SUCCESS)
421 return state;
422 /* now can be translated part after #enc: */
424 semi+= 5;
425 slash = strchr (semi, PATH_SEP);
426 /* ignore slashes after size; */
427 if (slash - path >= size) slash = NULL;
429 ms = (slash != NULL) ? slash - semi : (int) strlen (semi);
430 ms = min ((unsigned int) ms, sizeof (encoding) - 1);
431 /* limit encoding size (ms) to path size (size) */
432 if (semi + ms > path + size) ms = path + size - semi;
433 memcpy (encoding, semi, ms);
434 encoding[ms] = '\0';
436 switch (vfs_supported_enconding (encoding)) {
437 case 1:
438 coder = str_crt_conv_to (encoding);
439 if (coder != INVALID_CONV) {
440 if (slash != NULL) {
441 state = str_vfs_convert_to (coder, slash,
442 path + size - slash, buffer);
443 } else if (buffer->str[0] == '\0') {
444 /* exmaple "/#enc:utf-8" */
445 g_string_append_c(buffer, PATH_SEP);
447 str_close_conv (coder);
448 return state;
449 } else {
450 errno = EINVAL;
451 return ESTR_FAILURE;
453 break;
454 default:
455 errno = EINVAL;
456 return ESTR_FAILURE;
458 } else {
459 /* path can be translated whole at once */
460 state = str_vfs_convert_to (defcnv, path, size, buffer);
461 return state;
464 return ESTR_SUCCESS;
467 char *
468 vfs_translate_path (const char *path)
470 estr_t state;
472 g_string_set_size(vfs_str_buffer,0);
473 state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
475 strict version
476 return (state == 0) ? vfs_str_buffer->data : NULL;
478 return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
481 char *
482 vfs_translate_path_n (const char *path)
484 char *result;
486 result = vfs_translate_path (path);
487 return (result != NULL) ? g_strdup (result) : NULL;
490 char *
491 vfs_canon_and_translate (const char *path)
493 char *canon;
494 char *result;
495 canon = vfs_canon (path);
496 result = vfs_translate_path_n (canon);
497 g_free (canon);
498 return result;
501 static int
502 ferrno (struct vfs_class *vfs)
504 return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
505 /* Hope that error message is obscure enough ;-) */
509 mc_open (const char *filename, int flags, ...)
511 int mode;
512 void *info;
513 va_list ap;
515 char *file = vfs_canon_and_translate (filename);
516 if (file != NULL) {
517 struct vfs_class *vfs = vfs_get_class (file);
519 /* Get the mode flag */
520 if (flags & O_CREAT) {
521 va_start (ap, flags);
522 mode = va_arg (ap, int);
523 va_end (ap);
524 } else
525 mode = 0;
527 if (!vfs->open) {
528 g_free (file);
529 errno = -EOPNOTSUPP;
530 return -1;
533 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
534 g_free (file);
535 if (!info){
536 errno = ferrno (vfs);
537 return -1;
540 return vfs_new_handle (vfs, info);
541 } else return -1;
545 #define MC_NAMEOP(name, inarg, callarg) \
546 int mc_##name inarg \
548 struct vfs_class *vfs; \
549 int result; \
550 char *mpath = vfs_canon_and_translate (path); \
551 if (mpath != NULL) { \
552 vfs = vfs_get_class (mpath); \
553 result = vfs->name ? (*vfs->name)callarg : -1; \
554 g_free (mpath); \
555 if (result == -1) \
556 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
557 return result; \
558 } else return -1; \
561 MC_NAMEOP (chmod, (const char *path, mode_t mode), (vfs, mpath, mode))
562 MC_NAMEOP (chown, (const char *path, uid_t owner, gid_t group), (vfs, mpath, owner, group))
563 MC_NAMEOP (utime, (const char *path, struct utimbuf *times), (vfs, mpath, times))
564 MC_NAMEOP (readlink, (const char *path, char *buf, int bufsiz), (vfs, mpath, buf, bufsiz))
565 MC_NAMEOP (unlink, (const char *path), (vfs, mpath))
566 MC_NAMEOP (mkdir, (const char *path, mode_t mode), (vfs, mpath, mode))
567 MC_NAMEOP (rmdir, (const char *path), (vfs, mpath))
568 MC_NAMEOP (mknod, (const char *path, mode_t mode, dev_t dev), (vfs, mpath, mode, dev))
570 int
571 mc_symlink (const char *name1, const char *path)
573 struct vfs_class *vfs;
574 int result;
575 char *mpath;
576 char *lpath;
577 char *tmp;
579 mpath = vfs_canon_and_translate (path);
580 if (mpath != NULL) {
581 tmp = g_strdup (name1);
582 lpath = vfs_translate_path_n (tmp);
583 g_free (tmp);
585 if (lpath != NULL) {
586 vfs = vfs_get_class (mpath);
587 result = vfs->symlink ? (*vfs->symlink) (vfs, lpath, mpath) : -1;
588 g_free (lpath);
590 if (result == -1)
591 errno = vfs->symlink ? ferrno (vfs) : E_NOTSUPP;
592 return result;
594 g_free (mpath);
596 return -1;
599 #define MC_HANDLEOP(name, inarg, callarg) \
600 ssize_t mc_##name inarg \
602 struct vfs_class *vfs; \
603 int result; \
604 if (handle == -1) \
605 return -1; \
606 vfs = vfs_op (handle); \
607 result = vfs->name ? (*vfs->name)callarg : -1; \
608 if (result == -1) \
609 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
610 return result; \
613 MC_HANDLEOP(read, (int handle, void *buffer, int count), (vfs_info (handle), buffer, count))
614 MC_HANDLEOP(write, (int handle, const void *buf, int nbyte), (vfs_info (handle), buf, nbyte))
617 #define MC_RENAMEOP(name) \
618 int mc_##name (const char *fname1, const char *fname2) \
620 struct vfs_class *vfs; \
621 int result; \
622 char *name2, *name1; \
623 name1 = vfs_canon_and_translate (fname1); \
624 if (name1 != NULL) { \
625 name2 = vfs_canon_and_translate (fname2); \
626 if (name2 != NULL) { \
627 vfs = vfs_get_class (name1); \
628 if (vfs != vfs_get_class (name2)){ \
629 errno = EXDEV; \
630 g_free (name1); \
631 g_free (name2); \
632 return -1; \
634 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
635 g_free (name1); \
636 g_free (name2); \
637 if (result == -1) \
638 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
639 return result; \
640 } else { \
641 g_free (name1); \
642 return -1; \
644 } else return -1; \
647 MC_RENAMEOP (link)
648 MC_RENAMEOP (rename)
652 mc_ctl (int handle, int ctlop, void *arg)
654 struct vfs_class *vfs = vfs_op (handle);
656 return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
660 mc_setctl (const char *path, int ctlop, void *arg)
662 struct vfs_class *vfs;
663 int result;
664 char *mpath;
666 if (!path)
667 vfs_die("You don't want to pass NULL to mc_setctl.");
669 mpath = vfs_canon_and_translate (path);
670 if (mpath != NULL) {
671 vfs = vfs_get_class (mpath);
672 result = vfs->setctl ? (*vfs->setctl)(vfs, mpath, ctlop, arg) : 0;
673 g_free (mpath);
674 return result;
675 } else return -1;
679 mc_close (int handle)
681 struct vfs_class *vfs;
682 int result;
684 if (handle == -1 || !vfs_info (handle))
685 return -1;
687 vfs = vfs_op (handle);
688 if (handle < 3)
689 return close (handle);
691 if (!vfs->close)
692 vfs_die ("VFS must support close.\n");
693 result = (*vfs->close)(vfs_info (handle));
694 vfs_free_handle (handle);
695 if (result == -1)
696 errno = ferrno (vfs);
698 return result;
701 DIR *
702 mc_opendir (const char *dirname)
704 int handle, *handlep;
705 void *info;
706 struct vfs_class *vfs;
707 char *canon;
708 char *dname;
709 struct vfs_dirinfo *dirinfo;
710 const char *encoding;
712 canon = vfs_canon (dirname);
713 dname = vfs_translate_path_n (canon);
715 if (dname != NULL) {
716 vfs = vfs_get_class (dname);
717 info = vfs->opendir ? (*vfs->opendir)(vfs, dname) : NULL;
718 g_free (dname);
720 if (!info){
721 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
722 g_free (canon);
723 return NULL;
726 dirinfo = g_new (struct vfs_dirinfo, 1);
727 dirinfo->info = info;
729 encoding = vfs_get_encoding (canon);
730 g_free (canon);
731 dirinfo->converter = (encoding != NULL) ? str_crt_conv_from (encoding) :
732 str_cnv_from_term;
733 if (dirinfo->converter == INVALID_CONV) dirinfo->converter =str_cnv_from_term;
735 handle = vfs_new_handle (vfs, dirinfo);
737 handlep = g_new (int, 1);
738 *handlep = handle;
739 return (DIR *) handlep;
740 } else {
741 g_free (canon);
742 return NULL;
746 static struct dirent * mc_readdir_result = NULL;
748 struct dirent *
749 mc_readdir (DIR *dirp)
751 int handle;
752 struct vfs_class *vfs;
753 struct dirent *entry = NULL;
754 struct vfs_dirinfo *dirinfo;
755 estr_t state;
757 if (!mc_readdir_result)
759 /* We can't just allocate struct dirent as (see man dirent.h)
760 * struct dirent has VERY nonnaive semantics of allocating
761 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
762 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
763 * heap corrupter. So, allocate longliving dirent with at least
764 * (NAME_MAX + 1) for d_name in it.
765 * Strictly saying resulting dirent is unusable as we don't adjust internal
766 * structures, holding dirent size. But we don't use it in libc infrastructure.
767 * TODO: to make simpler homemade dirent-alike structure.
769 mc_readdir_result = (struct dirent *)malloc(sizeof(struct dirent *) + NAME_MAX + 1);
772 if (!dirp) {
773 errno = EFAULT;
774 return NULL;
776 handle = *(int *) dirp;
777 vfs = vfs_op (handle);
778 dirinfo = vfs_info (handle);
779 if (vfs->readdir) {
780 entry = (*vfs->readdir) (dirinfo->info);
781 if (entry == NULL) return NULL;
782 g_string_set_size(vfs_str_buffer,0);
783 state = str_vfs_convert_from (dirinfo->converter,
784 entry->d_name, vfs_str_buffer);
785 mc_readdir_result->d_ino = entry->d_ino;
786 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, NAME_MAX + 1);
788 if (entry == NULL) errno = vfs->readdir ? ferrno (vfs) : E_NOTSUPP;
789 return (entry != NULL) ? mc_readdir_result : NULL;
793 mc_closedir (DIR *dirp)
795 int handle = *(int *) dirp;
796 struct vfs_class *vfs = vfs_op (handle);
797 int result;
798 struct vfs_dirinfo *dirinfo;
800 dirinfo = vfs_info (handle);
801 if (dirinfo->converter != str_cnv_from_term) str_close_conv (dirinfo->converter);
803 result = vfs->closedir ? (*vfs->closedir)(dirinfo->info) : -1;
804 vfs_free_handle (handle);
805 g_free (dirinfo);
806 g_free (dirp);
807 return result;
810 int mc_stat (const char *filename, struct stat *buf) {
811 struct vfs_class *vfs;
812 int result;
813 char *path;
815 path = vfs_canon_and_translate (filename);
817 if (path != NULL) {
818 vfs = vfs_get_class (path);
820 result = vfs->stat ? (*vfs->stat) (vfs, path, buf) : -1;
822 g_free (path);
824 if (result == -1)
825 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
826 return result;
827 } else return -1;
830 int mc_lstat (const char *filename, struct stat *buf) {
831 struct vfs_class *vfs;
832 int result;
833 char *path;
835 path = vfs_canon_and_translate (filename);
837 if (path != NULL) {
838 vfs = vfs_get_class (path);
839 result = vfs->lstat ? (*vfs->lstat) (vfs, path, buf) : -1;
840 g_free (path);
841 if (result == -1)
842 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
843 return result;
844 } else return -1;
847 int mc_fstat (int handle, struct stat *buf) {
848 struct vfs_class *vfs;
849 int result;
851 if (handle == -1)
852 return -1;
853 vfs = vfs_op (handle);
854 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
855 if (result == -1)
856 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
857 return result;
861 * Return current directory. If it's local, reread the current directory
862 * from the OS. You must g_strdup() whatever this function returns.
864 static const char *
865 _vfs_get_cwd (void)
867 char *sys_cwd;
868 char *trans;
869 const char *encoding;
870 char *tmp;
871 estr_t state;
872 struct stat my_stat, my_stat2;
874 trans = vfs_translate_path_n (current_dir); /* add check if NULL */
876 if (!_vfs_get_class (trans)) {
877 encoding = vfs_get_encoding (current_dir);
878 if (encoding == NULL) {
879 tmp = g_get_current_dir ();
880 if (tmp != NULL) { /* One of the directories in the path is not readable */
881 g_string_set_size(vfs_str_buffer,0);
882 state = str_vfs_convert_from (str_cnv_from_term, tmp, vfs_str_buffer);
883 g_free (tmp);
884 sys_cwd = (state == ESTR_SUCCESS) ? g_strdup (vfs_str_buffer->str) : NULL;
885 if (!sys_cwd)
886 return current_dir;
888 /* Otherwise check if it is O.K. to use the current_dir */
889 if (!cd_symlinks || mc_stat (sys_cwd, &my_stat)
890 || mc_stat (current_dir, &my_stat2)
891 || my_stat.st_ino != my_stat2.st_ino
892 || my_stat.st_dev != my_stat2.st_dev) {
893 g_free (current_dir);
894 current_dir = sys_cwd;
895 return sys_cwd;
896 }/* Otherwise we return current_dir below */
900 g_free (trans);
901 return current_dir;
904 static void
905 vfs_setup_wd (void)
907 current_dir = g_strdup (PATH_SEP_STR);
908 _vfs_get_cwd ();
910 if (strlen (current_dir) > MC_MAXPATHLEN - 2)
911 vfs_die ("Current dir too long.\n");
913 current_vfs = vfs_get_class (current_dir);
917 * Return current directory. If it's local, reread the current directory
918 * from the OS. Put directory to the provided buffer.
920 char *
921 mc_get_current_wd (char *buffer, int size)
923 const char *cwd = _vfs_get_cwd ();
925 g_strlcpy (buffer, cwd, size);
926 return buffer;
930 * Return current directory without any OS calls.
932 char *
933 vfs_get_current_dir (void)
935 return current_dir;
938 off_t mc_lseek (int fd, off_t offset, int whence)
940 struct vfs_class *vfs;
941 int result;
943 if (fd == -1)
944 return -1;
946 vfs = vfs_op (fd);
947 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
948 if (result == -1)
949 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
950 return result;
954 * remove //, /./ and /../
957 #define ISSLASH(a) (!a || (a == '/'))
959 char *
960 vfs_canon (const char *path)
962 if (!path)
963 vfs_die("Cannot canonicalize NULL");
965 /* Relative to current directory */
966 if (*path != PATH_SEP){
967 char *local, *result;
969 local = concat_dir_and_file (current_dir, path);
971 result = vfs_canon (local);
972 g_free (local);
973 return result;
977 * So we have path of following form:
978 * /p1/p2#op/.././././p3#op/p4. Good luck.
981 char *result = g_strdup (path);
982 canonicalize_pathname (result);
983 return result;
988 * VFS chdir.
989 * Return 0 on success, -1 on failure.
992 mc_chdir (const char *path)
994 char *new_dir;
995 char *trans_dir;
996 struct vfs_class *old_vfs, *new_vfs;
997 vfsid old_vfsid;
998 int result;
1000 new_dir = vfs_canon (path);
1001 trans_dir = vfs_translate_path_n (new_dir);
1002 if (trans_dir != NULL) {
1003 new_vfs = vfs_get_class (trans_dir);
1004 if (!new_vfs->chdir) {
1005 g_free (new_dir);
1006 g_free (trans_dir);
1007 return -1;
1010 result = (*new_vfs->chdir) (new_vfs, trans_dir);
1012 if (result == -1) {
1013 errno = ferrno (new_vfs);
1014 g_free (new_dir);
1015 g_free (trans_dir);
1016 return -1;
1019 old_vfsid = vfs_getid (current_vfs, current_dir);
1020 old_vfs = current_vfs;
1022 /* Actually change directory */
1023 g_free (current_dir);
1024 current_dir = new_dir;
1025 current_vfs = new_vfs;
1027 /* This function uses the new current_dir implicitly */
1028 vfs_stamp_create (old_vfs, old_vfsid);
1030 /* Sometimes we assume no trailing slash on cwd */
1031 if (*current_dir) {
1032 char *p;
1033 p = strchr (current_dir, 0) - 1;
1034 if (*p == PATH_SEP && p > current_dir)
1035 *p = 0;
1038 g_free (trans_dir);
1039 return 0;
1040 } else {
1041 g_free (new_dir);
1042 return -1;
1046 /* Return 1 is the current VFS class is local */
1048 vfs_current_is_local (void)
1050 return (current_vfs->flags & VFSF_LOCAL) != 0;
1053 /* Return flags of the VFS class of the given filename */
1055 vfs_file_class_flags (const char *filename)
1057 struct vfs_class *vfs;
1058 char *fname;
1060 fname = vfs_canon_and_translate (filename);
1061 if (fname != NULL) {
1062 vfs = vfs_get_class (fname);
1063 g_free (fname);
1064 return vfs->flags;
1065 } else return -1;
1068 static char *
1069 mc_def_getlocalcopy (const char *filename)
1071 char *tmp;
1072 int fdin, fdout, i;
1073 char buffer[8192];
1074 struct stat mystat;
1076 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
1077 if (fdin == -1)
1078 return NULL;
1080 fdout = vfs_mkstemps (&tmp, "vfs", filename);
1082 if (fdout == -1)
1083 goto fail;
1084 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0) {
1085 if (write (fdout, buffer, i) != i)
1086 goto fail;
1088 if (i == -1)
1089 goto fail;
1090 i = mc_close (fdin);
1091 fdin = -1;
1092 if (i == -1)
1093 goto fail;
1094 if (close (fdout) == -1) {
1095 fdout = -1;
1096 goto fail;
1099 if (mc_stat (filename, &mystat) != -1) {
1100 chmod (tmp, mystat.st_mode);
1102 return tmp;
1104 fail:
1105 if (fdout != -1)
1106 close (fdout);
1107 if (fdin != -1)
1108 mc_close (fdin);
1109 g_free (tmp);
1110 return NULL;
1113 char *
1114 mc_getlocalcopy (const char *pathname)
1116 char *result;
1117 char *path;
1119 path = vfs_canon_and_translate (pathname);
1120 if (path != NULL) {
1121 struct vfs_class *vfs = vfs_get_class (path);
1123 result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, path) :
1124 mc_def_getlocalcopy (path);
1125 g_free (path);
1126 if (!result)
1127 errno = ferrno (vfs);
1128 return result;
1129 } else return NULL;
1132 static int
1133 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
1134 const char *local, int has_changed)
1136 int fdin = -1, fdout = -1, i;
1137 if (has_changed) {
1138 char buffer[8192];
1140 if (!vfs->write)
1141 goto failed;
1143 fdin = open (local, O_RDONLY);
1144 if (fdin == -1)
1145 goto failed;
1146 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1147 if (fdout == -1)
1148 goto failed;
1149 while ((i = read (fdin, buffer, sizeof (buffer))) > 0) {
1150 if (mc_write (fdout, buffer, i) != i)
1151 goto failed;
1153 if (i == -1)
1154 goto failed;
1156 if (close (fdin) == -1) {
1157 fdin = -1;
1158 goto failed;
1160 fdin = -1;
1161 if (mc_close (fdout) == -1) {
1162 fdout = -1;
1163 goto failed;
1166 unlink (local);
1167 return 0;
1169 failed:
1170 message (D_ERROR, _("Changes to file lost"), "%s", filename);
1171 if (fdout != -1)
1172 mc_close (fdout);
1173 if (fdin != -1)
1174 close (fdin);
1175 unlink (local);
1176 return -1;
1180 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
1182 int return_value = 0;
1183 char *path;
1185 path = vfs_canon_and_translate (pathname);
1186 if (path != NULL) {
1187 struct vfs_class *vfs = vfs_get_class (path);
1189 return_value = vfs->ungetlocalcopy ?
1190 (*vfs->ungetlocalcopy)(vfs, path, local, has_changed) :
1191 mc_def_ungetlocalcopy (vfs, path, local, has_changed);
1192 g_free (path);
1193 return return_value;
1194 } else return -1;
1198 void
1199 vfs_init (void)
1201 vfs_str_buffer = g_string_new("");
1202 /* localfs needs to be the first one */
1203 init_localfs();
1204 /* fallback value for vfs_get_class() */
1205 localfs_class = vfs_list;
1207 init_extfs ();
1208 init_sfs ();
1209 init_tarfs ();
1210 init_cpiofs ();
1212 #ifdef USE_EXT2FSLIB
1213 init_undelfs ();
1214 #endif /* USE_EXT2FSLIB */
1216 #ifdef USE_NETCODE
1217 tcp_init();
1218 init_ftpfs ();
1219 init_fish ();
1220 #ifdef WITH_SMBFS
1221 init_smbfs ();
1222 #endif /* WITH_SMBFS */
1223 #ifdef WITH_MCFS
1224 init_mcfs ();
1225 #endif /* WITH_MCFS */
1226 #endif /* USE_NETCODE */
1228 vfs_setup_wd ();
1231 void
1232 vfs_shut (void)
1234 struct vfs_class *vfs;
1236 vfs_gc_done ();
1238 g_free (current_dir);
1240 for (vfs = vfs_list; vfs; vfs = vfs->next)
1241 if (vfs->done)
1242 (*vfs->done) (vfs);
1244 g_slist_free (vfs_openfiles);
1246 g_string_free (vfs_str_buffer, TRUE);
1250 * These ones grab information from the VFS
1251 * and handles them to an upper layer
1253 void
1254 vfs_fill_names (fill_names_f func)
1256 struct vfs_class *vfs;
1258 for (vfs=vfs_list; vfs; vfs=vfs->next)
1259 if (vfs->fill_names)
1260 (*vfs->fill_names) (vfs, func);
1264 * Returns vfs path corresponding to given url. If passed string is
1265 * not recognized as url, g_strdup(url) is returned.
1268 static const struct {
1269 const char *name;
1270 size_t name_len;
1271 const char *substitute;
1272 } url_table[] = { {"ftp://", 6, "/#ftp:"},
1273 {"mc://", 5, "/#mc:"},
1274 {"smb://", 6, "/#smb:"},
1275 {"sh://", 5, "/#sh:"},
1276 {"ssh://", 6, "/#sh:"},
1277 {"a:", 2, "/#a"}
1280 char *
1281 vfs_translate_url (const char *url)
1283 size_t i;
1285 for (i = 0; i < sizeof (url_table)/sizeof (url_table[0]); i++)
1286 if (strncmp (url, url_table[i].name, url_table[i].name_len) == 0)
1287 return g_strconcat (url_table[i].substitute, url + url_table[i].name_len, (char*) NULL);
1289 return g_strdup (url);
1292 int vfs_file_is_local (const char *filename)
1294 return vfs_file_class_flags (filename) & VFSF_LOCAL;