1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* Warning: funtions like extfs_lstat() have right to destroy any
25 * strings you pass to them. This is acutally ok as you g_strdup what
26 * you are passing to them, anyway; still, beware. */
28 /* Namespace: exports *many* functions with vfs_ prefix; exports
29 parse_ls_lga and friends which do not have that prefix. */
38 #include <stdlib.h> /* For atol() */
42 #include <sys/types.h>
48 #include "../src/dir.h"
49 #include "../src/main.h"
50 #ifndef VFS_STANDALONE
51 #include "../src/panel.h"
52 #include "../src/key.h" /* Required for the async alarm handler */
53 #include "../src/layout.h" /* For get_panel_widget and get_other_index */
54 #include "../src/dialog.h"
57 #include "xdirentry.h"
59 #include "extfs.h" /* FIXME: we should not know anything about our modules */
65 extern int get_other_type (void);
67 int vfs_timeout
= 60; /* VFS timeout in seconds */
68 int vfs_flags
= 0; /* Flags */
70 extern int cd_symlinks
; /* Defined in main.c */
72 /* They keep track of the current directory */
73 static vfs
*current_vfs
= &vfs_local_ops
;
74 static char *current_dir
= NULL
;
77 * FIXME: this is broken. It depends on mc not crossing border on month!
79 static int current_mday
;
80 static int current_mon
;
81 static int current_year
;
86 /* FIXME: Open files managed by the vfs layer, should be dynamical */
87 #define MAX_VFS_FILES 100
92 } vfs_file_table
[MAX_VFS_FILES
];
99 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
100 for (i
= 3; i
< MAX_VFS_FILES
; i
++){
101 if (!vfs_file_table
[i
].fs_info
)
105 vfs_die ("No more virtual file handles");
109 /* vfs_local_ops needs to be the first one */
110 static vfs
*vfs_list
= &vfs_local_ops
;
113 vfs_register (vfs
*vfs
)
116 vfs_die("You can not register NULL.");
118 if (vfs
->init
) /* vfs has own initialization function */
119 if (!(*vfs
->init
)(vfs
)) /* but it failed */
122 vfs
->next
= vfs_list
;
129 vfs_type_from_op (char *path
)
134 vfs_die ("vfs_type_from_op got NULL: impossible");
136 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
138 if ((*vfs
->which
) (vfs
, path
) == -1)
142 if (!strncmp (path
, vfs
->prefix
, strlen (vfs
->prefix
)))
145 return NULL
; /* shut up stupid gcc */
148 /* Strip known vfs suffixes from a filename (possible improvement: strip
149 suffix from last path component).
150 Returns a malloced string which has to be freed. */
152 vfs_strip_suffix_from_filename (const char *filename
)
159 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
161 p
= g_strdup (filename
);
162 if (!(semi
= strrchr (p
, '#')))
165 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
167 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
169 *semi
= '\0'; /* Found valid suffix */
172 if (!strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
173 *semi
= '\0'; /* Found valid suffix */
181 path_magic (const char *path
)
185 if (vfs_flags
& FL_ALWAYS_MAGIC
)
188 if (!stat(path
, &buf
))
195 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
196 * What is left in path is p1. You still want to g_free(path), you DON'T
197 * want to free neither *inpath nor *op
200 vfs_split (char *path
, char **inpath
, char **op
)
207 vfs_die("Can not split NULL");
209 semi
= strrchr (path
, '#');
210 if (!semi
|| !path_magic(path
))
213 slash
= strchr (semi
, PATH_SEP
);
225 if ((ret
= vfs_type_from_op (semi
+1))){
229 *inpath
= slash
? slash
+ 1 : NULL
;
236 ret
= vfs_split (path
, inpath
, op
);
242 vfs_rosplit (char *path
)
248 g_return_val_if_fail(path
, NULL
);
250 semi
= strrchr (path
, '#');
251 if (!semi
|| !path_magic (path
))
254 slash
= strchr (semi
, PATH_SEP
);
259 ret
= vfs_type_from_op (semi
+1);
260 if (!ret
&& (vfs_flags
& FL_NO_LOCALHASH
))
266 ret
= vfs_rosplit (path
);
273 vfs_type (char *path
)
277 vfs
= vfs_rosplit(path
);
280 vfs
= &vfs_local_ops
;
285 static struct vfs_stamping
*stamps
;
288 * Returns the number of seconds remaining to the vfs timeout
290 * FIXME: currently this is set to 10 seconds. We should compute this.
295 return stamps
? 10 : 0;
299 vfs_addstamp (vfs
*v
, vfsid id
, struct vfs_stamping
*parent
)
301 if (v
!= &vfs_local_ops
&& id
!= (vfsid
)-1){
302 struct vfs_stamping
*stamp
, *st1
;
304 for (stamp
= stamps
; stamp
!= NULL
; st1
= stamp
, stamp
= stamp
->next
)
305 if (stamp
->v
== v
&& stamp
->id
== id
){
306 gettimeofday(&(stamp
->time
), NULL
);
309 stamp
= g_new (struct vfs_stamping
, 1);
313 struct vfs_stamping
*st
= stamp
;
315 st
->parent
= g_new (struct vfs_stamping
, 1);
316 *st
->parent
= *parent
;
317 parent
= parent
->parent
;
325 gettimeofday (&(stamp
->time
), NULL
);
336 vfs_stamp (vfs
*v
, vfsid id
)
338 struct vfs_stamping
*stamp
;
340 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
)
341 if (stamp
->v
== v
&& stamp
->id
== id
){
343 gettimeofday (&(stamp
->time
), NULL
);
344 if (stamp
->parent
!= NULL
)
345 vfs_stamp (stamp
->parent
->v
, stamp
->parent
->id
);
352 vfs_rm_parents (struct vfs_stamping
*stamp
)
354 struct vfs_stamping
*parent
;
357 parent
= stamp
->parent
;
364 vfs_rmstamp (vfs
*v
, vfsid id
, int removeparents
)
366 struct vfs_stamping
*stamp
, *st1
;
368 for (stamp
= stamps
, st1
= NULL
; stamp
!= NULL
; st1
= stamp
, stamp
= stamp
->next
)
369 if (stamp
->v
== v
&& stamp
->id
== id
){
370 if (stamp
->parent
!= NULL
){
372 vfs_rmstamp (stamp
->parent
->v
, stamp
->parent
->id
, 1);
373 vfs_rm_parents (stamp
->parent
);
376 stamps
= stamp
->next
;
378 st1
->next
= stamp
->next
;
389 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
390 /* Hope that error message is obscure enough ;-) */
394 mc_open (const char *filename
, int flags
, ...)
401 char *file
= vfs_canon (filename
);
402 vfs
*vfs
= vfs_type (file
);
404 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
405 va_start (ap
, flags
);
406 mode
= va_arg (ap
, int);
414 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
417 errno
= ferrno (vfs
);
420 handle
= get_bucket ();
421 vfs_file_table
[handle
].fs_info
= info
;
422 vfs_file_table
[handle
].operations
= vfs
;
427 #define vfs_op(handle) vfs_file_table [handle].operations
428 #define vfs_info(handle) vfs_file_table [handle].fs_info
429 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
431 #define MC_OP(name, inarg, callarg, pre, post) \
432 int mc_##name inarg \
438 result = vfs->name ? (*vfs->name)callarg : -1; \
441 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
445 #define MC_NAMEOP(name, inarg, callarg) \
446 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
447 #define MC_HANDLEOP(name, inarg, callarg) \
448 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
450 MC_HANDLEOP(read
, (int handle
, char *buffer
, int count
), (vfs_info (handle
), buffer
, count
) )
453 mc_ctl (int handle
, int ctlop
, int arg
)
455 vfs
*vfs
= vfs_op (handle
);
457 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
461 mc_setctl (char *path
, int ctlop
, char *arg
)
467 vfs_die("You don't want to pass NULL to mc_setctl.");
469 path
= vfs_canon (path
);
470 vfs
= vfs_type (path
);
471 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, path
, ctlop
, arg
) : 0;
477 mc_close (int handle
)
482 if (handle
== -1 || !vfs_info (handle
))
485 vfs
= vfs_op (handle
);
487 return close (handle
);
490 vfs_die ("VFS must support close.\n");
491 result
= (*vfs
->close
)(vfs_info (handle
));
492 vfs_free_bucket (handle
);
494 errno
= ferrno (vfs
);
500 mc_opendir (char *dirname
)
502 int handle
, *handlep
;
506 dirname
= vfs_canon (dirname
);
507 vfs
= vfs_type (dirname
);
509 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dirname
) : NULL
;
512 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
515 handle
= get_bucket ();
516 vfs_file_table
[handle
].fs_info
= info
;
517 vfs_file_table
[handle
].operations
= vfs
;
519 handlep
= g_new (int, 1);
521 return (DIR *) handlep
;
524 /* This should strip the non needed part of a path name */
525 #define vfs_name(x) x
528 mc_seekdir (DIR *dirp
, int offset
)
537 handle
= *(int *) dirp
;
538 vfs
= vfs_op (handle
);
540 (*vfs
->seekdir
) (vfs_info (handle
), offset
);
545 #define MC_DIROP(name, type, onerr ) \
546 type mc_##name (DIR *dirp) \
556 handle = *(int *) dirp; \
557 vfs = vfs_op (handle); \
558 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
559 if (result == onerr) \
560 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
564 MC_DIROP (readdir
, struct dirent
*, NULL
)
565 MC_DIROP (telldir
, int, -1)
568 mc_closedir (DIR *dirp
)
570 int handle
= *(int *) dirp
;
571 vfs
*vfs
= vfs_op (handle
);
574 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
575 vfs_free_bucket (handle
);
580 MC_NAMEOP (stat
, (char *path
, struct stat
*buf
), (vfs
, vfs_name (path
), buf
))
581 MC_NAMEOP (lstat
, (char *path
, struct stat
*buf
), (vfs
, vfs_name (path
), buf
))
582 MC_HANDLEOP (fstat
, (int handle
, struct stat
*buf
), (vfs_info (handle
), buf
))
585 * You must g_strdup whatever this function returns, static buffers are in use
592 struct stat my_stat
, my_stat2
;
594 if (!vfs_rosplit (current_dir
)){
595 static char buffer
[MC_MAXPATHLEN
];
597 p
= get_current_wd (buffer
, MC_MAXPATHLEN
);
598 if (!p
) /* One of the directories in the path is not readable */
601 /* Otherwise check if it is O.K. to use the current_dir */
602 mc_stat (p
, &my_stat
);
603 mc_stat (current_dir
, &my_stat2
);
604 if (my_stat
.st_ino
!= my_stat2
.st_ino
||
605 my_stat
.st_dev
!= my_stat2
.st_dev
||
607 g_free (current_dir
);
608 current_dir
= g_strdup (p
);
610 } /* Otherwise we return current_dir below */
616 mc_get_current_wd (char *buffer
, int size
)
618 char *cwd
= mc_return_cwd();
620 strncpy (buffer
, cwd
, size
);
624 MC_NAMEOP (chmod
, (char *path
, int mode
), (vfs
, vfs_name (path
), mode
))
625 MC_NAMEOP (chown
, (char *path
, int owner
, int group
), (vfs
, vfs_name (path
), owner
, group
))
626 MC_NAMEOP (utime
, (char *path
, struct utimbuf
*times
), (vfs
, vfs_name (path
), times
))
627 MC_NAMEOP (readlink
, (char *path
, char *buf
, int bufsiz
), (vfs
, vfs_name (path
), buf
, bufsiz
))
628 MC_NAMEOP (unlink
, (char *path
), (vfs
, vfs_name (path
)))
629 MC_NAMEOP (symlink
, (char *name1
, char *path
), (vfs
, vfs_name (name1
), vfs_name (path
)))
631 #define MC_RENAMEOP(name) \
632 int mc_##name (char *name1, char *name2) \
637 name1 = vfs_canon (name1); \
638 vfs = vfs_type (name1); \
639 name2 = vfs_canon (name2); \
640 if (vfs != vfs_type (name2)){ \
647 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
651 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
658 MC_HANDLEOP (write
, (int handle
, char *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
660 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
669 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
671 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
676 * remove //, /./ and /../, local should point to big enough buffer
679 #define ISSLASH(a) (!a || (a == '/'))
682 vfs_canon (const char *path
)
685 vfs_die("Can not canonize NULL");
687 /* Tilde expansion */
689 char *local
, *result
;
691 local
= tilde_expand (path
);
693 result
= vfs_canon (local
);
699 /* Relative to current directory */
700 if (*path
!= PATH_SEP
){
701 char *local
, *result
;
703 local
= concat_dir_and_file (current_dir
, path
);
705 result
= vfs_canon (local
);
711 * So we have path of following form:
712 * /p1/p2#op/.././././p3#op/p4. Good luck.
715 char *result
= g_strdup (path
);
717 mad_check("(pre-canonicalize)", 0);
718 canonicalize_pathname (result
);
719 mad_check("(post-canonicalize)", 0);
726 vfs_ncs_getid (vfs
*nvfs
, char *dir
, struct vfs_stamping
**par
)
730 dir
= concat_dir_and_file (dir
, "");
732 nvfsid
= (*nvfs
->getid
)(nvfs
, dir
, par
);
739 is_parent (vfs
* nvfs
, vfsid nvfsid
, struct vfs_stamping
*parent
)
741 struct vfs_stamping
*stamp
;
743 for (stamp
= parent
; stamp
; stamp
= stamp
->parent
)
744 if (stamp
->v
== nvfs
&& stamp
->id
== nvfsid
)
747 return (stamp
? 1 : 0);
751 vfs_add_noncurrent_stamps (vfs
* oldvfs
, vfsid oldvfsid
, struct vfs_stamping
*parent
)
753 #ifndef VFS_STANDALONE
754 vfs
*nvfs
, *n2vfs
, *n3vfs
;
755 vfsid nvfsid
, n2vfsid
, n3vfsid
;
756 struct vfs_stamping
*par
, *stamp
;
759 /* FIXME: As soon as we convert to multiple panels, this stuff
760 has to change. It works like this: We do not time out the
761 vfs's which are current in any panel and on the other
762 side we add the old directory with all its parents which
763 are not in any panel (if we find such one, we stop adding
764 parents to the time-outing structure. */
766 /* There are three directories we have to take care of: current_dir,
767 cpanel->cwd and opanel->cwd. Athough most of the time either
768 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
769 same, it's possible that all three are different -- Norbert */
774 nvfs
= vfs_type (current_dir
);
775 nvfsid
= vfs_ncs_getid (nvfs
, current_dir
, &par
);
776 vfs_rmstamp (nvfs
, nvfsid
, 1);
778 f
= is_parent (oldvfs
, oldvfsid
, par
);
779 vfs_rm_parents (par
);
780 if ((nvfs
== oldvfs
&& nvfsid
== oldvfsid
) || oldvfsid
== (vfsid
*)-1 || f
){
784 if (get_current_type () == view_listing
){
785 n2vfs
= vfs_type (cpanel
->cwd
);
786 n2vfsid
= vfs_ncs_getid (n2vfs
, cpanel
->cwd
, &par
);
787 f
= is_parent (oldvfs
, oldvfsid
, par
);
788 vfs_rm_parents (par
);
789 if ((n2vfs
== oldvfs
&& n2vfsid
== oldvfsid
) || f
)
793 n2vfsid
= (vfs
*) -1;
796 if (get_other_type () == view_listing
){
797 n3vfs
= vfs_type (opanel
->cwd
);
798 n3vfsid
= vfs_ncs_getid (n3vfs
, opanel
->cwd
, &par
);
799 f
= is_parent (oldvfs
, oldvfsid
, par
);
800 vfs_rm_parents (par
);
801 if ((n3vfs
== oldvfs
&& n3vfsid
== oldvfsid
) || f
)
808 if ((*oldvfs
->nothingisopen
) (oldvfsid
)){
809 if (oldvfs
== &vfs_extfs_ops
&& ((extfs_archive
*) oldvfsid
)->name
== 0){
810 /* Free the resources immediatly when we leave a mtools fs
811 ('cd a:') instead of waiting for the vfs-timeout */
812 (oldvfs
->free
) (oldvfsid
);
814 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
815 for (stamp
= parent
; stamp
!= NULL
; stamp
= stamp
->parent
){
816 if ((stamp
->v
== nvfs
&& stamp
->id
== nvfsid
) ||
817 (stamp
->v
== n2vfs
&& stamp
->id
== n2vfsid
) ||
818 (stamp
->v
== n3vfs
&& stamp
->id
== n3vfsid
) ||
819 stamp
->id
== (vfsid
) - 1 ||
820 !(*stamp
->v
->nothingisopen
) (stamp
->id
))
822 if (stamp
->v
== &vfs_extfs_ops
&& ((extfs_archive
*) stamp
->id
)->name
== 0){
823 (stamp
->v
->free
) (stamp
->id
);
824 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
826 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
830 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
835 vfs_stamp_path (char *path
)
839 struct vfs_stamping
*par
, *stamp
;
841 vfs
= vfs_type (path
);
842 id
= vfs_ncs_getid (vfs
, path
, &par
);
843 vfs_addstamp (vfs
, id
, par
);
845 for (stamp
= par
; stamp
!= NULL
; stamp
= stamp
->parent
)
846 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
847 vfs_rm_parents (par
);
850 #ifndef VFS_STANDALONE
852 vfs_add_current_stamps (void)
854 vfs_stamp_path (current_dir
);
857 if (get_current_type () == view_listing
)
858 vfs_stamp_path (cpanel
->cwd
);
862 if (get_other_type () == view_listing
)
863 vfs_stamp_path (opanel
->cwd
);
868 /* This function is really broken */
870 mc_chdir (char *path
)
877 struct vfs_stamping
*parent
;
879 a
= current_dir
; /* Save a copy for case of failure */
880 current_dir
= vfs_canon (path
);
881 current_vfs
= vfs_type (current_dir
);
882 b
= g_strdup (current_dir
);
883 result
= (*current_vfs
->chdir
) ? (*current_vfs
->chdir
)(current_vfs
, vfs_name (b
)) : -1;
886 errno
= ferrno (current_vfs
);
887 g_free (current_dir
);
888 current_vfs
= vfs_type (a
);
891 oldvfs
= vfs_type (a
);
892 oldvfsid
= vfs_ncs_getid (oldvfs
, a
, &parent
);
894 vfs_add_noncurrent_stamps (oldvfs
, oldvfsid
, parent
);
895 vfs_rm_parents (parent
);
899 p
= strchr (current_dir
, 0) - 1;
900 if (*p
== PATH_SEP
&& p
> current_dir
)
901 *p
= 0; /* Sometimes we assume no trailing slash on cwd */
907 vfs_current_is_local (void)
909 return current_vfs
== &vfs_local_ops
;
913 /* External world should not do differences between VFS-s */
915 vfs_current_is_extfs (void)
917 return current_vfs
== &vfs_extfs_ops
;
921 vfs_current_is_tarfs (void)
923 return current_vfs
== &vfs_tarfs_ops
;
927 vfs_current_is_cpiofs (void)
929 return current_vfs
== &vfs_cpiofs_ops
;
934 vfs_file_is_local (const char *file
)
936 char *filename
= vfs_canon (file
);
937 vfs
*vfs
= vfs_type (filename
);
940 return vfs
== &vfs_local_ops
;
944 vfs_file_is_ftp (char *filename
)
949 filename
= vfs_canon (filename
);
950 vfs
= vfs_type (filename
);
952 return vfs
== &vfs_ftpfs_ops
;
959 vfs_file_is_smb (char *filename
)
965 filename
= vfs_canon (filename
);
966 vfs
= vfs_type (filename
);
968 return vfs
== &vfs_smbfs_ops
;
969 #endif /* USE_NETCODE */
970 #endif /* WITH_SMBFS */
974 char *vfs_get_current_dir (void)
979 static void vfs_setup_wd (void)
981 current_dir
= g_strdup (PATH_SEP_STR
);
982 if (!(vfs_flags
& FL_NO_CWDSETUP
))
985 if (strlen(current_dir
)>MC_MAXPATHLEN
-2)
986 vfs_die ("Current dir too long.\n");
989 MC_NAMEOP (mkdir
, (char *path
, mode_t mode
), (vfs
, vfs_name (path
), mode
))
990 MC_NAMEOP (rmdir
, (char *path
), (vfs
, vfs_name (path
)))
991 MC_NAMEOP (mknod
, (char *path
, int mode
, int dev
), (vfs
, vfs_name (path
), mode
, dev
))
994 static struct mc_mmapping
{
998 struct mc_mmapping
*next
;
999 } *mc_mmaparray
= NULL
;
1002 mc_mmap (caddr_t addr
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
1006 struct mc_mmapping
*mcm
;
1009 return (caddr_t
) -1;
1012 result
= vfs
->mmap
? (*vfs
->mmap
)(vfs
, addr
, len
, prot
, flags
, vfs_info (fd
), offset
) : (caddr_t
)-1;
1013 if (result
== (caddr_t
)-1){
1014 errno
= ferrno (vfs
);
1017 mcm
=g_new (struct mc_mmapping
, 1);
1019 mcm
->vfs_info
= vfs_info (fd
);
1021 mcm
->next
= mc_mmaparray
;
1027 mc_munmap (caddr_t addr
, size_t len
)
1029 struct mc_mmapping
*mcm
, *mcm2
= NULL
;
1031 for (mcm
= mc_mmaparray
; mcm
!= NULL
; mcm2
= mcm
, mcm
= mcm
->next
){
1032 if (mcm
->addr
== addr
){
1034 mc_mmaparray
= mcm
->next
;
1036 mcm2
->next
= mcm
->next
;
1037 if (mcm
->vfs
->munmap
)
1038 (*mcm
->vfs
->munmap
)(mcm
->vfs
, addr
, len
, mcm
->vfs_info
);
1049 mc_def_getlocalcopy (vfs
*vfs
, char *filename
)
1056 fdin
= mc_open (filename
, O_RDONLY
);
1059 tmp
= g_tempnam (NULL
, "mclocalcopy");
1060 fdout
= open (tmp
, O_CREAT
|O_WRONLY
|O_TRUNC
|O_EXCL
, 0600);
1063 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0){
1064 if (write (fdout
, buffer
, i
) != i
)
1069 i
= mc_close (fdin
);
1073 if (close (fdout
)==-1)
1076 if (mc_stat (filename
, &mystat
) != -1){
1077 chmod (tmp
, mystat
.st_mode
);
1082 if (fdout
) close(fdout
);
1083 if (fdin
) mc_close (fdin
);
1089 mc_getlocalcopy (const char *pathname
)
1092 char *path
= vfs_canon (pathname
);
1093 vfs
*vfs
= vfs_type (path
);
1095 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, vfs_name (path
)) :
1096 mc_def_getlocalcopy (vfs
, vfs_name (path
));
1099 errno
= ferrno (vfs
);
1104 mc_def_ungetlocalcopy (vfs
*vfs
, char *filename
, char *local
, int has_changed
)
1105 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1106 int fdin
= -1, fdout
= -1, i
;
1110 fdin
= open (local
, O_RDONLY
);
1113 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
1116 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0){
1117 if (mc_write (fdout
, buffer
, i
) != i
)
1123 if (close (fdin
)==-1) {
1128 if (mc_close (fdout
)==-1) {
1138 message_1s (1, _("Changes to file lost"), filename
);
1139 if (fdout
!=-1) mc_close(fdout
);
1140 if (fdin
!=-1) close(fdin
);
1147 mc_ungetlocalcopy (const char *pathname
, char *local
, int has_changed
)
1149 int return_value
= 0;
1150 char *path
= vfs_canon (pathname
);
1151 vfs
*vfs
= vfs_type (path
);
1153 return_value
= vfs
->ungetlocalcopy
?
1154 (*vfs
->ungetlocalcopy
)(vfs
, vfs_name (path
), local
, has_changed
) :
1155 mc_def_ungetlocalcopy (vfs
, vfs_name (path
), local
, has_changed
);
1157 return return_value
;
1161 * Hmm, as timeout is minute or so, do we need to care about usecs?
1164 timeoutcmp (struct timeval
*t1
, struct timeval
*t2
)
1166 return ((t1
->tv_sec
< t2
->tv_sec
)
1167 || ((t1
->tv_sec
== t2
->tv_sec
) && (t1
->tv_usec
<= t2
->tv_usec
)));
1170 /* This is called from timeout handler with now = 0, or can be called
1171 with now = 1 to force freeing all filesystems that are not in use */
1174 vfs_expire (int now
)
1176 static int locked
= 0;
1177 struct timeval time
;
1178 struct vfs_stamping
*stamp
, *st
;
1180 /* Avoid recursive invocation, e.g. when one of the free functions
1186 gettimeofday (&time
, NULL
);
1187 time
.tv_sec
-= vfs_timeout
;
1189 for (stamp
= stamps
; stamp
!= NULL
;){
1190 if (now
|| (timeoutcmp (&stamp
->time
, &time
))){
1192 (*stamp
->v
->free
) (stamp
->id
);
1193 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
1196 stamp
= stamp
->next
;
1202 vfs_timeout_handler (void)
1210 time_t current_time
;
1213 memset (vfs_file_table
, 0, sizeof (vfs_file_table
));
1214 current_time
= time (NULL
);
1215 t
= localtime (¤t_time
);
1216 current_mday
= t
->tm_mday
;
1217 current_mon
= t
->tm_mon
;
1218 current_year
= t
->tm_year
;
1220 /* We do not want to register vfs_local_ops */
1224 vfs_register (&vfs_ftpfs_ops
);
1226 vfs_register (&vfs_smbfs_ops
);
1228 vfs_register (&vfs_mcfs_ops
);
1231 vfs_register (&vfs_fish_ops
);
1232 vfs_register (&vfs_extfs_ops
);
1233 vfs_register (&vfs_sfs_ops
);
1234 vfs_register (&vfs_tarfs_ops
);
1235 vfs_register (&vfs_cpiofs_ops
);
1237 #ifdef USE_EXT2FSLIB
1238 vfs_register (&vfs_undelfs_ops
);
1245 vfs_free_resources (char *path
)
1249 struct vfs_stamping
*parent
;
1251 vfs
= vfs_type (path
);
1252 vid
= vfs_ncs_getid (vfs
, path
, &parent
);
1253 if (vid
!= (vfsid
) -1)
1255 vfs_rm_parents (parent
);
1259 /* Shutdown a vfs given a path name */
1261 vfs_shut_path (char *p
)
1264 struct vfs_stamping
*par
;
1266 the_vfs
= vfs_type (p
);
1267 vfs_ncs_getid (the_vfs
, p
, &par
);
1268 (*par
->v
->free
)(par
->id
);
1269 vfs_rm_parents (par
);
1276 struct vfs_stamping
*stamp
, *st
;
1279 for (stamp
= stamps
, stamps
= 0; stamp
!= NULL
;){
1280 (*stamp
->v
->free
)(stamp
->id
);
1287 vfs_rmstamp (stamps
->v
, stamps
->id
, 1);
1290 g_free (current_dir
);
1292 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1298 * These ones grab information from the VFS
1299 * and handles them to an upper layer
1302 vfs_fill_names (void (*func
)(char *))
1306 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1307 if (vfs
->fill_names
)
1308 (*vfs
->fill_names
) (vfs
, func
);
1311 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1314 static char *columns
[MAXCOLS
]; /* Points to the string in column n */
1315 static int column_ptr
[MAXCOLS
]; /* Index from 0 to the starting positions of the columns */
1318 vfs_split_text (char *p
)
1323 for (numcols
= 0; *p
&& numcols
< MAXCOLS
; numcols
++){
1324 while (*p
== ' ' || *p
== '\r' || *p
== '\n'){
1328 columns
[numcols
] = p
;
1329 column_ptr
[numcols
] = p
- original
;
1330 while (*p
&& *p
!= ' ' && *p
!= '\r' && *p
!= '\n')
1339 if (!columns
[idx
] || columns
[idx
][0] < '0' || columns
[idx
][0] > '9')
1345 is_dos_date(char *str
)
1347 if (strlen(str
) == 8 && str
[2] == str
[5] && strchr("\\-/", (int)str
[2]) != NULL
)
1354 is_week (char *str
, struct tm
*tim
)
1356 static char *week
= "SunMonTueWedThuFriSat";
1359 if((pos
=strstr(week
, str
)) != NULL
){
1361 tim
->tm_wday
= (pos
- week
)/3;
1368 is_month (char *str
, struct tm
*tim
)
1370 static 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
)
1386 if ((p
=strchr(str
, ':')) && (p2
=strrchr(str
, ':'))) {
1388 if (sscanf (str
, "%2d:%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
, &tim
->tm_sec
) != 3)
1392 if (sscanf (str
, "%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
) != 2)
1402 static int is_year(char *str
, struct tm
*tim
)
1406 if (strchr(str
,':'))
1412 if (sscanf(str
, "%ld", &year
) != 1)
1415 if (year
< 1900 || year
> 3000)
1418 tim
->tm_year
= (int) (year
- 1900);
1424 * FIXME: this is broken. Consider following entry:
1425 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1426 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1430 vfs_parse_filetype (char c
)
1433 case 'd': return S_IFDIR
;
1434 case 'b': return S_IFBLK
;
1435 case 'c': return S_IFCHR
;
1436 case 'l': return S_IFLNK
;
1438 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty close */
1441 case 'p': return S_IFIFO
;
1442 case 'm': case 'n': /* Don't know what these are :-) */
1443 case '-': case '?': return S_IFREG
;
1448 int vfs_parse_filemode (const char *p
)
1449 { /* converts rw-rw-rw- into 0666 */
1452 case 'r': res
|= 0400; break;
1457 case 'w': res
|= 0200; break;
1462 case 'x': res
|= 0100; break;
1463 case 's': res
|= 0100 | S_ISUID
; break;
1464 case 'S': res
|= S_ISUID
; break;
1469 case 'r': res
|= 0040; break;
1474 case 'w': res
|= 0020; break;
1479 case 'x': res
|= 0010; break;
1480 case 's': res
|= 0010 | S_ISGID
; break;
1481 case 'l': /* Solaris produces these */
1482 case 'S': res
|= S_ISGID
; break;
1487 case 'r': res
|= 0004; break;
1492 case 'w': res
|= 0002; break;
1497 case 'x': res
|= 0001; break;
1498 case 't': res
|= 0001 | S_ISVTX
; break;
1499 case 'T': res
|= S_ISVTX
; break;
1506 int vfs_parse_filedate(int idx
, time_t *t
)
1507 { /* This thing parses from idx in columns[] array */
1514 /* Let's setup default time values */
1515 tim
.tm_year
= current_year
;
1516 tim
.tm_mon
= current_mon
;
1517 tim
.tm_mday
= current_mday
;
1521 tim
.tm_isdst
= -1; /* Let mktime() try to guess correct dst offset */
1523 p
= columns
[idx
++];
1525 /* We eat weekday name in case of extfs */
1526 if(is_week(p
, &tim
))
1527 p
= columns
[idx
++];
1530 if(is_month(p
, &tim
)){
1531 /* And we expect, it followed by day number */
1533 tim
.tm_mday
= (int)atol (columns
[idx
++]);
1535 return 0; /* No day */
1538 /* We usually expect:
1541 But in case of extfs we allow these date formats:
1544 Wek Mon DD hh:mm:ss YYYY
1546 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1547 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1549 /* Here just this special case with MM-DD-YY */
1550 if (is_dos_date(p
)){
1553 if(sscanf(p
, "%2d-%2d-%2d", &d
[0], &d
[1], &d
[2]) == 3){
1554 /* We expect to get:
1560 /* Hmm... maybe, next time :)*/
1562 /* At last, MM-DD-YY */
1563 d
[0]--; /* Months are zerobased */
1573 return 0; /* sscanf failed */
1575 return 0; /* unsupported format */
1578 /* Here we expect to find time and/or year */
1581 if(is_time(columns
[idx
], &tim
) || (got_year
= is_year(columns
[idx
], &tim
))) {
1584 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1585 if(is_num (idx
) && (columns
[idx
+1][0]) &&
1586 ((got_year
|= is_year(columns
[idx
], &tim
)) || is_time(columns
[idx
], &tim
)))
1587 idx
++; /* time & year or reverse */
1588 } /* only time or date */
1591 return 0; /* Nor time or date */
1594 * If the date is less than 6 months in the past, it is shown without year
1595 * other dates in the past or future are shown with year but without time
1596 * This does not check for years before 1900 ... I don't know, how
1597 * to represent them at all
1600 current_mon
< 6 && current_mon
< tim
.tm_mon
&&
1601 tim
.tm_mon
- current_mon
>= 6)
1605 if ((*t
= mktime(&tim
)) < 0)
1611 vfs_parse_ls_lga (const char *p
, struct stat
*s
, char **filename
, char **linkname
)
1613 int idx
, idx2
, num_cols
;
1618 if (strncmp (p
, "total", 5) == 0)
1621 p_copy
= g_strdup(p
);
1622 if ((i
= vfs_parse_filetype(*(p
++))) == -1)
1626 if (*p
== ' ') /* Notwell 4 */
1629 if (strlen (p
) <= 8 || p
[8] != ']')
1631 /* Should parse here the Notwell permissions :) */
1632 if (S_ISDIR (s
->st_mode
))
1633 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
);
1635 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
);
1638 if ((i
= vfs_parse_filemode(p
)) == -1)
1643 /* This is for an extra ACL attribute (HP-UX) */
1649 p_copy
= g_strdup(p
);
1650 num_cols
= vfs_split_text (p_copy
);
1652 s
->st_nlink
= atol (columns
[0]);
1653 if (s
->st_nlink
< 0)
1657 s
->st_uid
= finduid (columns
[1]);
1659 s
->st_uid
= (uid_t
) atol (columns
[1]);
1661 /* Mhm, the ls -lg did not produce a group field */
1662 for (idx
= 3; idx
<= 5; idx
++)
1663 if (is_month(columns
[idx
], NULL
) || is_week(columns
[idx
], NULL
) || is_dos_date(columns
[idx
]))
1666 if (idx
== 6 || (idx
== 5 && !S_ISCHR (s
->st_mode
) && !S_ISBLK (s
->st_mode
)))
1669 /* We don't have gid */
1670 if (idx
== 3 || (idx
== 4 && (S_ISCHR(s
->st_mode
) || S_ISBLK (s
->st_mode
))))
1673 /* We have gid field */
1675 s
->st_gid
= (gid_t
) atol (columns
[2]);
1677 s
->st_gid
= findgid (columns
[2]);
1681 /* This is device */
1682 if (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
)){
1685 if (!is_num (idx2
) || sscanf(columns
[idx2
], " %d,", &maj
) != 1)
1688 if (!is_num (++idx2
) || sscanf(columns
[idx2
], " %d", &min
) != 1)
1692 s
->st_rdev
= ((maj
& 0xff) << 8) | (min
& 0xffff00ff);
1697 /* Common file size */
1701 s
->st_size
= (size_t) atol (columns
[idx2
]);
1707 idx
= vfs_parse_filedate(idx
, &s
->st_mtime
);
1710 /* Use resulting time value */
1711 s
->st_atime
= s
->st_ctime
= s
->st_mtime
;
1713 /* These variables must be initialized by vfs_s_new_inode () */
1717 #ifdef HAVE_ST_BLKSIZE
1718 s
->st_blksize
= 512;
1720 #ifdef HAVE_ST_BLOCKS
1721 s
->st_blocks
= (s
->st_size
+ 511) / 512;
1724 for (i
= idx
+ 1, idx2
= 0; i
< num_cols
; i
++ )
1725 if (strcmp (columns
[i
], "->") == 0){
1730 if (((S_ISLNK (s
->st_mode
) ||
1731 (num_cols
== idx
+ 3 && s
->st_nlink
> 1))) /* Maybe a hardlink? (in extfs) */
1735 *filename
= g_strndup (p
+ column_ptr
[idx
], column_ptr
[idx2
] - column_ptr
[idx
] - 1);
1738 t
= g_strdup (p
+ column_ptr
[idx2
+1]);
1742 /* Extract the filename from the string copy, not from the columns
1743 * this way we have a chance of entering hidden directories like ". ."
1747 *filename = g_strdup (columns [idx++]);
1750 t
= g_strdup (p
+ column_ptr
[idx
]);
1759 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1761 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1770 static int errorcount
= 0;
1772 if (++errorcount
< 5) {
1773 message_1s (1, _("Could not parse:"), p_copy
);
1774 } else if (errorcount
== 5)
1775 message_1s (1, _("More parsing errors will be ignored."), _("(sorry)"));
1785 message_1s (1, _("Internal error:"), m
);
1790 vfs_print_stats (char *fs_name
, char *action
, char *file_name
, int have
, int need
)
1792 static char *i18n_percent_transf_format
= NULL
, *i18n_transf_format
= NULL
;
1794 if (i18n_percent_transf_format
== NULL
) {
1795 i18n_percent_transf_format
= _("%s: %s: %s %3d%% (%ld bytes transfered)");
1796 i18n_transf_format
= _("%s: %s: %s %ld bytes transfered");
1800 print_vfs_message (i18n_percent_transf_format
,
1801 fs_name
, action
, file_name
, have
*100/need
, have
);
1803 print_vfs_message (i18n_transf_format
,
1804 fs_name
, action
, file_name
, have
);
1807 #ifndef VFS_STANDALONE
1809 vfs_get_password (char *msg
)
1811 return (char *) input_dialog (msg
, _("Password:"), "");
1816 * Returns vfs path corresponding to given url. If passed string is
1817 * not recognized as url, g_strdup(url) is returned.
1820 vfs_translate_url (char *url
)
1822 if (strncmp (url
, "ftp://", 6) == 0)
1823 return g_strconcat ("/#ftp:", url
+ 6, NULL
);
1824 else if (strncmp (url
, "a:", 2) == 0)
1825 return g_strdup ("/#a");
1827 return g_strdup (url
);