1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* Warning: funtions like extfs_lstat() have right to destroy any
23 * strings you pass to them. This is acutally ok as you g_strdup what
24 * you are passing to them, anyway; still, beware. */
26 /* Namespace: exports *many* functions with vfs_ prefix; exports
27 parse_ls_lga and friends which do not have that prefix. */
36 #include <stdlib.h> /* For atol() */
40 #include <sys/types.h>
45 #include "../src/panel.h" /* get_current_panel() */
46 #include "../src/layout.h" /* get_current_type() */
47 #include "../src/wtools.h" /* input_dialog() */
49 #include "xdirentry.h"
51 #include "extfs.h" /* FIXME: we should not know anything about our modules */
57 int vfs_timeout
= 60; /* VFS timeout in seconds */
58 static int vfs_flags
= 0; /* Flags */
60 /* They keep track of the current directory */
61 static vfs
*current_vfs
= &vfs_local_ops
;
62 static char *current_dir
= NULL
;
65 * FIXME: this is broken. It depends on mc not crossing border on month!
67 static int current_mday
;
68 static int current_mon
;
69 static int current_year
;
71 /* FIXME: Open files managed by the vfs layer, should be dynamical */
72 #define MAX_VFS_FILES 100
77 } vfs_file_table
[MAX_VFS_FILES
];
84 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
85 for (i
= 3; i
< MAX_VFS_FILES
; i
++){
86 if (!vfs_file_table
[i
].fs_info
)
90 vfs_die ("No more virtual file handles");
94 /* vfs_local_ops needs to be the first one */
95 static vfs
*vfs_list
= &vfs_local_ops
;
98 vfs_register (vfs
*vfs
)
101 vfs_die("You cannot register NULL.");
103 if (vfs
->init
) /* vfs has own initialization function */
104 if (!(*vfs
->init
)(vfs
)) /* but it failed */
107 vfs
->next
= vfs_list
;
114 vfs_type_from_op (char *path
)
119 vfs_die ("vfs_type_from_op got NULL: impossible");
121 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
123 if ((*vfs
->which
) (vfs
, path
) == -1)
127 if (!strncmp (path
, vfs
->prefix
, strlen (vfs
->prefix
)))
130 return NULL
; /* shut up stupid gcc */
133 /* Strip known vfs suffixes from a filename (possible improvement: strip
134 suffix from last path component).
135 Returns a malloced string which has to be freed. */
137 vfs_strip_suffix_from_filename (const char *filename
)
144 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
146 p
= g_strdup (filename
);
147 if (!(semi
= strrchr (p
, '#')))
150 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
152 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
154 *semi
= '\0'; /* Found valid suffix */
157 if (!strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
158 *semi
= '\0'; /* Found valid suffix */
166 path_magic (const char *path
)
170 if (vfs_flags
& FL_ALWAYS_MAGIC
)
173 if (!stat(path
, &buf
))
180 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
181 * What is left in path is p1. You still want to g_free(path), you DON'T
182 * want to free neither *inpath nor *op
185 vfs_split (char *path
, char **inpath
, char **op
)
192 vfs_die("Cannot split NULL");
194 semi
= strrchr (path
, '#');
195 if (!semi
|| !path_magic(path
))
198 slash
= strchr (semi
, PATH_SEP
);
210 if ((ret
= vfs_type_from_op (semi
+1))){
214 *inpath
= slash
? slash
+ 1 : NULL
;
221 ret
= vfs_split (path
, inpath
, op
);
227 vfs_rosplit (char *path
)
233 g_return_val_if_fail(path
, NULL
);
235 semi
= strrchr (path
, '#');
236 if (!semi
|| !path_magic (path
))
239 slash
= strchr (semi
, PATH_SEP
);
244 ret
= vfs_type_from_op (semi
+1);
245 if (!ret
&& (vfs_flags
& FL_NO_LOCALHASH
))
251 ret
= vfs_rosplit (path
);
258 vfs_type (char *path
)
262 vfs
= vfs_rosplit(path
);
265 vfs
= &vfs_local_ops
;
270 static struct vfs_stamping
*stamps
;
273 * Returns the number of seconds remaining to the vfs timeout
275 * FIXME: currently this is set to 10 seconds. We should compute this.
280 return stamps
? 10 : 0;
284 vfs_addstamp (vfs
*v
, vfsid id
, struct vfs_stamping
*parent
)
286 if (v
!= &vfs_local_ops
&& id
!= (vfsid
)-1){
287 struct vfs_stamping
*stamp
;
288 struct vfs_stamping
*last_stamp
= NULL
;
290 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
) {
291 if (stamp
->v
== v
&& stamp
->id
== id
){
292 gettimeofday(&(stamp
->time
), NULL
);
297 stamp
= g_new (struct vfs_stamping
, 1);
301 struct vfs_stamping
*st
= stamp
;
303 st
->parent
= g_new (struct vfs_stamping
, 1);
304 *st
->parent
= *parent
;
305 parent
= parent
->parent
;
313 gettimeofday (&(stamp
->time
), NULL
);
318 last_stamp
->next
= stamp
;
320 /* Add first element */
327 vfs_stamp (vfs
*v
, vfsid id
)
329 struct vfs_stamping
*stamp
;
331 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
)
332 if (stamp
->v
== v
&& stamp
->id
== id
){
334 gettimeofday (&(stamp
->time
), NULL
);
335 if (stamp
->parent
!= NULL
)
336 vfs_stamp (stamp
->parent
->v
, stamp
->parent
->id
);
343 vfs_rm_parents (struct vfs_stamping
*stamp
)
345 struct vfs_stamping
*parent
;
348 parent
= stamp
->parent
;
355 vfs_rmstamp (vfs
*v
, vfsid id
, int removeparents
)
357 struct vfs_stamping
*stamp
, *st1
;
359 for (stamp
= stamps
, st1
= NULL
; stamp
!= NULL
; st1
= stamp
, stamp
= stamp
->next
)
360 if (stamp
->v
== v
&& stamp
->id
== id
){
361 if (stamp
->parent
!= NULL
){
363 vfs_rmstamp (stamp
->parent
->v
, stamp
->parent
->id
, 1);
364 vfs_rm_parents (stamp
->parent
);
367 stamps
= stamp
->next
;
369 st1
->next
= stamp
->next
;
380 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
381 /* Hope that error message is obscure enough ;-) */
385 mc_open (const char *filename
, int flags
, ...)
392 char *file
= vfs_canon (filename
);
393 vfs
*vfs
= vfs_type (file
);
395 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
396 va_start (ap
, flags
);
397 mode
= va_arg (ap
, int);
405 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
408 errno
= ferrno (vfs
);
411 handle
= get_bucket ();
412 vfs_file_table
[handle
].fs_info
= info
;
413 vfs_file_table
[handle
].operations
= vfs
;
418 #define vfs_op(handle) vfs_file_table [handle].operations
419 #define vfs_info(handle) vfs_file_table [handle].fs_info
420 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
422 #define MC_OP(name, inarg, callarg, pre, post) \
423 int mc_##name inarg \
429 result = vfs->name ? (*vfs->name)callarg : -1; \
432 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
436 #define MC_NAMEOP(name, inarg, callarg) \
437 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
438 #define MC_HANDLEOP(name, inarg, callarg) \
439 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, ;)
441 MC_HANDLEOP(read
, (int handle
, char *buffer
, int count
), (vfs_info (handle
), buffer
, count
) )
444 mc_ctl (int handle
, int ctlop
, int arg
)
446 vfs
*vfs
= vfs_op (handle
);
448 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
452 mc_setctl (char *path
, int ctlop
, char *arg
)
458 vfs_die("You don't want to pass NULL to mc_setctl.");
460 path
= vfs_canon (path
);
461 vfs
= vfs_type (path
);
462 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, path
, ctlop
, arg
) : 0;
468 mc_close (int handle
)
473 if (handle
== -1 || !vfs_info (handle
))
476 vfs
= vfs_op (handle
);
478 return close (handle
);
481 vfs_die ("VFS must support close.\n");
482 result
= (*vfs
->close
)(vfs_info (handle
));
483 vfs_free_bucket (handle
);
485 errno
= ferrno (vfs
);
491 mc_opendir (char *dirname
)
493 int handle
, *handlep
;
497 dirname
= vfs_canon (dirname
);
498 vfs
= vfs_type (dirname
);
500 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dirname
) : NULL
;
503 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
506 handle
= get_bucket ();
507 vfs_file_table
[handle
].fs_info
= info
;
508 vfs_file_table
[handle
].operations
= vfs
;
510 handlep
= g_new (int, 1);
512 return (DIR *) handlep
;
516 mc_seekdir (DIR *dirp
, int offset
)
525 handle
= *(int *) dirp
;
526 vfs
= vfs_op (handle
);
528 (*vfs
->seekdir
) (vfs_info (handle
), offset
);
533 #define MC_DIROP(name, type, onerr ) \
534 type mc_##name (DIR *dirp) \
544 handle = *(int *) dirp; \
545 vfs = vfs_op (handle); \
546 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
547 if (result == onerr) \
548 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
552 MC_DIROP (readdir
, struct dirent
*, NULL
)
553 MC_DIROP (telldir
, int, -1)
556 mc_closedir (DIR *dirp
)
558 int handle
= *(int *) dirp
;
559 vfs
*vfs
= vfs_op (handle
);
562 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
563 vfs_free_bucket (handle
);
568 int mc_stat (const char *filename
, struct stat
*buf
) {
572 path
= vfs_canon (filename
); vfs
= vfs_type (path
);
573 result
= vfs
->stat
? (*vfs
->stat
) (vfs
, path
, buf
) : -1;
576 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
580 int mc_lstat (const char *filename
, struct stat
*buf
) {
584 path
= vfs_canon (filename
); vfs
= vfs_type (path
);
585 result
= vfs
->lstat
? (*vfs
->lstat
) (vfs
, path
, buf
) : -1;
588 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
592 int mc_fstat (int handle
, struct stat
*buf
) {
598 vfs
= vfs_op (handle
);
599 result
= vfs
->fstat
? (*vfs
->fstat
) (vfs_info (handle
), buf
) : -1;
601 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
606 * You must g_strdup whatever this function returns.
613 struct stat my_stat
, my_stat2
;
615 if (!vfs_rosplit (current_dir
)){
616 p
= g_get_current_dir ();
617 if (!p
) /* One of the directories in the path is not readable */
620 /* Otherwise check if it is O.K. to use the current_dir */
622 mc_stat (p
, &my_stat
) ||
623 mc_stat (current_dir
, &my_stat2
) ||
624 my_stat
.st_ino
!= my_stat2
.st_ino
||
625 my_stat
.st_dev
!= my_stat2
.st_dev
){
626 g_free (current_dir
);
629 } /* Otherwise we return current_dir below */
636 mc_get_current_wd (char *buffer
, int size
)
638 const char *cwd
= mc_return_cwd();
640 strncpy (buffer
, cwd
, size
- 1);
641 buffer
[size
- 1] = 0;
645 MC_NAMEOP (chmod
, (char *path
, int mode
), (vfs
, path
, mode
))
646 MC_NAMEOP (chown
, (char *path
, int owner
, int group
), (vfs
, path
, owner
, group
))
647 MC_NAMEOP (utime
, (char *path
, struct utimbuf
*times
), (vfs
, path
, times
))
648 MC_NAMEOP (readlink
, (char *path
, char *buf
, int bufsiz
), (vfs
, path
, buf
, bufsiz
))
649 MC_NAMEOP (unlink
, (char *path
), (vfs
, path
))
650 MC_NAMEOP (symlink
, (char *name1
, char *path
), (vfs
, name1
, path
))
652 #define MC_RENAMEOP(name) \
653 int mc_##name (const char *fname1, const char *fname2) \
658 char *name2, *name1 = vfs_canon (fname1); \
659 vfs = vfs_type (name1); \
660 name2 = vfs_canon (fname2); \
661 if (vfs != vfs_type (name2)){ \
668 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
672 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
679 MC_HANDLEOP (write
, (int handle
, char *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
681 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
690 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
692 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
697 * remove //, /./ and /../, local should point to big enough buffer
700 #define ISSLASH(a) (!a || (a == '/'))
703 vfs_canon (const char *path
)
706 vfs_die("Cannot canonicalize NULL");
708 /* Tilde expansion */
710 char *local
, *result
;
712 local
= tilde_expand (path
);
714 result
= vfs_canon (local
);
720 /* Relative to current directory */
721 if (*path
!= PATH_SEP
){
722 char *local
, *result
;
724 local
= concat_dir_and_file (current_dir
, path
);
726 result
= vfs_canon (local
);
732 * So we have path of following form:
733 * /p1/p2#op/.././././p3#op/p4. Good luck.
736 char *result
= g_strdup (path
);
737 canonicalize_pathname (result
);
743 vfs_ncs_getid (vfs
*nvfs
, char *dir
, struct vfs_stamping
**par
)
747 dir
= concat_dir_and_file (dir
, "");
749 nvfsid
= (*nvfs
->getid
)(nvfs
, dir
, par
);
756 is_parent (vfs
* nvfs
, vfsid nvfsid
, struct vfs_stamping
*parent
)
758 struct vfs_stamping
*stamp
;
760 for (stamp
= parent
; stamp
; stamp
= stamp
->parent
)
761 if (stamp
->v
== nvfs
&& stamp
->id
== nvfsid
)
764 return (stamp
? 1 : 0);
768 vfs_add_noncurrent_stamps (vfs
* oldvfs
, vfsid oldvfsid
, struct vfs_stamping
*parent
)
770 vfs
*nvfs
, *n2vfs
, *n3vfs
;
771 vfsid nvfsid
, n2vfsid
, n3vfsid
;
772 struct vfs_stamping
*par
, *stamp
;
775 /* FIXME: As soon as we convert to multiple panels, this stuff
776 has to change. It works like this: We do not time out the
777 vfs's which are current in any panel and on the other
778 side we add the old directory with all its parents which
779 are not in any panel (if we find such one, we stop adding
780 parents to the time-outing structure. */
782 /* There are three directories we have to take care of: current_dir,
783 cpanel->cwd and opanel->cwd. Athough most of the time either
784 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
785 same, it's possible that all three are different -- Norbert */
790 nvfs
= vfs_type (current_dir
);
791 nvfsid
= vfs_ncs_getid (nvfs
, current_dir
, &par
);
792 vfs_rmstamp (nvfs
, nvfsid
, 1);
794 f
= is_parent (oldvfs
, oldvfsid
, par
);
795 vfs_rm_parents (par
);
796 if ((nvfs
== oldvfs
&& nvfsid
== oldvfsid
) || oldvfsid
== (vfsid
*)-1 || f
){
800 if (get_current_type () == view_listing
){
801 n2vfs
= vfs_type (cpanel
->cwd
);
802 n2vfsid
= vfs_ncs_getid (n2vfs
, cpanel
->cwd
, &par
);
803 f
= is_parent (oldvfs
, oldvfsid
, par
);
804 vfs_rm_parents (par
);
805 if ((n2vfs
== oldvfs
&& n2vfsid
== oldvfsid
) || f
)
809 n2vfsid
= (vfs
*) -1;
812 if (get_other_type () == view_listing
){
813 n3vfs
= vfs_type (opanel
->cwd
);
814 n3vfsid
= vfs_ncs_getid (n3vfs
, opanel
->cwd
, &par
);
815 f
= is_parent (oldvfs
, oldvfsid
, par
);
816 vfs_rm_parents (par
);
817 if ((n3vfs
== oldvfs
&& n3vfsid
== oldvfsid
) || f
)
824 if ((*oldvfs
->nothingisopen
) (oldvfsid
)){
825 if (oldvfs
== &vfs_extfs_ops
&& ((extfs_archive
*) oldvfsid
)->name
== 0){
826 /* Free the resources immediatly when we leave a mtools fs
827 ('cd a:') instead of waiting for the vfs-timeout */
828 (oldvfs
->free
) (oldvfsid
);
830 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
831 for (stamp
= parent
; stamp
!= NULL
; stamp
= stamp
->parent
){
832 if ((stamp
->v
== nvfs
&& stamp
->id
== nvfsid
) ||
833 (stamp
->v
== n2vfs
&& stamp
->id
== n2vfsid
) ||
834 (stamp
->v
== n3vfs
&& stamp
->id
== n3vfsid
) ||
835 stamp
->id
== (vfsid
) - 1 ||
836 !(*stamp
->v
->nothingisopen
) (stamp
->id
))
838 if (stamp
->v
== &vfs_extfs_ops
&& ((extfs_archive
*) stamp
->id
)->name
== 0){
839 (stamp
->v
->free
) (stamp
->id
);
840 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
842 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
848 vfs_stamp_path (char *path
)
852 struct vfs_stamping
*par
, *stamp
;
854 vfs
= vfs_type (path
);
855 id
= vfs_ncs_getid (vfs
, path
, &par
);
856 vfs_addstamp (vfs
, id
, par
);
858 for (stamp
= par
; stamp
!= NULL
; stamp
= stamp
->parent
)
859 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
860 vfs_rm_parents (par
);
864 vfs_add_current_stamps (void)
866 vfs_stamp_path (current_dir
);
869 if (get_current_type () == view_listing
)
870 vfs_stamp_path (cpanel
->cwd
);
874 if (get_other_type () == view_listing
)
875 vfs_stamp_path (opanel
->cwd
);
881 * Return 0 on success, -1 on failure.
884 mc_chdir (char *path
)
886 char *new_dir
, *new_dir_copy
;
887 vfs
*old_vfs
, *new_vfs
;
889 struct vfs_stamping
*parent
;
892 new_dir
= vfs_canon (path
);
893 new_vfs
= vfs_type (new_dir
);
895 vfs_die ("No chdir function defined");
897 /* new_vfs->chdir can write to the second argument, use a copy */
898 new_dir_copy
= g_strdup (new_dir
);
899 result
= (*new_vfs
->chdir
) (new_vfs
, new_dir_copy
);
900 g_free (new_dir_copy
);
903 errno
= ferrno (new_vfs
);
908 old_vfsid
= vfs_ncs_getid (current_vfs
, current_dir
, &parent
);
909 old_vfs
= current_vfs
;
911 /* Actually change directory */
912 g_free (current_dir
);
913 current_dir
= new_dir
;
914 current_vfs
= new_vfs
;
916 /* This function uses the new current_dir implicitly */
917 vfs_add_noncurrent_stamps (old_vfs
, old_vfsid
, parent
);
918 vfs_rm_parents (parent
);
920 /* Sometimes we assume no trailing slash on cwd */
923 p
= strchr (current_dir
, 0) - 1;
924 if (*p
== PATH_SEP
&& p
> current_dir
)
932 vfs_current_is_local (void)
934 return current_vfs
== &vfs_local_ops
;
938 vfs_file_is_local (const char *file
)
940 char *filename
= vfs_canon (file
);
941 vfs
*vfs
= vfs_type (filename
);
944 return vfs
== &vfs_local_ops
;
948 vfs_file_is_ftp (char *filename
)
953 filename
= vfs_canon (filename
);
954 vfs
= vfs_type (filename
);
956 return vfs
== &vfs_ftpfs_ops
;
963 vfs_file_is_smb (char *filename
)
969 filename
= vfs_canon (filename
);
970 vfs
= vfs_type (filename
);
972 return vfs
== &vfs_smbfs_ops
;
973 #endif /* USE_NETCODE */
974 #endif /* WITH_SMBFS */
978 char *vfs_get_current_dir (void)
983 static void vfs_setup_wd (void)
985 current_dir
= g_strdup (PATH_SEP_STR
);
986 if (!(vfs_flags
& FL_NO_CWDSETUP
))
989 if (strlen(current_dir
)>MC_MAXPATHLEN
-2)
990 vfs_die ("Current dir too long.\n");
993 MC_NAMEOP (mkdir
, (char *path
, mode_t mode
), (vfs
, path
, mode
))
994 MC_NAMEOP (rmdir
, (char *path
), (vfs
, path
))
995 MC_NAMEOP (mknod
, (char *path
, int mode
, int dev
), (vfs
, path
, mode
, dev
))
998 static struct mc_mmapping
{
1002 struct mc_mmapping
*next
;
1003 } *mc_mmaparray
= NULL
;
1006 mc_mmap (caddr_t addr
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
1010 struct mc_mmapping
*mcm
;
1013 return (caddr_t
) -1;
1016 result
= vfs
->mmap
? (*vfs
->mmap
)(vfs
, addr
, len
, prot
, flags
, vfs_info (fd
), offset
) : (caddr_t
)-1;
1017 if (result
== (caddr_t
)-1){
1018 errno
= ferrno (vfs
);
1021 mcm
=g_new (struct mc_mmapping
, 1);
1023 mcm
->vfs_info
= vfs_info (fd
);
1025 mcm
->next
= mc_mmaparray
;
1031 mc_munmap (caddr_t addr
, size_t len
)
1033 struct mc_mmapping
*mcm
, *mcm2
= NULL
;
1035 for (mcm
= mc_mmaparray
; mcm
!= NULL
; mcm2
= mcm
, mcm
= mcm
->next
){
1036 if (mcm
->addr
== addr
){
1038 mc_mmaparray
= mcm
->next
;
1040 mcm2
->next
= mcm
->next
;
1041 if (mcm
->vfs
->munmap
)
1042 (*mcm
->vfs
->munmap
)(mcm
->vfs
, addr
, len
, mcm
->vfs_info
);
1053 mc_def_getlocalcopy (vfs
*vfs
, char *filename
)
1062 fdin
= mc_open (filename
, O_RDONLY
);
1066 /* Try to preserve existing extension */
1067 for (ptr
= filename
+ strlen(filename
) - 1; ptr
>= filename
; ptr
--) {
1073 if (!isalnum((unsigned char) *ptr
))
1077 fdout
= mc_mkstemps (&tmp
, "mclocalcopy", ext
);
1080 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0){
1081 if (write (fdout
, buffer
, i
) != i
)
1086 i
= mc_close (fdin
);
1090 if (close (fdout
)==-1)
1093 if (mc_stat (filename
, &mystat
) != -1){
1094 chmod (tmp
, mystat
.st_mode
);
1099 if (fdout
) close(fdout
);
1100 if (fdin
) mc_close (fdin
);
1106 mc_getlocalcopy (const char *pathname
)
1109 char *path
= vfs_canon (pathname
);
1110 vfs
*vfs
= vfs_type (path
);
1112 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, path
) :
1113 mc_def_getlocalcopy (vfs
, path
);
1116 errno
= ferrno (vfs
);
1121 mc_def_ungetlocalcopy (vfs
*vfs
, char *filename
, char *local
, int has_changed
)
1122 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1123 int fdin
= -1, fdout
= -1, i
;
1127 fdin
= open (local
, O_RDONLY
);
1130 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
1133 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0){
1134 if (mc_write (fdout
, buffer
, i
) != i
)
1140 if (close (fdin
)==-1) {
1145 if (mc_close (fdout
)==-1) {
1155 message_1s (1, _("Changes to file lost"), filename
);
1156 if (fdout
!=-1) mc_close(fdout
);
1157 if (fdin
!=-1) close(fdin
);
1164 mc_ungetlocalcopy (const char *pathname
, char *local
, int has_changed
)
1166 int return_value
= 0;
1167 char *path
= vfs_canon (pathname
);
1168 vfs
*vfs
= vfs_type (path
);
1170 return_value
= vfs
->ungetlocalcopy
?
1171 (*vfs
->ungetlocalcopy
)(vfs
, path
, local
, has_changed
) :
1172 mc_def_ungetlocalcopy (vfs
, path
, local
, has_changed
);
1174 return return_value
;
1178 * Hmm, as timeout is minute or so, do we need to care about usecs?
1181 timeoutcmp (struct timeval
*t1
, struct timeval
*t2
)
1183 return ((t1
->tv_sec
< t2
->tv_sec
)
1184 || ((t1
->tv_sec
== t2
->tv_sec
) && (t1
->tv_usec
<= t2
->tv_usec
)));
1187 /* This is called from timeout handler with now = 0, or can be called
1188 with now = 1 to force freeing all filesystems that are not in use */
1191 vfs_expire (int now
)
1193 static int locked
= 0;
1194 struct timeval time
;
1195 struct vfs_stamping
*stamp
, *st
;
1197 /* Avoid recursive invocation, e.g. when one of the free functions
1203 gettimeofday (&time
, NULL
);
1204 time
.tv_sec
-= vfs_timeout
;
1206 for (stamp
= stamps
; stamp
!= NULL
;){
1207 if (now
|| (timeoutcmp (&stamp
->time
, &time
))){
1209 (*stamp
->v
->free
) (stamp
->id
);
1210 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
1213 stamp
= stamp
->next
;
1219 vfs_timeout_handler (void)
1227 time_t current_time
;
1230 memset (vfs_file_table
, 0, sizeof (vfs_file_table
));
1231 current_time
= time (NULL
);
1232 t
= localtime (¤t_time
);
1233 current_mday
= t
->tm_mday
;
1234 current_mon
= t
->tm_mon
;
1235 current_year
= t
->tm_year
;
1237 /* We do not want to register vfs_local_ops */
1241 vfs_register (&vfs_ftpfs_ops
);
1242 vfs_register (&vfs_fish_ops
);
1244 vfs_register (&vfs_smbfs_ops
);
1245 #endif /* WITH_SMBFS */
1247 vfs_register (&vfs_mcfs_ops
);
1248 #endif /* WITH_SMBFS */
1249 #endif /* USE_NETCODE */
1251 vfs_register (&vfs_extfs_ops
);
1252 vfs_register (&vfs_sfs_ops
);
1253 vfs_register (&vfs_tarfs_ops
);
1254 vfs_register (&vfs_cpiofs_ops
);
1256 #ifdef USE_EXT2FSLIB
1257 vfs_register (&vfs_undelfs_ops
);
1258 #endif /* USE_EXT2FSLIB */
1266 struct vfs_stamping
*stamp
, *st
;
1269 for (stamp
= stamps
, stamps
= 0; stamp
!= NULL
;){
1270 (*stamp
->v
->free
)(stamp
->id
);
1277 vfs_rmstamp (stamps
->v
, stamps
->id
, 1);
1280 g_free (current_dir
);
1282 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1288 * These ones grab information from the VFS
1289 * and handles them to an upper layer
1292 vfs_fill_names (void (*func
)(char *))
1296 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1297 if (vfs
->fill_names
)
1298 (*vfs
->fill_names
) (vfs
, func
);
1301 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1304 static char *columns
[MAXCOLS
]; /* Points to the string in column n */
1305 static int column_ptr
[MAXCOLS
]; /* Index from 0 to the starting positions of the columns */
1308 vfs_split_text (char *p
)
1313 memset (columns
, 0, sizeof (columns
));
1315 for (numcols
= 0; *p
&& numcols
< MAXCOLS
; numcols
++){
1316 while (*p
== ' ' || *p
== '\r' || *p
== '\n'){
1320 columns
[numcols
] = p
;
1321 column_ptr
[numcols
] = p
- original
;
1322 while (*p
&& *p
!= ' ' && *p
!= '\r' && *p
!= '\n')
1331 char *column
= columns
[idx
];
1333 if (!column
|| column
[0] < '0' || column
[0] > '9')
1339 /* Return 1 for MM-DD-YY and MM-DD-YYYY */
1341 is_dos_date (const char *str
)
1349 if (len
!= 8 && len
!= 10)
1352 if (str
[2] != str
[5])
1355 if (!strchr ("\\-/", (int) str
[2]))
1362 is_week (char *str
, struct tm
*tim
)
1364 static const char *week
= "SunMonTueWedThuFriSat";
1370 if ((pos
= strstr (week
, str
)) != NULL
) {
1372 tim
->tm_wday
= (pos
- week
) / 3;
1379 is_month (char *str
, struct tm
*tim
)
1381 static const char *month
= "JanFebMarAprMayJunJulAugSepOctNovDec";
1387 if ((pos
= strstr (month
, str
)) != NULL
) {
1389 tim
->tm_mon
= (pos
- month
) / 3;
1396 is_time (char *str
, struct tm
*tim
)
1403 if ((p
= strchr (str
, ':')) && (p2
= strrchr (str
, ':'))) {
1406 (str
, "%2d:%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
,
1410 if (sscanf (str
, "%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
) != 2)
1419 static int is_year (char *str
, struct tm
*tim
)
1426 if (strchr (str
, ':'))
1429 if (strlen (str
) != 4)
1432 if (sscanf (str
, "%ld", &year
) != 1)
1435 if (year
< 1900 || year
> 3000)
1438 tim
->tm_year
= (int) (year
- 1900);
1444 * FIXME: this is broken. Consider following entry:
1445 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1446 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1450 vfs_parse_filetype (char c
)
1453 case 'd': return S_IFDIR
;
1454 case 'b': return S_IFBLK
;
1455 case 'c': return S_IFCHR
;
1456 case 'l': return S_IFLNK
;
1457 case 's': /* Socket */
1461 /* If not supported, we fall through to IFIFO */
1464 case 'D': /* Solaris door */
1470 case 'p': return S_IFIFO
;
1471 case 'm': case 'n': /* Don't know what these are :-) */
1472 case '-': case '?': return S_IFREG
;
1477 int vfs_parse_filemode (const char *p
)
1478 { /* converts rw-rw-rw- into 0666 */
1481 case 'r': res
|= 0400; break;
1486 case 'w': res
|= 0200; break;
1491 case 'x': res
|= 0100; break;
1492 case 's': res
|= 0100 | S_ISUID
; break;
1493 case 'S': res
|= S_ISUID
; break;
1498 case 'r': res
|= 0040; break;
1503 case 'w': res
|= 0020; break;
1508 case 'x': res
|= 0010; break;
1509 case 's': res
|= 0010 | S_ISGID
; break;
1510 case 'l': /* Solaris produces these */
1511 case 'S': res
|= S_ISGID
; break;
1516 case 'r': res
|= 0004; break;
1521 case 'w': res
|= 0002; break;
1526 case 'x': res
|= 0001; break;
1527 case 't': res
|= 0001 | S_ISVTX
; break;
1528 case 'T': res
|= S_ISVTX
; break;
1535 /* This function parses from idx in the columns[] array */
1537 vfs_parse_filedate (int idx
, time_t *t
)
1544 /* Let's setup default time values */
1545 tim
.tm_year
= current_year
;
1546 tim
.tm_mon
= current_mon
;
1547 tim
.tm_mday
= current_mday
;
1551 tim
.tm_isdst
= -1; /* Let mktime() try to guess correct dst offset */
1555 /* We eat weekday name in case of extfs */
1556 if (is_week (p
, &tim
))
1560 if (is_month (p
, &tim
)) {
1561 /* And we expect, it followed by day number */
1563 tim
.tm_mday
= (int) atol (columns
[idx
++]);
1565 return 0; /* No day */
1568 /* We usually expect:
1571 But in case of extfs we allow these date formats:
1574 Wek Mon DD hh:mm:ss YYYY
1576 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1577 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1579 /* Special case with MM-DD-YY or MM-DD-YYYY */
1580 if (is_dos_date (p
)) {
1583 if (sscanf (p
, "%2d-%2d-%d", &d
[0], &d
[1], &d
[2]) == 3) {
1584 /* Months are zero based */
1601 return 0; /* sscanf failed */
1603 return 0; /* unsupported format */
1606 /* Here we expect to find time and/or year */
1609 if (is_time (columns
[idx
], &tim
)
1610 || (got_year
= is_year (columns
[idx
], &tim
))) {
1613 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1614 if (is_num (idx
) && (columns
[idx
+ 1][0])) {
1616 if (is_time (columns
[idx
], &tim
))
1617 idx
++; /* time also */
1619 if ((got_year
= is_year (columns
[idx
], &tim
)))
1620 idx
++; /* year also */
1623 } /* only time or date */
1625 return 0; /* Nor time or date */
1628 * If the date is less than 6 months in the past, it is shown without year
1629 * other dates in the past or future are shown with year but without time
1630 * This does not check for years before 1900 ... I don't know, how
1631 * to represent them at all
1633 if (!got_year
&& current_mon
< 6 && current_mon
< tim
.tm_mon
1634 && tim
.tm_mon
- current_mon
>= 6)
1638 if ((*t
= mktime (&tim
)) < 0)
1644 vfs_parse_ls_lga (const char *p
, struct stat
*s
, char **filename
, char **linkname
)
1646 int idx
, idx2
, num_cols
;
1648 char *p_copy
= NULL
;
1650 const char *line
= p
;
1652 if (strncmp (p
, "total", 5) == 0)
1655 if ((i
= vfs_parse_filetype(*(p
++))) == -1)
1659 if (*p
== ' ') /* Notwell 4 */
1662 if (strlen (p
) <= 8 || p
[8] != ']')
1664 /* Should parse here the Notwell permissions :) */
1665 if (S_ISDIR (s
->st_mode
))
1666 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
);
1668 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
);
1671 if ((i
= vfs_parse_filemode(p
)) == -1)
1676 /* This is for an extra ACL attribute (HP-UX) */
1681 p_copy
= g_strdup(p
);
1682 num_cols
= vfs_split_text (p_copy
);
1684 s
->st_nlink
= atol (columns
[0]);
1685 if (s
->st_nlink
<= 0)
1689 s
->st_uid
= finduid (columns
[1]);
1691 s
->st_uid
= (uid_t
) atol (columns
[1]);
1693 /* Mhm, the ls -lg did not produce a group field */
1694 for (idx
= 3; idx
<= 5; idx
++)
1695 if (is_month(columns
[idx
], NULL
) || is_week(columns
[idx
], NULL
) || is_dos_date(columns
[idx
]))
1698 if (idx
== 6 || (idx
== 5 && !S_ISCHR (s
->st_mode
) && !S_ISBLK (s
->st_mode
)))
1701 /* We don't have gid */
1702 if (idx
== 3 || (idx
== 4 && (S_ISCHR(s
->st_mode
) || S_ISBLK (s
->st_mode
))))
1705 /* We have gid field */
1707 s
->st_gid
= (gid_t
) atol (columns
[2]);
1709 s
->st_gid
= findgid (columns
[2]);
1713 /* This is device */
1714 if (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
)){
1717 if (!is_num (idx2
) || sscanf(columns
[idx2
], " %d,", &maj
) != 1)
1720 if (!is_num (++idx2
) || sscanf(columns
[idx2
], " %d", &min
) != 1)
1724 s
->st_rdev
= ((maj
& 0xff) << 8) | (min
& 0xffff00ff);
1729 /* Common file size */
1733 s
->st_size
= (size_t) atol (columns
[idx2
]);
1739 idx
= vfs_parse_filedate(idx
, &s
->st_mtime
);
1742 /* Use resulting time value */
1743 s
->st_atime
= s
->st_ctime
= s
->st_mtime
;
1744 /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
1745 #ifdef HAVE_ST_BLKSIZE
1746 s
->st_blksize
= 512;
1748 #ifdef HAVE_ST_BLOCKS
1749 s
->st_blocks
= (s
->st_size
+ 511) / 512;
1752 for (i
= idx
+ 1, idx2
= 0; i
< num_cols
; i
++ )
1753 if (strcmp (columns
[i
], "->") == 0){
1758 if (((S_ISLNK (s
->st_mode
) ||
1759 (num_cols
== idx
+ 3 && s
->st_nlink
> 1))) /* Maybe a hardlink? (in extfs) */
1763 *filename
= g_strndup (p
+ column_ptr
[idx
], column_ptr
[idx2
] - column_ptr
[idx
] - 1);
1766 t
= g_strdup (p
+ column_ptr
[idx2
+1]);
1770 /* Extract the filename from the string copy, not from the columns
1771 * this way we have a chance of entering hidden directories like ". ."
1775 *filename = g_strdup (columns [idx++]);
1778 t
= g_strdup (p
+ column_ptr
[idx
]);
1787 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1789 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1798 static int errorcount
= 0;
1800 if (++errorcount
< 5) {
1801 message_1s (1, _("Cannot parse:"), (p_copy
&& *p_copy
) ? p_copy
: line
);
1802 } else if (errorcount
== 5)
1803 message_1s (1, _("Error"), _("More parsing errors will be ignored."));
1811 vfs_die (const char *m
)
1813 message_1s (1, _("Internal error:"), m
);
1818 vfs_print_stats (const char *fs_name
, const char *action
, const char *file_name
, off_t have
, off_t need
)
1820 static char *i18n_percent_transf_format
= NULL
, *i18n_transf_format
= NULL
;
1822 if (i18n_percent_transf_format
== NULL
) {
1823 i18n_percent_transf_format
= _("%s: %s: %s %3d%% (%lu bytes transferred)");
1824 i18n_transf_format
= _("%s: %s: %s %lu bytes transferred");
1828 print_vfs_message (i18n_percent_transf_format
, fs_name
, action
,
1829 file_name
, (int)((double)have
*100/need
), (unsigned long) have
);
1831 print_vfs_message (i18n_transf_format
,
1832 fs_name
, action
, file_name
, (unsigned long) have
);
1836 vfs_get_password (char *msg
)
1838 return (char *) input_dialog (msg
, _("Password:"), INPUT_PASSWORD
);
1842 * Returns vfs path corresponding to given url. If passed string is
1843 * not recognized as url, g_strdup(url) is returned.
1846 vfs_translate_url (char *url
)
1848 if (strncmp (url
, "ftp://", 6) == 0)
1849 return g_strconcat ("/#ftp:", url
+ 6, NULL
);
1850 else if (strncmp (url
, "a:", 2) == 0)
1851 return g_strdup ("/#a");
1853 return g_strdup (url
);