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/dir.h"
46 #include "../src/main.h"
47 #include "../src/panel.h"
48 #include "../src/key.h" /* Required for the async alarm handler */
49 #include "../src/layout.h" /* For get_panel_widget and get_other_index */
50 #include "../src/wtools.h" /* input_dialog() */
52 #include "xdirentry.h"
54 #include "extfs.h" /* FIXME: we should not know anything about our modules */
60 extern int get_other_type (void);
62 int vfs_timeout
= 60; /* VFS timeout in seconds */
63 static int vfs_flags
= 0; /* Flags */
65 extern int cd_symlinks
; /* Defined in main.c */
67 /* They keep track of the current directory */
68 static vfs
*current_vfs
= &vfs_local_ops
;
69 static char *current_dir
= NULL
;
72 * FIXME: this is broken. It depends on mc not crossing border on month!
74 static int current_mday
;
75 static int current_mon
;
76 static int current_year
;
78 /* FIXME: Open files managed by the vfs layer, should be dynamical */
79 #define MAX_VFS_FILES 100
84 } vfs_file_table
[MAX_VFS_FILES
];
91 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
92 for (i
= 3; i
< MAX_VFS_FILES
; i
++){
93 if (!vfs_file_table
[i
].fs_info
)
97 vfs_die ("No more virtual file handles");
101 /* vfs_local_ops needs to be the first one */
102 static vfs
*vfs_list
= &vfs_local_ops
;
105 vfs_register (vfs
*vfs
)
108 vfs_die("You can not register NULL.");
110 if (vfs
->init
) /* vfs has own initialization function */
111 if (!(*vfs
->init
)(vfs
)) /* but it failed */
114 vfs
->next
= vfs_list
;
121 vfs_type_from_op (char *path
)
126 vfs_die ("vfs_type_from_op got NULL: impossible");
128 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
130 if ((*vfs
->which
) (vfs
, path
) == -1)
134 if (!strncmp (path
, vfs
->prefix
, strlen (vfs
->prefix
)))
137 return NULL
; /* shut up stupid gcc */
140 /* Strip known vfs suffixes from a filename (possible improvement: strip
141 suffix from last path component).
142 Returns a malloced string which has to be freed. */
144 vfs_strip_suffix_from_filename (const char *filename
)
151 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
153 p
= g_strdup (filename
);
154 if (!(semi
= strrchr (p
, '#')))
157 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
159 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
161 *semi
= '\0'; /* Found valid suffix */
164 if (!strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
165 *semi
= '\0'; /* Found valid suffix */
173 path_magic (const char *path
)
177 if (vfs_flags
& FL_ALWAYS_MAGIC
)
180 if (!stat(path
, &buf
))
187 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
188 * What is left in path is p1. You still want to g_free(path), you DON'T
189 * want to free neither *inpath nor *op
192 vfs_split (char *path
, char **inpath
, char **op
)
199 vfs_die("Can not split NULL");
201 semi
= strrchr (path
, '#');
202 if (!semi
|| !path_magic(path
))
205 slash
= strchr (semi
, PATH_SEP
);
217 if ((ret
= vfs_type_from_op (semi
+1))){
221 *inpath
= slash
? slash
+ 1 : NULL
;
228 ret
= vfs_split (path
, inpath
, op
);
234 vfs_rosplit (char *path
)
240 g_return_val_if_fail(path
, NULL
);
242 semi
= strrchr (path
, '#');
243 if (!semi
|| !path_magic (path
))
246 slash
= strchr (semi
, PATH_SEP
);
251 ret
= vfs_type_from_op (semi
+1);
252 if (!ret
&& (vfs_flags
& FL_NO_LOCALHASH
))
258 ret
= vfs_rosplit (path
);
265 vfs_type (char *path
)
269 vfs
= vfs_rosplit(path
);
272 vfs
= &vfs_local_ops
;
277 static struct vfs_stamping
*stamps
;
280 * Returns the number of seconds remaining to the vfs timeout
282 * FIXME: currently this is set to 10 seconds. We should compute this.
287 return stamps
? 10 : 0;
291 vfs_addstamp (vfs
*v
, vfsid id
, struct vfs_stamping
*parent
)
293 if (v
!= &vfs_local_ops
&& id
!= (vfsid
)-1){
294 struct vfs_stamping
*stamp
;
295 struct vfs_stamping
*last_stamp
= NULL
;
297 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
) {
298 if (stamp
->v
== v
&& stamp
->id
== id
){
299 gettimeofday(&(stamp
->time
), NULL
);
304 stamp
= g_new (struct vfs_stamping
, 1);
308 struct vfs_stamping
*st
= stamp
;
310 st
->parent
= g_new (struct vfs_stamping
, 1);
311 *st
->parent
= *parent
;
312 parent
= parent
->parent
;
320 gettimeofday (&(stamp
->time
), NULL
);
325 last_stamp
->next
= stamp
;
327 /* Add first element */
334 vfs_stamp (vfs
*v
, vfsid id
)
336 struct vfs_stamping
*stamp
;
338 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
)
339 if (stamp
->v
== v
&& stamp
->id
== id
){
341 gettimeofday (&(stamp
->time
), NULL
);
342 if (stamp
->parent
!= NULL
)
343 vfs_stamp (stamp
->parent
->v
, stamp
->parent
->id
);
350 vfs_rm_parents (struct vfs_stamping
*stamp
)
352 struct vfs_stamping
*parent
;
355 parent
= stamp
->parent
;
362 vfs_rmstamp (vfs
*v
, vfsid id
, int removeparents
)
364 struct vfs_stamping
*stamp
, *st1
;
366 for (stamp
= stamps
, st1
= NULL
; stamp
!= NULL
; st1
= stamp
, stamp
= stamp
->next
)
367 if (stamp
->v
== v
&& stamp
->id
== id
){
368 if (stamp
->parent
!= NULL
){
370 vfs_rmstamp (stamp
->parent
->v
, stamp
->parent
->id
, 1);
371 vfs_rm_parents (stamp
->parent
);
374 stamps
= stamp
->next
;
376 st1
->next
= stamp
->next
;
387 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
388 /* Hope that error message is obscure enough ;-) */
392 mc_open (const char *filename
, int flags
, ...)
399 char *file
= vfs_canon (filename
);
400 vfs
*vfs
= vfs_type (file
);
402 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
403 va_start (ap
, flags
);
404 mode
= va_arg (ap
, int);
412 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
415 errno
= ferrno (vfs
);
418 handle
= get_bucket ();
419 vfs_file_table
[handle
].fs_info
= info
;
420 vfs_file_table
[handle
].operations
= vfs
;
425 #define vfs_op(handle) vfs_file_table [handle].operations
426 #define vfs_info(handle) vfs_file_table [handle].fs_info
427 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
429 #define MC_OP(name, inarg, callarg, pre, post) \
430 int mc_##name inarg \
436 result = vfs->name ? (*vfs->name)callarg : -1; \
439 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
443 #define MC_NAMEOP(name, inarg, callarg) \
444 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
445 #define MC_HANDLEOP(name, inarg, callarg) \
446 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
448 MC_HANDLEOP(read
, (int handle
, char *buffer
, int count
), (vfs_info (handle
), buffer
, count
) )
451 mc_ctl (int handle
, int ctlop
, int arg
)
453 vfs
*vfs
= vfs_op (handle
);
455 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
459 mc_setctl (char *path
, int ctlop
, char *arg
)
465 vfs_die("You don't want to pass NULL to mc_setctl.");
467 path
= vfs_canon (path
);
468 vfs
= vfs_type (path
);
469 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, path
, ctlop
, arg
) : 0;
475 mc_close (int handle
)
480 if (handle
== -1 || !vfs_info (handle
))
483 vfs
= vfs_op (handle
);
485 return close (handle
);
488 vfs_die ("VFS must support close.\n");
489 result
= (*vfs
->close
)(vfs_info (handle
));
490 vfs_free_bucket (handle
);
492 errno
= ferrno (vfs
);
498 mc_opendir (char *dirname
)
500 int handle
, *handlep
;
504 dirname
= vfs_canon (dirname
);
505 vfs
= vfs_type (dirname
);
507 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dirname
) : NULL
;
510 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
513 handle
= get_bucket ();
514 vfs_file_table
[handle
].fs_info
= info
;
515 vfs_file_table
[handle
].operations
= vfs
;
517 handlep
= g_new (int, 1);
519 return (DIR *) handlep
;
522 /* This should strip the non needed part of a path name */
523 #define vfs_name(x) x
526 mc_seekdir (DIR *dirp
, int offset
)
535 handle
= *(int *) dirp
;
536 vfs
= vfs_op (handle
);
538 (*vfs
->seekdir
) (vfs_info (handle
), offset
);
543 #define MC_DIROP(name, type, onerr ) \
544 type mc_##name (DIR *dirp) \
554 handle = *(int *) dirp; \
555 vfs = vfs_op (handle); \
556 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
557 if (result == onerr) \
558 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
562 MC_DIROP (readdir
, struct dirent
*, NULL
)
563 MC_DIROP (telldir
, int, -1)
566 mc_closedir (DIR *dirp
)
568 int handle
= *(int *) dirp
;
569 vfs
*vfs
= vfs_op (handle
);
572 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
573 vfs_free_bucket (handle
);
578 int mc_stat (char *path
, struct stat
*buf
) {
582 path
= vfs_canon (path
); vfs
= vfs_type (path
);
583 result
= vfs
->stat
? (*vfs
->stat
) (vfs
, vfs_name (path
), buf
) : -1;
586 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
590 int mc_lstat (char *path
, struct stat
*buf
) {
594 path
= vfs_canon (path
); vfs
= vfs_type (path
);
595 result
= vfs
->lstat
? (*vfs
->lstat
) (vfs
, vfs_name (path
), buf
) : -1;
598 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
602 int mc_fstat (int handle
, struct stat
*buf
) {
608 vfs
= vfs_op (handle
);
609 result
= vfs
->fstat
? (*vfs
->fstat
) (vfs_info (handle
), buf
) : -1;
611 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
616 * You must g_strdup whatever this function returns.
623 struct stat my_stat
, my_stat2
;
625 if (!vfs_rosplit (current_dir
)){
626 p
= g_get_current_dir ();
627 if (!p
) /* One of the directories in the path is not readable */
630 /* Otherwise check if it is O.K. to use the current_dir */
632 mc_stat (p
, &my_stat
) ||
633 mc_stat (current_dir
, &my_stat2
) ||
634 my_stat
.st_ino
!= my_stat2
.st_ino
||
635 my_stat
.st_dev
!= my_stat2
.st_dev
){
636 g_free (current_dir
);
639 } /* Otherwise we return current_dir below */
646 mc_get_current_wd (char *buffer
, int size
)
648 const char *cwd
= mc_return_cwd();
650 strncpy (buffer
, cwd
, size
);
654 MC_NAMEOP (chmod
, (char *path
, int mode
), (vfs
, vfs_name (path
), mode
))
655 MC_NAMEOP (chown
, (char *path
, int owner
, int group
), (vfs
, vfs_name (path
), owner
, group
))
656 MC_NAMEOP (utime
, (char *path
, struct utimbuf
*times
), (vfs
, vfs_name (path
), times
))
657 MC_NAMEOP (readlink
, (char *path
, char *buf
, int bufsiz
), (vfs
, vfs_name (path
), buf
, bufsiz
))
658 MC_NAMEOP (unlink
, (char *path
), (vfs
, vfs_name (path
)))
659 MC_NAMEOP (symlink
, (char *name1
, char *path
), (vfs
, vfs_name (name1
), vfs_name (path
)))
661 #define MC_RENAMEOP(name) \
662 int mc_##name (const char *fname1, const char *fname2) \
667 char *name2, *name1 = vfs_canon (fname1); \
668 vfs = vfs_type (name1); \
669 name2 = vfs_canon (fname2); \
670 if (vfs != vfs_type (name2)){ \
677 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
681 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
688 MC_HANDLEOP (write
, (int handle
, char *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
690 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
699 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
701 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
706 * remove //, /./ and /../, local should point to big enough buffer
709 #define ISSLASH(a) (!a || (a == '/'))
712 vfs_canon (const char *path
)
715 vfs_die("Cannot canonicalize NULL");
717 /* Tilde expansion */
719 char *local
, *result
;
721 local
= tilde_expand (path
);
723 result
= vfs_canon (local
);
729 /* Relative to current directory */
730 if (*path
!= PATH_SEP
){
731 char *local
, *result
;
733 local
= concat_dir_and_file (current_dir
, path
);
735 result
= vfs_canon (local
);
741 * So we have path of following form:
742 * /p1/p2#op/.././././p3#op/p4. Good luck.
745 char *result
= g_strdup (path
);
746 canonicalize_pathname (result
);
752 vfs_ncs_getid (vfs
*nvfs
, char *dir
, struct vfs_stamping
**par
)
756 dir
= concat_dir_and_file (dir
, "");
758 nvfsid
= (*nvfs
->getid
)(nvfs
, dir
, par
);
765 is_parent (vfs
* nvfs
, vfsid nvfsid
, struct vfs_stamping
*parent
)
767 struct vfs_stamping
*stamp
;
769 for (stamp
= parent
; stamp
; stamp
= stamp
->parent
)
770 if (stamp
->v
== nvfs
&& stamp
->id
== nvfsid
)
773 return (stamp
? 1 : 0);
777 vfs_add_noncurrent_stamps (vfs
* oldvfs
, vfsid oldvfsid
, struct vfs_stamping
*parent
)
779 vfs
*nvfs
, *n2vfs
, *n3vfs
;
780 vfsid nvfsid
, n2vfsid
, n3vfsid
;
781 struct vfs_stamping
*par
, *stamp
;
784 /* FIXME: As soon as we convert to multiple panels, this stuff
785 has to change. It works like this: We do not time out the
786 vfs's which are current in any panel and on the other
787 side we add the old directory with all its parents which
788 are not in any panel (if we find such one, we stop adding
789 parents to the time-outing structure. */
791 /* There are three directories we have to take care of: current_dir,
792 cpanel->cwd and opanel->cwd. Athough most of the time either
793 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
794 same, it's possible that all three are different -- Norbert */
799 nvfs
= vfs_type (current_dir
);
800 nvfsid
= vfs_ncs_getid (nvfs
, current_dir
, &par
);
801 vfs_rmstamp (nvfs
, nvfsid
, 1);
803 f
= is_parent (oldvfs
, oldvfsid
, par
);
804 vfs_rm_parents (par
);
805 if ((nvfs
== oldvfs
&& nvfsid
== oldvfsid
) || oldvfsid
== (vfsid
*)-1 || f
){
809 if (get_current_type () == view_listing
){
810 n2vfs
= vfs_type (cpanel
->cwd
);
811 n2vfsid
= vfs_ncs_getid (n2vfs
, cpanel
->cwd
, &par
);
812 f
= is_parent (oldvfs
, oldvfsid
, par
);
813 vfs_rm_parents (par
);
814 if ((n2vfs
== oldvfs
&& n2vfsid
== oldvfsid
) || f
)
818 n2vfsid
= (vfs
*) -1;
821 if (get_other_type () == view_listing
){
822 n3vfs
= vfs_type (opanel
->cwd
);
823 n3vfsid
= vfs_ncs_getid (n3vfs
, opanel
->cwd
, &par
);
824 f
= is_parent (oldvfs
, oldvfsid
, par
);
825 vfs_rm_parents (par
);
826 if ((n3vfs
== oldvfs
&& n3vfsid
== oldvfsid
) || f
)
833 if ((*oldvfs
->nothingisopen
) (oldvfsid
)){
834 if (oldvfs
== &vfs_extfs_ops
&& ((extfs_archive
*) oldvfsid
)->name
== 0){
835 /* Free the resources immediatly when we leave a mtools fs
836 ('cd a:') instead of waiting for the vfs-timeout */
837 (oldvfs
->free
) (oldvfsid
);
839 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
840 for (stamp
= parent
; stamp
!= NULL
; stamp
= stamp
->parent
){
841 if ((stamp
->v
== nvfs
&& stamp
->id
== nvfsid
) ||
842 (stamp
->v
== n2vfs
&& stamp
->id
== n2vfsid
) ||
843 (stamp
->v
== n3vfs
&& stamp
->id
== n3vfsid
) ||
844 stamp
->id
== (vfsid
) - 1 ||
845 !(*stamp
->v
->nothingisopen
) (stamp
->id
))
847 if (stamp
->v
== &vfs_extfs_ops
&& ((extfs_archive
*) stamp
->id
)->name
== 0){
848 (stamp
->v
->free
) (stamp
->id
);
849 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
851 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
857 vfs_stamp_path (char *path
)
861 struct vfs_stamping
*par
, *stamp
;
863 vfs
= vfs_type (path
);
864 id
= vfs_ncs_getid (vfs
, path
, &par
);
865 vfs_addstamp (vfs
, id
, par
);
867 for (stamp
= par
; stamp
!= NULL
; stamp
= stamp
->parent
)
868 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
869 vfs_rm_parents (par
);
873 vfs_add_current_stamps (void)
875 vfs_stamp_path (current_dir
);
878 if (get_current_type () == view_listing
)
879 vfs_stamp_path (cpanel
->cwd
);
883 if (get_other_type () == view_listing
)
884 vfs_stamp_path (opanel
->cwd
);
888 /* This function is really broken */
890 mc_chdir (char *path
)
897 struct vfs_stamping
*parent
;
899 a
= current_dir
; /* Save a copy for case of failure */
900 current_dir
= vfs_canon (path
);
901 current_vfs
= vfs_type (current_dir
);
902 b
= g_strdup (current_dir
);
903 result
= (*current_vfs
->chdir
) ? (*current_vfs
->chdir
)(current_vfs
, vfs_name (b
)) : -1;
906 errno
= ferrno (current_vfs
);
907 g_free (current_dir
);
908 current_vfs
= vfs_type (a
);
911 oldvfs
= vfs_type (a
);
912 oldvfsid
= vfs_ncs_getid (oldvfs
, a
, &parent
);
914 vfs_add_noncurrent_stamps (oldvfs
, oldvfsid
, parent
);
915 vfs_rm_parents (parent
);
919 p
= strchr (current_dir
, 0) - 1;
920 if (*p
== PATH_SEP
&& p
> current_dir
)
921 *p
= 0; /* Sometimes we assume no trailing slash on cwd */
927 vfs_current_is_local (void)
929 return current_vfs
== &vfs_local_ops
;
933 vfs_file_is_local (const char *file
)
935 char *filename
= vfs_canon (file
);
936 vfs
*vfs
= vfs_type (filename
);
939 return vfs
== &vfs_local_ops
;
943 vfs_file_is_ftp (char *filename
)
948 filename
= vfs_canon (filename
);
949 vfs
= vfs_type (filename
);
951 return vfs
== &vfs_ftpfs_ops
;
958 vfs_file_is_smb (char *filename
)
964 filename
= vfs_canon (filename
);
965 vfs
= vfs_type (filename
);
967 return vfs
== &vfs_smbfs_ops
;
968 #endif /* USE_NETCODE */
969 #endif /* WITH_SMBFS */
973 char *vfs_get_current_dir (void)
978 static void vfs_setup_wd (void)
980 current_dir
= g_strdup (PATH_SEP_STR
);
981 if (!(vfs_flags
& FL_NO_CWDSETUP
))
984 if (strlen(current_dir
)>MC_MAXPATHLEN
-2)
985 vfs_die ("Current dir too long.\n");
988 MC_NAMEOP (mkdir
, (char *path
, mode_t mode
), (vfs
, vfs_name (path
), mode
))
989 MC_NAMEOP (rmdir
, (char *path
), (vfs
, vfs_name (path
)))
990 MC_NAMEOP (mknod
, (char *path
, int mode
, int dev
), (vfs
, vfs_name (path
), mode
, dev
))
993 static struct mc_mmapping
{
997 struct mc_mmapping
*next
;
998 } *mc_mmaparray
= NULL
;
1001 mc_mmap (caddr_t addr
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
1005 struct mc_mmapping
*mcm
;
1008 return (caddr_t
) -1;
1011 result
= vfs
->mmap
? (*vfs
->mmap
)(vfs
, addr
, len
, prot
, flags
, vfs_info (fd
), offset
) : (caddr_t
)-1;
1012 if (result
== (caddr_t
)-1){
1013 errno
= ferrno (vfs
);
1016 mcm
=g_new (struct mc_mmapping
, 1);
1018 mcm
->vfs_info
= vfs_info (fd
);
1020 mcm
->next
= mc_mmaparray
;
1026 mc_munmap (caddr_t addr
, size_t len
)
1028 struct mc_mmapping
*mcm
, *mcm2
= NULL
;
1030 for (mcm
= mc_mmaparray
; mcm
!= NULL
; mcm2
= mcm
, mcm
= mcm
->next
){
1031 if (mcm
->addr
== addr
){
1033 mc_mmaparray
= mcm
->next
;
1035 mcm2
->next
= mcm
->next
;
1036 if (mcm
->vfs
->munmap
)
1037 (*mcm
->vfs
->munmap
)(mcm
->vfs
, addr
, len
, mcm
->vfs_info
);
1048 mc_def_getlocalcopy (vfs
*vfs
, char *filename
)
1057 fdin
= mc_open (filename
, O_RDONLY
);
1061 /* Try to preserve existing extension */
1062 for (ptr
= filename
+ strlen(filename
) - 1; ptr
>= filename
; ptr
--) {
1068 if (!isalnum((unsigned char) *ptr
))
1072 fdout
= mc_mkstemps (&tmp
, "mclocalcopy", ext
);
1075 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0){
1076 if (write (fdout
, buffer
, i
) != i
)
1081 i
= mc_close (fdin
);
1085 if (close (fdout
)==-1)
1088 if (mc_stat (filename
, &mystat
) != -1){
1089 chmod (tmp
, mystat
.st_mode
);
1094 if (fdout
) close(fdout
);
1095 if (fdin
) mc_close (fdin
);
1101 mc_getlocalcopy (const char *pathname
)
1104 char *path
= vfs_canon (pathname
);
1105 vfs
*vfs
= vfs_type (path
);
1107 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, vfs_name (path
)) :
1108 mc_def_getlocalcopy (vfs
, vfs_name (path
));
1111 errno
= ferrno (vfs
);
1116 mc_def_ungetlocalcopy (vfs
*vfs
, char *filename
, char *local
, int has_changed
)
1117 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1118 int fdin
= -1, fdout
= -1, i
;
1122 fdin
= open (local
, O_RDONLY
);
1125 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
1128 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0){
1129 if (mc_write (fdout
, buffer
, i
) != i
)
1135 if (close (fdin
)==-1) {
1140 if (mc_close (fdout
)==-1) {
1150 message_1s (1, _("Changes to file lost"), filename
);
1151 if (fdout
!=-1) mc_close(fdout
);
1152 if (fdin
!=-1) close(fdin
);
1159 mc_ungetlocalcopy (const char *pathname
, char *local
, int has_changed
)
1161 int return_value
= 0;
1162 char *path
= vfs_canon (pathname
);
1163 vfs
*vfs
= vfs_type (path
);
1165 return_value
= vfs
->ungetlocalcopy
?
1166 (*vfs
->ungetlocalcopy
)(vfs
, vfs_name (path
), local
, has_changed
) :
1167 mc_def_ungetlocalcopy (vfs
, vfs_name (path
), local
, has_changed
);
1169 return return_value
;
1173 * Hmm, as timeout is minute or so, do we need to care about usecs?
1176 timeoutcmp (struct timeval
*t1
, struct timeval
*t2
)
1178 return ((t1
->tv_sec
< t2
->tv_sec
)
1179 || ((t1
->tv_sec
== t2
->tv_sec
) && (t1
->tv_usec
<= t2
->tv_usec
)));
1182 /* This is called from timeout handler with now = 0, or can be called
1183 with now = 1 to force freeing all filesystems that are not in use */
1186 vfs_expire (int now
)
1188 static int locked
= 0;
1189 struct timeval time
;
1190 struct vfs_stamping
*stamp
, *st
;
1192 /* Avoid recursive invocation, e.g. when one of the free functions
1198 gettimeofday (&time
, NULL
);
1199 time
.tv_sec
-= vfs_timeout
;
1201 for (stamp
= stamps
; stamp
!= NULL
;){
1202 if (now
|| (timeoutcmp (&stamp
->time
, &time
))){
1204 (*stamp
->v
->free
) (stamp
->id
);
1205 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
1208 stamp
= stamp
->next
;
1214 vfs_timeout_handler (void)
1222 time_t current_time
;
1225 memset (vfs_file_table
, 0, sizeof (vfs_file_table
));
1226 current_time
= time (NULL
);
1227 t
= localtime (¤t_time
);
1228 current_mday
= t
->tm_mday
;
1229 current_mon
= t
->tm_mon
;
1230 current_year
= t
->tm_year
;
1232 /* We do not want to register vfs_local_ops */
1236 vfs_register (&vfs_ftpfs_ops
);
1237 vfs_register (&vfs_fish_ops
);
1239 vfs_register (&vfs_smbfs_ops
);
1240 #endif /* WITH_SMBFS */
1242 vfs_register (&vfs_mcfs_ops
);
1243 #endif /* WITH_SMBFS */
1244 #endif /* USE_NETCODE */
1246 vfs_register (&vfs_extfs_ops
);
1247 vfs_register (&vfs_sfs_ops
);
1248 vfs_register (&vfs_tarfs_ops
);
1249 vfs_register (&vfs_cpiofs_ops
);
1251 #ifdef USE_EXT2FSLIB
1252 vfs_register (&vfs_undelfs_ops
);
1253 #endif /* USE_EXT2FSLIB */
1261 struct vfs_stamping
*stamp
, *st
;
1264 for (stamp
= stamps
, stamps
= 0; stamp
!= NULL
;){
1265 (*stamp
->v
->free
)(stamp
->id
);
1272 vfs_rmstamp (stamps
->v
, stamps
->id
, 1);
1275 g_free (current_dir
);
1277 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1283 * These ones grab information from the VFS
1284 * and handles them to an upper layer
1287 vfs_fill_names (void (*func
)(char *))
1291 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1292 if (vfs
->fill_names
)
1293 (*vfs
->fill_names
) (vfs
, func
);
1296 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1299 static char *columns
[MAXCOLS
]; /* Points to the string in column n */
1300 static int column_ptr
[MAXCOLS
]; /* Index from 0 to the starting positions of the columns */
1303 vfs_split_text (char *p
)
1308 memset (columns
, 0, sizeof (columns
));
1310 for (numcols
= 0; *p
&& numcols
< MAXCOLS
; numcols
++){
1311 while (*p
== ' ' || *p
== '\r' || *p
== '\n'){
1315 columns
[numcols
] = p
;
1316 column_ptr
[numcols
] = p
- original
;
1317 while (*p
&& *p
!= ' ' && *p
!= '\r' && *p
!= '\n')
1326 char *column
= columns
[idx
];
1328 if (!column
|| column
[0] < '0' || column
[0] > '9')
1335 is_dos_date (char *str
)
1340 if (strlen (str
) == 8 && str
[2] == str
[5]
1341 && strchr ("\\-/", (int) str
[2]) != NULL
)
1348 is_week (char *str
, struct tm
*tim
)
1350 static const char *week
= "SunMonTueWedThuFriSat";
1356 if ((pos
= strstr (week
, str
)) != NULL
) {
1358 tim
->tm_wday
= (pos
- week
) / 3;
1365 is_month (char *str
, struct tm
*tim
)
1367 static const char *month
= "JanFebMarAprMayJunJulAugSepOctNovDec";
1373 if ((pos
= strstr (month
, str
)) != NULL
) {
1375 tim
->tm_mon
= (pos
- month
) / 3;
1382 is_time (char *str
, struct tm
*tim
)
1389 if ((p
= strchr (str
, ':')) && (p2
= strrchr (str
, ':'))) {
1392 (str
, "%2d:%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
,
1396 if (sscanf (str
, "%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
) != 2)
1405 static int is_year (char *str
, struct tm
*tim
)
1412 if (strchr (str
, ':'))
1415 if (strlen (str
) != 4)
1418 if (sscanf (str
, "%ld", &year
) != 1)
1421 if (year
< 1900 || year
> 3000)
1424 tim
->tm_year
= (int) (year
- 1900);
1430 * FIXME: this is broken. Consider following entry:
1431 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1432 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1436 vfs_parse_filetype (char c
)
1439 case 'd': return S_IFDIR
;
1440 case 'b': return S_IFBLK
;
1441 case 'c': return S_IFCHR
;
1442 case 'l': return S_IFLNK
;
1443 case 's': /* Socket */
1447 /* If not supported, we fall through to IFIFO */
1450 case 'D': /* Solaris door */
1456 case 'p': return S_IFIFO
;
1457 case 'm': case 'n': /* Don't know what these are :-) */
1458 case '-': case '?': return S_IFREG
;
1463 int vfs_parse_filemode (const char *p
)
1464 { /* converts rw-rw-rw- into 0666 */
1467 case 'r': res
|= 0400; break;
1472 case 'w': res
|= 0200; break;
1477 case 'x': res
|= 0100; break;
1478 case 's': res
|= 0100 | S_ISUID
; break;
1479 case 'S': res
|= S_ISUID
; break;
1484 case 'r': res
|= 0040; break;
1489 case 'w': res
|= 0020; break;
1494 case 'x': res
|= 0010; break;
1495 case 's': res
|= 0010 | S_ISGID
; break;
1496 case 'l': /* Solaris produces these */
1497 case 'S': res
|= S_ISGID
; break;
1502 case 'r': res
|= 0004; break;
1507 case 'w': res
|= 0002; break;
1512 case 'x': res
|= 0001; break;
1513 case 't': res
|= 0001 | S_ISVTX
; break;
1514 case 'T': res
|= S_ISVTX
; break;
1521 /* This function parses from idx in the columns[] array */
1523 vfs_parse_filedate (int idx
, time_t *t
)
1530 /* Let's setup default time values */
1531 tim
.tm_year
= current_year
;
1532 tim
.tm_mon
= current_mon
;
1533 tim
.tm_mday
= current_mday
;
1537 tim
.tm_isdst
= -1; /* Let mktime() try to guess correct dst offset */
1541 /* We eat weekday name in case of extfs */
1542 if (is_week (p
, &tim
))
1546 if (is_month (p
, &tim
)) {
1547 /* And we expect, it followed by day number */
1549 tim
.tm_mday
= (int) atol (columns
[idx
++]);
1551 return 0; /* No day */
1554 /* We usually expect:
1557 But in case of extfs we allow these date formats:
1560 Wek Mon DD hh:mm:ss YYYY
1562 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1563 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1565 /* Here just this special case with MM-DD-YY */
1566 if (is_dos_date (p
)) {
1569 if (sscanf (p
, "%2d-%2d-%2d", &d
[0], &d
[1], &d
[2]) == 3) {
1570 /* We expect to get:
1576 /* Hmm... maybe, next time :) */
1578 /* At last, MM-DD-YY */
1579 d
[0]--; /* Months are zerobased */
1589 return 0; /* sscanf failed */
1591 return 0; /* unsupported format */
1594 /* Here we expect to find time and/or year */
1597 if (is_time (columns
[idx
], &tim
)
1598 || (got_year
= is_year (columns
[idx
], &tim
))) {
1601 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1602 if (is_num (idx
) && (columns
[idx
+ 1][0])) {
1604 if (is_time (columns
[idx
], &tim
))
1605 idx
++; /* time also */
1607 if ((got_year
= is_year (columns
[idx
], &tim
)))
1608 idx
++; /* year also */
1611 } /* only time or date */
1613 return 0; /* Nor time or date */
1616 * If the date is less than 6 months in the past, it is shown without year
1617 * other dates in the past or future are shown with year but without time
1618 * This does not check for years before 1900 ... I don't know, how
1619 * to represent them at all
1621 if (!got_year
&& current_mon
< 6 && current_mon
< tim
.tm_mon
1622 && tim
.tm_mon
- current_mon
>= 6)
1626 if ((*t
= mktime (&tim
)) < 0)
1632 vfs_parse_ls_lga (const char *p
, struct stat
*s
, char **filename
, char **linkname
)
1634 int idx
, idx2
, num_cols
;
1636 char *p_copy
= NULL
;
1638 const char *line
= p
;
1640 if (strncmp (p
, "total", 5) == 0)
1643 if ((i
= vfs_parse_filetype(*(p
++))) == -1)
1647 if (*p
== ' ') /* Notwell 4 */
1650 if (strlen (p
) <= 8 || p
[8] != ']')
1652 /* Should parse here the Notwell permissions :) */
1653 if (S_ISDIR (s
->st_mode
))
1654 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
);
1656 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
);
1659 if ((i
= vfs_parse_filemode(p
)) == -1)
1664 /* This is for an extra ACL attribute (HP-UX) */
1669 p_copy
= g_strdup(p
);
1670 num_cols
= vfs_split_text (p_copy
);
1672 s
->st_nlink
= atol (columns
[0]);
1673 if (s
->st_nlink
<= 0)
1677 s
->st_uid
= finduid (columns
[1]);
1679 s
->st_uid
= (uid_t
) atol (columns
[1]);
1681 /* Mhm, the ls -lg did not produce a group field */
1682 for (idx
= 3; idx
<= 5; idx
++)
1683 if (is_month(columns
[idx
], NULL
) || is_week(columns
[idx
], NULL
) || is_dos_date(columns
[idx
]))
1686 if (idx
== 6 || (idx
== 5 && !S_ISCHR (s
->st_mode
) && !S_ISBLK (s
->st_mode
)))
1689 /* We don't have gid */
1690 if (idx
== 3 || (idx
== 4 && (S_ISCHR(s
->st_mode
) || S_ISBLK (s
->st_mode
))))
1693 /* We have gid field */
1695 s
->st_gid
= (gid_t
) atol (columns
[2]);
1697 s
->st_gid
= findgid (columns
[2]);
1701 /* This is device */
1702 if (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
)){
1705 if (!is_num (idx2
) || sscanf(columns
[idx2
], " %d,", &maj
) != 1)
1708 if (!is_num (++idx2
) || sscanf(columns
[idx2
], " %d", &min
) != 1)
1712 s
->st_rdev
= ((maj
& 0xff) << 8) | (min
& 0xffff00ff);
1717 /* Common file size */
1721 s
->st_size
= (size_t) atol (columns
[idx2
]);
1727 idx
= vfs_parse_filedate(idx
, &s
->st_mtime
);
1730 /* Use resulting time value */
1731 s
->st_atime
= s
->st_ctime
= s
->st_mtime
;
1732 #ifdef HAVE_ST_BLKSIZE
1733 s
->st_blksize
= 512;
1735 #ifdef HAVE_ST_BLOCKS
1736 s
->st_blocks
= (s
->st_size
+ 511) / 512;
1739 for (i
= idx
+ 1, idx2
= 0; i
< num_cols
; i
++ )
1740 if (strcmp (columns
[i
], "->") == 0){
1745 if (((S_ISLNK (s
->st_mode
) ||
1746 (num_cols
== idx
+ 3 && s
->st_nlink
> 1))) /* Maybe a hardlink? (in extfs) */
1750 *filename
= g_strndup (p
+ column_ptr
[idx
], column_ptr
[idx2
] - column_ptr
[idx
] - 1);
1753 t
= g_strdup (p
+ column_ptr
[idx2
+1]);
1757 /* Extract the filename from the string copy, not from the columns
1758 * this way we have a chance of entering hidden directories like ". ."
1762 *filename = g_strdup (columns [idx++]);
1765 t
= g_strdup (p
+ column_ptr
[idx
]);
1774 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1776 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1785 static int errorcount
= 0;
1787 if (++errorcount
< 5) {
1788 message_1s (1, _("Could not parse:"), (p_copy
&& *p_copy
) ? p_copy
: line
);
1789 } else if (errorcount
== 5)
1790 message_1s (1, _("Error"), _("More parsing errors will be ignored."));
1800 message_1s (1, _("Internal error:"), m
);
1805 vfs_print_stats (const char *fs_name
, const char *action
, const char *file_name
, off_t have
, off_t need
)
1807 static char *i18n_percent_transf_format
= NULL
, *i18n_transf_format
= NULL
;
1809 if (i18n_percent_transf_format
== NULL
) {
1810 i18n_percent_transf_format
= _("%s: %s: %s %3d%% (%lu bytes transferred)");
1811 i18n_transf_format
= _("%s: %s: %s %lu bytes transferred");
1815 print_vfs_message (i18n_percent_transf_format
, fs_name
, action
,
1816 file_name
, (int)((double)have
*100/need
), (unsigned long) have
);
1818 print_vfs_message (i18n_transf_format
,
1819 fs_name
, action
, file_name
, (unsigned long) have
);
1823 vfs_get_password (char *msg
)
1825 return (char *) input_dialog (msg
, _("Password:"), INPUT_PASSWORD
);
1829 * Returns vfs path corresponding to given url. If passed string is
1830 * not recognized as url, g_strdup(url) is returned.
1833 vfs_translate_url (char *url
)
1835 if (strncmp (url
, "ftp://", 6) == 0)
1836 return g_strconcat ("/#ftp:", url
+ 6, NULL
);
1837 else if (strncmp (url
, "a:", 2) == 0)
1838 return g_strdup ("/#a");
1840 return g_strdup (url
);