*** empty log message ***
[midnight-commander.git] / vfs / vfs.c
blob2c68aa339bcdb3dc8e121568da47623ab9d78e4f
1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
5 1995 Jakub Jelinek
6 1998 Pavel Machek
8 $Id$
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. */
31 #include <config.h>
33 #ifndef NO_SYSLOG_H
34 # include <syslog.h>
35 #endif
37 #include <stdio.h>
38 #include <stdlib.h> /* For atol() */
39 #include <stdarg.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <sys/types.h>
43 #include <fcntl.h>
44 #include <signal.h>
46 #include "utilvfs.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"
55 #endif
57 #include "xdirentry.h"
58 #include "vfs.h"
59 #include "extfs.h" /* FIXME: we should not know anything about our modules */
60 #include "names.h"
61 #ifdef USE_NETCODE
62 # include "tcputil.h"
63 #endif
65 #ifdef VFS_STANDALONE
66 #undef WITH_SMBFS
67 #endif
69 extern int get_other_type (void);
71 int vfs_timeout = 60; /* VFS timeout in seconds */
72 int vfs_flags = 0; /* Flags */
74 extern int cd_symlinks; /* Defined in main.c */
76 /* They keep track of the current directory */
77 static vfs *current_vfs = &vfs_local_ops;
78 static char *current_dir = NULL;
81 * FIXME: this is broken. It depends on mc not crossing border on month!
83 static int current_mday;
84 static int current_mon;
85 static int current_year;
87 uid_t vfs_uid = 0;
88 gid_t vfs_gid = 0;
90 /* FIXME: Open files managed by the vfs layer, should be dynamical */
91 #define MAX_VFS_FILES 100
93 static struct {
94 void *fs_info;
95 vfs *operations;
96 } vfs_file_table [MAX_VFS_FILES];
98 static int
99 get_bucket (void)
101 int i;
103 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
104 for (i = 3; i < MAX_VFS_FILES; i++){
105 if (!vfs_file_table [i].fs_info)
106 return i;
109 vfs_die ("No more virtual file handles");
110 return 0;
113 /* vfs_local_ops needs to be the first one */
114 static vfs *vfs_list = &vfs_local_ops;
116 static int
117 vfs_register (vfs *vfs)
119 if (!vfs)
120 vfs_die("You can not register NULL.");
122 if (vfs->init) /* vfs has own initialization function */
123 if (!(*vfs->init)(vfs)) /* but it failed */
124 return 0;
126 vfs->next = vfs_list;
127 vfs_list = vfs;
129 return 1;
132 static vfs *
133 vfs_type_from_op (char *path)
135 vfs *vfs;
137 if (!path)
138 vfs_die ("vfs_type_from_op got NULL: impossible");
140 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
141 if (vfs->which) {
142 if ((*vfs->which) (vfs, path) == -1)
143 continue;
144 return vfs;
146 if (!strncmp (path, vfs->prefix, strlen (vfs->prefix)))
147 return vfs;
149 return NULL; /* shut up stupid gcc */
152 /* Strip known vfs suffixes from a filename (possible improvement: strip
153 suffix from last path component).
154 Returns a malloced string which has to be freed. */
155 char *
156 vfs_strip_suffix_from_filename (const char *filename)
158 vfs *vfs;
159 char *semi;
160 char *p;
162 if (!filename)
163 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
165 p = g_strdup (filename);
166 if (!(semi = strrchr (p, '#')))
167 return p;
169 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
170 if (vfs->which){
171 if ((*vfs->which) (vfs, semi + 1) == -1)
172 continue;
173 *semi = '\0'; /* Found valid suffix */
174 return p;
176 if (!strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
177 *semi = '\0'; /* Found valid suffix */
178 return p;
181 return p;
184 static int
185 path_magic (const char *path)
187 struct stat buf;
189 if (vfs_flags & FL_ALWAYS_MAGIC)
190 return 1;
192 if (!stat(path, &buf))
193 return 0;
195 return 1;
199 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
200 * What is left in path is p1. You still want to g_free(path), you DON'T
201 * want to free neither *inpath nor *op
203 vfs *
204 vfs_split (char *path, char **inpath, char **op)
206 char *semi;
207 char *slash;
208 vfs *ret;
210 if (!path)
211 vfs_die("Can not split NULL");
213 semi = strrchr (path, '#');
214 if (!semi || !path_magic(path))
215 return NULL;
217 slash = strchr (semi, PATH_SEP);
218 *semi = 0;
220 if (op)
221 *op = NULL;
223 if (inpath)
224 *inpath = NULL;
226 if (slash)
227 *slash = 0;
229 if ((ret = vfs_type_from_op (semi+1))){
230 if (op)
231 *op = semi + 1;
232 if (inpath)
233 *inpath = slash ? slash + 1 : NULL;
234 return ret;
238 if (slash)
239 *slash = PATH_SEP;
240 ret = vfs_split (path, inpath, op);
241 *semi = '#';
242 return ret;
245 static vfs *
246 vfs_rosplit (char *path)
248 char *semi;
249 char *slash;
250 vfs *ret;
252 g_return_val_if_fail(path, NULL);
254 semi = strrchr (path, '#');
255 if (!semi || !path_magic (path))
256 return NULL;
258 slash = strchr (semi, PATH_SEP);
259 *semi = 0;
260 if (slash)
261 *slash = 0;
263 ret = vfs_type_from_op (semi+1);
264 if (!ret && (vfs_flags & FL_NO_LOCALHASH))
265 return &vfs_nil_ops;
267 if (slash)
268 *slash = PATH_SEP;
269 if (!ret)
270 ret = vfs_rosplit (path);
272 *semi = '#';
273 return ret;
276 vfs *
277 vfs_type (char *path)
279 vfs *vfs;
281 vfs = vfs_rosplit(path);
283 if (!vfs)
284 vfs = &vfs_local_ops;
286 return vfs;
289 static struct vfs_stamping *stamps;
292 * Returns the number of seconds remaining to the vfs timeout
294 * FIXME: currently this is set to 10 seconds. We should compute this.
297 vfs_timeouts ()
299 return stamps ? 10 : 0;
302 void
303 vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
305 if (v != &vfs_local_ops && id != (vfsid)-1){
306 struct vfs_stamping *stamp;
307 struct vfs_stamping *last_stamp = NULL;
309 for (stamp = stamps; stamp != NULL; stamp = stamp->next) {
310 if (stamp->v == v && stamp->id == id){
311 gettimeofday(&(stamp->time), NULL);
312 return;
314 last_stamp = stamp;
316 stamp = g_new (struct vfs_stamping, 1);
317 stamp->v = v;
318 stamp->id = id;
319 if (parent){
320 struct vfs_stamping *st = stamp;
321 while (parent){
322 st->parent = g_new (struct vfs_stamping, 1);
323 *st->parent = *parent;
324 parent = parent->parent;
325 st = st->parent;
327 st->parent = 0;
329 else
330 stamp->parent = 0;
332 gettimeofday (&(stamp->time), NULL);
333 stamp->next = 0;
335 if (stamps) {
336 /* Add to the end */
337 last_stamp->next = stamp;
338 } else {
339 /* Add first element */
340 stamps = stamp;
345 void
346 vfs_stamp (vfs *v, vfsid id)
348 struct vfs_stamping *stamp;
350 for (stamp = stamps; stamp != NULL; stamp = stamp->next)
351 if (stamp->v == v && stamp->id == id){
353 gettimeofday (&(stamp->time), NULL);
354 if (stamp->parent != NULL)
355 vfs_stamp (stamp->parent->v, stamp->parent->id);
357 return;
361 void
362 vfs_rm_parents (struct vfs_stamping *stamp)
364 struct vfs_stamping *parent;
366 while (stamp) {
367 parent = stamp->parent;
368 g_free (stamp);
369 stamp = parent;
373 void
374 vfs_rmstamp (vfs *v, vfsid id, int removeparents)
376 struct vfs_stamping *stamp, *st1;
378 for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
379 if (stamp->v == v && stamp->id == id){
380 if (stamp->parent != NULL){
381 if (removeparents)
382 vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
383 vfs_rm_parents (stamp->parent);
385 if (st1 == NULL){
386 stamps = stamp->next;
387 } else {
388 st1->next = stamp->next;
390 g_free (stamp);
392 return;
396 static int
397 ferrno (vfs *vfs)
399 return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
400 /* Hope that error message is obscure enough ;-) */
404 mc_open (const char *filename, int flags, ...)
406 int handle;
407 int mode;
408 void *info;
409 va_list ap;
411 char *file = vfs_canon (filename);
412 vfs *vfs = vfs_type (file);
414 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
415 va_start (ap, flags);
416 mode = va_arg (ap, int);
417 va_end (ap);
419 if (!vfs->open) {
420 errno = -EOPNOTSUPP;
421 return -1;
424 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
425 g_free (file);
426 if (!info){
427 errno = ferrno (vfs);
428 return -1;
430 handle = get_bucket ();
431 vfs_file_table [handle].fs_info = info;
432 vfs_file_table [handle].operations = vfs;
434 return handle;
437 #define vfs_op(handle) vfs_file_table [handle].operations
438 #define vfs_info(handle) vfs_file_table [handle].fs_info
439 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
441 #define MC_OP(name, inarg, callarg, pre, post) \
442 int mc_##name inarg \
444 vfs *vfs; \
445 int result; \
447 pre \
448 result = vfs->name ? (*vfs->name)callarg : -1; \
449 post \
450 if (result == -1) \
451 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
452 return result; \
455 #define MC_NAMEOP(name, inarg, callarg) \
456 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
457 #define MC_HANDLEOP(name, inarg, callarg) \
458 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
460 MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) )
463 mc_ctl (int handle, int ctlop, int arg)
465 vfs *vfs = vfs_op (handle);
467 return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
471 mc_setctl (char *path, int ctlop, char *arg)
473 vfs *vfs;
474 int result;
476 if (!path)
477 vfs_die("You don't want to pass NULL to mc_setctl.");
479 path = vfs_canon (path);
480 vfs = vfs_type (path);
481 result = vfs->setctl ? (*vfs->setctl)(vfs, path, ctlop, arg) : 0;
482 g_free (path);
483 return result;
487 mc_close (int handle)
489 vfs *vfs;
490 int result;
492 if (handle == -1 || !vfs_info (handle))
493 return -1;
495 vfs = vfs_op (handle);
496 if (handle < 3)
497 return close (handle);
499 if (!vfs->close)
500 vfs_die ("VFS must support close.\n");
501 result = (*vfs->close)(vfs_info (handle));
502 vfs_free_bucket (handle);
503 if (result == -1)
504 errno = ferrno (vfs);
506 return result;
509 DIR *
510 mc_opendir (char *dirname)
512 int handle, *handlep;
513 void *info;
514 vfs *vfs;
516 dirname = vfs_canon (dirname);
517 vfs = vfs_type (dirname);
519 info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
520 g_free (dirname);
521 if (!info){
522 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
523 return NULL;
525 handle = get_bucket ();
526 vfs_file_table [handle].fs_info = info;
527 vfs_file_table [handle].operations = vfs;
529 handlep = g_new (int, 1);
530 *handlep = handle;
531 return (DIR *) handlep;
534 /* This should strip the non needed part of a path name */
535 #define vfs_name(x) x
537 void
538 mc_seekdir (DIR *dirp, int offset)
540 int handle;
541 vfs *vfs;
543 if (!dirp){
544 errno = EFAULT;
545 return;
547 handle = *(int *) dirp;
548 vfs = vfs_op (handle);
549 if (vfs->seekdir)
550 (*vfs->seekdir) (vfs_info (handle), offset);
551 else
552 errno = E_NOTSUPP;
555 #define MC_DIROP(name, type, onerr ) \
556 type mc_##name (DIR *dirp) \
558 int handle; \
559 vfs *vfs; \
560 type result; \
562 if (!dirp){ \
563 errno = EFAULT; \
564 return onerr; \
566 handle = *(int *) dirp; \
567 vfs = vfs_op (handle); \
568 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
569 if (result == onerr) \
570 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
571 return result; \
574 MC_DIROP (readdir, struct dirent *, NULL)
575 MC_DIROP (telldir, int, -1)
578 mc_closedir (DIR *dirp)
580 int handle = *(int *) dirp;
581 vfs *vfs = vfs_op (handle);
582 int result;
584 result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
585 vfs_free_bucket (handle);
586 g_free (dirp);
587 return result;
590 int mc_stat (char *path, struct stat *buf) {
591 vfs *vfs;
592 int result;
594 path = vfs_canon (path); vfs = vfs_type (path);
595 result = vfs->stat ? (*vfs->stat) (vfs, vfs_name (path), buf) : -1;
596 g_free (path);
597 if (result == -1)
598 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
599 return result;
602 int mc_lstat (char *path, struct stat *buf) {
603 vfs *vfs;
604 int result;
606 path = vfs_canon (path); vfs = vfs_type (path);
607 result = vfs->lstat ? (*vfs->lstat) (vfs, vfs_name (path), buf) : -1;
608 g_free (path);
609 if (result == -1)
610 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
611 return result;
614 int mc_fstat (int handle, struct stat *buf) {
615 vfs *vfs;
616 int result;
618 if (handle == -1)
619 return -1;
620 vfs = vfs_op (handle);
621 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
622 if (result == -1)
623 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
624 return result;
628 * You must g_strdup whatever this function returns, static buffers are in use
631 static char *
632 mc_return_cwd (void)
634 char *p;
635 struct stat my_stat, my_stat2;
637 if (!vfs_rosplit (current_dir)){
638 p = g_get_current_dir ();
639 if (!p) /* One of the directories in the path is not readable */
640 return current_dir;
642 /* Otherwise check if it is O.K. to use the current_dir */
643 mc_stat (p, &my_stat);
644 mc_stat (current_dir, &my_stat2);
645 if (my_stat.st_ino != my_stat2.st_ino ||
646 my_stat.st_dev != my_stat2.st_dev ||
647 !cd_symlinks){
648 g_free (current_dir);
649 current_dir = p;
650 return p;
651 } /* Otherwise we return current_dir below */
653 return current_dir;
656 char *
657 mc_get_current_wd (char *buffer, int size)
659 char *cwd = mc_return_cwd();
661 strncpy (buffer, cwd, size);
662 return buffer;
665 MC_NAMEOP (chmod, (char *path, int mode), (vfs, vfs_name (path), mode))
666 MC_NAMEOP (chown, (char *path, int owner, int group), (vfs, vfs_name (path), owner, group))
667 MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs, vfs_name (path), times))
668 MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs, vfs_name (path), buf, bufsiz))
669 MC_NAMEOP (unlink, (char *path), (vfs, vfs_name (path)))
670 MC_NAMEOP (symlink, (char *name1, char *path), (vfs, vfs_name (name1), vfs_name (path)))
672 #define MC_RENAMEOP(name) \
673 int mc_##name (char *name1, char *name2) \
675 vfs *vfs; \
676 int result; \
678 name1 = vfs_canon (name1); \
679 vfs = vfs_type (name1); \
680 name2 = vfs_canon (name2); \
681 if (vfs != vfs_type (name2)){ \
682 errno = EXDEV; \
683 g_free (name1); \
684 g_free (name2); \
685 return -1; \
688 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
689 g_free (name1); \
690 g_free (name2); \
691 if (result == -1) \
692 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
693 return result; \
696 MC_RENAMEOP (link)
697 MC_RENAMEOP (rename)
699 MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte))
701 off_t mc_lseek (int fd, off_t offset, int whence)
703 vfs *vfs;
704 int result;
706 if (fd == -1)
707 return -1;
709 vfs = vfs_op (fd);
710 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
711 if (result == -1)
712 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
713 return result;
717 * remove //, /./ and /../, local should point to big enough buffer
720 #define ISSLASH(a) (!a || (a == '/'))
722 char *
723 vfs_canon (const char *path)
725 if (!path)
726 vfs_die("Cannot canonicalize NULL");
728 /* Tilde expansion */
729 if (*path == '~'){
730 char *local, *result;
732 local = tilde_expand (path);
733 if (local){
734 result = vfs_canon (local);
735 g_free (local);
736 return result;
740 /* Relative to current directory */
741 if (*path != PATH_SEP){
742 char *local, *result;
744 local = concat_dir_and_file (current_dir, path);
746 result = vfs_canon (local);
747 g_free (local);
748 return result;
752 * So we have path of following form:
753 * /p1/p2#op/.././././p3#op/p4. Good luck.
756 char *result = g_strdup (path);
758 mad_check("(pre-canonicalize)", 0);
759 canonicalize_pathname (result);
760 mad_check("(post-canonicalize)", 0);
762 return result;
766 vfsid
767 vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
769 vfsid nvfsid;
771 dir = concat_dir_and_file (dir, "");
773 nvfsid = (*nvfs->getid)(nvfs, dir, par);
775 g_free (dir);
776 return nvfsid;
779 static int
780 is_parent (vfs * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
782 struct vfs_stamping *stamp;
784 for (stamp = parent; stamp; stamp = stamp->parent)
785 if (stamp->v == nvfs && stamp->id == nvfsid)
786 break;
788 return (stamp ? 1 : 0);
791 void
792 vfs_add_noncurrent_stamps (vfs * oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
794 #ifndef VFS_STANDALONE
795 vfs *nvfs, *n2vfs, *n3vfs;
796 vfsid nvfsid, n2vfsid, n3vfsid;
797 struct vfs_stamping *par, *stamp;
798 int f;
800 /* FIXME: As soon as we convert to multiple panels, this stuff
801 has to change. It works like this: We do not time out the
802 vfs's which are current in any panel and on the other
803 side we add the old directory with all its parents which
804 are not in any panel (if we find such one, we stop adding
805 parents to the time-outing structure. */
807 /* There are three directories we have to take care of: current_dir,
808 cpanel->cwd and opanel->cwd. Athough most of the time either
809 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
810 same, it's possible that all three are different -- Norbert */
812 if (!cpanel)
813 return;
815 nvfs = vfs_type (current_dir);
816 nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
817 vfs_rmstamp (nvfs, nvfsid, 1);
819 f = is_parent (oldvfs, oldvfsid, par);
820 vfs_rm_parents (par);
821 if ((nvfs == oldvfs && nvfsid == oldvfsid) || oldvfsid == (vfsid *)-1 || f){
822 return;
825 if (get_current_type () == view_listing){
826 n2vfs = vfs_type (cpanel->cwd);
827 n2vfsid = vfs_ncs_getid (n2vfs, cpanel->cwd, &par);
828 f = is_parent (oldvfs, oldvfsid, par);
829 vfs_rm_parents (par);
830 if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
831 return;
832 } else {
833 n2vfs = (vfs *) -1;
834 n2vfsid = (vfs *) -1;
837 if (get_other_type () == view_listing){
838 n3vfs = vfs_type (opanel->cwd);
839 n3vfsid = vfs_ncs_getid (n3vfs, opanel->cwd, &par);
840 f = is_parent (oldvfs, oldvfsid, par);
841 vfs_rm_parents (par);
842 if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
843 return;
844 } else {
845 n3vfs = (vfs *)-1;
846 n3vfsid = (vfs *)-1;
849 if ((*oldvfs->nothingisopen) (oldvfsid)){
850 if (oldvfs == &vfs_extfs_ops && ((extfs_archive *) oldvfsid)->name == 0){
851 /* Free the resources immediatly when we leave a mtools fs
852 ('cd a:') instead of waiting for the vfs-timeout */
853 (oldvfs->free) (oldvfsid);
854 } else
855 vfs_addstamp (oldvfs, oldvfsid, parent);
856 for (stamp = parent; stamp != NULL; stamp = stamp->parent){
857 if ((stamp->v == nvfs && stamp->id == nvfsid) ||
858 (stamp->v == n2vfs && stamp->id == n2vfsid) ||
859 (stamp->v == n3vfs && stamp->id == n3vfsid) ||
860 stamp->id == (vfsid) - 1 ||
861 !(*stamp->v->nothingisopen) (stamp->id))
862 break;
863 if (stamp->v == &vfs_extfs_ops && ((extfs_archive *) stamp->id)->name == 0){
864 (stamp->v->free) (stamp->id);
865 vfs_rmstamp (stamp->v, stamp->id, 0);
866 } else
867 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
870 #else
871 vfs_addstamp (oldvfs, oldvfsid, parent);
872 #endif
875 static void
876 vfs_stamp_path (char *path)
878 vfs *vfs;
879 vfsid id;
880 struct vfs_stamping *par, *stamp;
882 vfs = vfs_type (path);
883 id = vfs_ncs_getid (vfs, path, &par);
884 vfs_addstamp (vfs, id, par);
886 for (stamp = par; stamp != NULL; stamp = stamp->parent)
887 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
888 vfs_rm_parents (par);
891 #ifndef VFS_STANDALONE
892 void
893 vfs_add_current_stamps (void)
895 vfs_stamp_path (current_dir);
897 if (cpanel) {
898 if (get_current_type () == view_listing)
899 vfs_stamp_path (cpanel->cwd);
902 if (opanel) {
903 if (get_other_type () == view_listing)
904 vfs_stamp_path (opanel->cwd);
907 #endif
909 /* This function is really broken */
911 mc_chdir (char *path)
913 char *a, *b;
914 int result;
915 char *p = NULL;
916 vfs *oldvfs;
917 vfsid oldvfsid;
918 struct vfs_stamping *parent;
920 a = current_dir; /* Save a copy for case of failure */
921 current_dir = vfs_canon (path);
922 current_vfs = vfs_type (current_dir);
923 b = g_strdup (current_dir);
924 result = (*current_vfs->chdir) ? (*current_vfs->chdir)(current_vfs, vfs_name (b)) : -1;
925 g_free (b);
926 if (result == -1){
927 errno = ferrno (current_vfs);
928 g_free (current_dir);
929 current_vfs = vfs_type (a);
930 current_dir = a;
931 } else {
932 oldvfs = vfs_type (a);
933 oldvfsid = vfs_ncs_getid (oldvfs, a, &parent);
934 g_free (a);
935 vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
936 vfs_rm_parents (parent);
939 if (*current_dir){
940 p = strchr (current_dir, 0) - 1;
941 if (*p == PATH_SEP && p > current_dir)
942 *p = 0; /* Sometimes we assume no trailing slash on cwd */
944 return result;
948 vfs_current_is_local (void)
950 return current_vfs == &vfs_local_ops;
953 #if 0
954 /* External world should not do differences between VFS-s */
956 vfs_current_is_extfs (void)
958 return current_vfs == &vfs_extfs_ops;
962 vfs_current_is_tarfs (void)
964 return current_vfs == &vfs_tarfs_ops;
968 vfs_current_is_cpiofs (void)
970 return current_vfs == &vfs_cpiofs_ops;
972 #endif
975 vfs_file_is_local (const char *file)
977 char *filename = vfs_canon (file);
978 vfs *vfs = vfs_type (filename);
980 g_free (filename);
981 return vfs == &vfs_local_ops;
985 vfs_file_is_ftp (char *filename)
987 #ifdef USE_NETCODE
988 vfs *vfs;
990 filename = vfs_canon (filename);
991 vfs = vfs_type (filename);
992 g_free (filename);
993 return vfs == &vfs_ftpfs_ops;
994 #else
995 return 0;
996 #endif
1000 vfs_file_is_smb (char *filename)
1002 #ifdef WITH_SMBFS
1003 #ifdef USE_NETCODE
1004 vfs *vfs;
1006 filename = vfs_canon (filename);
1007 vfs = vfs_type (filename);
1008 g_free (filename);
1009 return vfs == &vfs_smbfs_ops;
1010 #endif /* USE_NETCODE */
1011 #endif /* WITH_SMBFS */
1012 return 0;
1015 char *vfs_get_current_dir (void)
1017 return current_dir;
1020 static void vfs_setup_wd (void)
1022 current_dir = g_strdup (PATH_SEP_STR);
1023 if (!(vfs_flags & FL_NO_CWDSETUP))
1024 mc_return_cwd();
1026 if (strlen(current_dir)>MC_MAXPATHLEN-2)
1027 vfs_die ("Current dir too long.\n");
1030 MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs, vfs_name (path), mode))
1031 MC_NAMEOP (rmdir, (char *path), (vfs, vfs_name (path)))
1032 MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs, vfs_name (path), mode, dev))
1034 #ifdef HAVE_MMAP
1035 static struct mc_mmapping {
1036 caddr_t addr;
1037 void *vfs_info;
1038 vfs *vfs;
1039 struct mc_mmapping *next;
1040 } *mc_mmaparray = NULL;
1042 caddr_t
1043 mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
1045 vfs *vfs;
1046 caddr_t result;
1047 struct mc_mmapping *mcm;
1049 if (fd == -1)
1050 return (caddr_t) -1;
1052 vfs = vfs_op (fd);
1053 result = vfs->mmap ? (*vfs->mmap)(vfs, addr, len, prot, flags, vfs_info (fd), offset) : (caddr_t)-1;
1054 if (result == (caddr_t)-1){
1055 errno = ferrno (vfs);
1056 return (caddr_t)-1;
1058 mcm =g_new (struct mc_mmapping, 1);
1059 mcm->addr = result;
1060 mcm->vfs_info = vfs_info (fd);
1061 mcm->vfs = vfs;
1062 mcm->next = mc_mmaparray;
1063 mc_mmaparray = mcm;
1064 return result;
1068 mc_munmap (caddr_t addr, size_t len)
1070 struct mc_mmapping *mcm, *mcm2 = NULL;
1072 for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
1073 if (mcm->addr == addr){
1074 if (mcm2 == NULL)
1075 mc_mmaparray = mcm->next;
1076 else
1077 mcm2->next = mcm->next;
1078 if (mcm->vfs->munmap)
1079 (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
1080 g_free (mcm);
1081 return 0;
1084 return -1;
1087 #endif
1089 char *
1090 mc_def_getlocalcopy (vfs *vfs, char *filename)
1092 char *tmp;
1093 int fdin, fdout, i;
1094 char buffer[8192];
1095 struct stat mystat;
1097 fdin = mc_open (filename, O_RDONLY);
1098 if (fdin == -1)
1099 return NULL;
1101 fdout = mc_mkstemps (&tmp, "mclocalcopy", NULL);
1102 if (fdout == -1)
1103 goto fail;
1104 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0){
1105 if (write (fdout, buffer, i) != i)
1106 goto fail;
1108 if (i == -1)
1109 goto fail;
1110 i = mc_close (fdin);
1111 fdin = -1;
1112 if (i==-1)
1113 goto fail;
1114 if (close (fdout)==-1)
1115 goto fail;
1117 if (mc_stat (filename, &mystat) != -1){
1118 chmod (tmp, mystat.st_mode);
1120 return tmp;
1122 fail:
1123 if (fdout) close(fdout);
1124 if (fdin) mc_close (fdin);
1125 g_free (tmp);
1126 return NULL;
1129 char *
1130 mc_getlocalcopy (const char *pathname)
1132 char *result;
1133 char *path = vfs_canon (pathname);
1134 vfs *vfs = vfs_type (path);
1136 result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, vfs_name (path)) :
1137 mc_def_getlocalcopy (vfs, vfs_name (path));
1138 g_free (path);
1139 if (!result)
1140 errno = ferrno (vfs);
1141 return result;
1145 mc_def_ungetlocalcopy (vfs *vfs, char *filename, char *local, int has_changed)
1146 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1147 int fdin = -1, fdout = -1, i;
1148 if (has_changed){
1149 char buffer [8192];
1151 fdin = open (local, O_RDONLY);
1152 if (fdin == -1)
1153 goto failed;
1154 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1155 if (fdout == -1)
1156 goto failed;
1157 while ((i = read (fdin, buffer, sizeof (buffer))) > 0){
1158 if (mc_write (fdout, buffer, i) != i)
1159 goto failed;
1161 if (i == -1)
1162 goto failed;
1164 if (close (fdin)==-1) {
1165 fdin = -1;
1166 goto failed;
1168 fdin = -1;
1169 if (mc_close (fdout)==-1) {
1170 fdout = -1;
1171 goto failed;
1174 unlink (local);
1175 g_free (local);
1176 return 0;
1178 failed:
1179 message_1s (1, _("Changes to file lost"), filename);
1180 if (fdout!=-1) mc_close(fdout);
1181 if (fdin!=-1) close(fdin);
1182 unlink (local);
1183 g_free (local);
1184 return -1;
1188 mc_ungetlocalcopy (const char *pathname, char *local, int has_changed)
1190 int return_value = 0;
1191 char *path = vfs_canon (pathname);
1192 vfs *vfs = vfs_type (path);
1194 return_value = vfs->ungetlocalcopy ?
1195 (*vfs->ungetlocalcopy)(vfs, vfs_name (path), local, has_changed) :
1196 mc_def_ungetlocalcopy (vfs, vfs_name (path), local, has_changed);
1197 g_free (path);
1198 return return_value;
1202 * Hmm, as timeout is minute or so, do we need to care about usecs?
1204 static inline int
1205 timeoutcmp (struct timeval *t1, struct timeval *t2)
1207 return ((t1->tv_sec < t2->tv_sec)
1208 || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
1211 /* This is called from timeout handler with now = 0, or can be called
1212 with now = 1 to force freeing all filesystems that are not in use */
1214 void
1215 vfs_expire (int now)
1217 static int locked = 0;
1218 struct timeval time;
1219 struct vfs_stamping *stamp, *st;
1221 /* Avoid recursive invocation, e.g. when one of the free functions
1222 calls message_1s */
1223 if (locked)
1224 return;
1225 locked = 1;
1227 gettimeofday (&time, NULL);
1228 time.tv_sec -= vfs_timeout;
1230 for (stamp = stamps; stamp != NULL;){
1231 if (now || (timeoutcmp (&stamp->time, &time))){
1232 st = stamp->next;
1233 (*stamp->v->free) (stamp->id);
1234 vfs_rmstamp (stamp->v, stamp->id, 0);
1235 stamp = st;
1236 } else
1237 stamp = stamp->next;
1239 locked = 0;
1242 void
1243 vfs_timeout_handler (void)
1245 vfs_expire (0);
1248 void
1249 vfs_init (void)
1251 time_t current_time;
1252 struct tm *t;
1254 memset (vfs_file_table, 0, sizeof (vfs_file_table));
1255 current_time = time (NULL);
1256 t = localtime (&current_time);
1257 current_mday = t->tm_mday;
1258 current_mon = t->tm_mon;
1259 current_year = t->tm_year;
1261 /* We do not want to register vfs_local_ops */
1263 #ifdef USE_NETCODE
1264 tcp_init();
1265 vfs_register (&vfs_ftpfs_ops);
1266 #ifdef WITH_SMBFS
1267 vfs_register (&vfs_smbfs_ops);
1268 #endif
1269 vfs_register (&vfs_mcfs_ops);
1270 #endif
1272 vfs_register (&vfs_fish_ops);
1273 vfs_register (&vfs_extfs_ops);
1274 vfs_register (&vfs_sfs_ops);
1275 vfs_register (&vfs_tarfs_ops);
1276 vfs_register (&vfs_cpiofs_ops);
1278 #ifdef USE_EXT2FSLIB
1279 vfs_register (&vfs_undelfs_ops);
1280 #endif
1282 vfs_setup_wd ();
1285 void
1286 vfs_free_resources (char *path)
1288 vfs *vfs;
1289 vfsid vid;
1290 struct vfs_stamping *parent;
1292 vfs = vfs_type (path);
1293 vid = vfs_ncs_getid (vfs, path, &parent);
1294 if (vid != (vfsid) -1)
1295 (*vfs->free)(vid);
1296 vfs_rm_parents (parent);
1299 #if 0
1300 /* Shutdown a vfs given a path name */
1301 void
1302 vfs_shut_path (char *p)
1304 vfs *the_vfs;
1305 struct vfs_stamping *par;
1307 the_vfs = vfs_type (p);
1308 vfs_ncs_getid (the_vfs, p, &par);
1309 (*par->v->free)(par->id);
1310 vfs_rm_parents (par);
1312 #endif
1314 void
1315 vfs_shut (void)
1317 struct vfs_stamping *stamp, *st;
1318 vfs *vfs;
1320 for (stamp = stamps, stamps = 0; stamp != NULL;){
1321 (*stamp->v->free)(stamp->id);
1322 st = stamp->next;
1323 g_free (stamp);
1324 stamp = st;
1327 if (stamps)
1328 vfs_rmstamp (stamps->v, stamps->id, 1);
1330 if (current_dir)
1331 g_free (current_dir);
1333 for (vfs=vfs_list; vfs; vfs=vfs->next)
1334 if (vfs->done)
1335 (*vfs->done) (vfs);
1339 * These ones grab information from the VFS
1340 * and handles them to an upper layer
1342 void
1343 vfs_fill_names (void (*func)(char *))
1345 vfs *vfs;
1347 for (vfs=vfs_list; vfs; vfs=vfs->next)
1348 if (vfs->fill_names)
1349 (*vfs->fill_names) (vfs, func);
1352 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1353 #define MAXCOLS 30
1355 static char *columns [MAXCOLS]; /* Points to the string in column n */
1356 static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
1359 vfs_split_text (char *p)
1361 char *original = p;
1362 int numcols;
1364 memset (columns, 0, sizeof (columns));
1366 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
1367 while (*p == ' ' || *p == '\r' || *p == '\n'){
1368 *p = 0;
1369 p++;
1371 columns [numcols] = p;
1372 column_ptr [numcols] = p - original;
1373 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
1374 p++;
1376 return numcols;
1379 static int
1380 is_num (int idx)
1382 if (!columns [idx] || columns [idx][0] < '0' || columns [idx][0] > '9')
1383 return 0;
1384 return 1;
1387 static int
1388 is_dos_date(char *str)
1390 if (strlen(str) == 8 && str[2] == str[5] && strchr("\\-/", (int)str[2]) != NULL)
1391 return (1);
1393 return (0);
1396 static int
1397 is_week (char *str, struct tm *tim)
1399 char *week = "SunMonTueWedThuFriSat";
1400 char *pos;
1402 if((pos=strstr(week, str)) != NULL){
1403 if(tim != NULL)
1404 tim->tm_wday = (pos - week)/3;
1405 return (1);
1407 return (0);
1410 static int
1411 is_month (char *str, struct tm *tim)
1413 char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
1414 char *pos;
1416 if((pos=strstr(month, str)) != NULL){
1417 if(tim != NULL)
1418 tim->tm_mon = (pos - month)/3;
1419 return (1);
1421 return (0);
1424 static int
1425 is_time (char *str, struct tm *tim)
1427 char *p, *p2;
1429 if ((p=strchr(str, ':')) && (p2=strrchr(str, ':'))) {
1430 if (p != p2) {
1431 if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
1432 return (0);
1434 else {
1435 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
1436 return (0);
1439 else
1440 return (0);
1442 return (1);
1445 static int is_year(char *str, struct tm *tim)
1447 long year;
1449 if (strchr(str,':'))
1450 return (0);
1452 if (strlen(str)!=4)
1453 return (0);
1455 if (sscanf(str, "%ld", &year) != 1)
1456 return (0);
1458 if (year < 1900 || year > 3000)
1459 return (0);
1461 tim->tm_year = (int) (year - 1900);
1463 return (1);
1467 * FIXME: this is broken. Consider following entry:
1468 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1469 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1473 vfs_parse_filetype (char c)
1475 switch (c) {
1476 case 'd': return S_IFDIR;
1477 case 'b': return S_IFBLK;
1478 case 'c': return S_IFCHR;
1479 case 'l': return S_IFLNK;
1480 case 's': /* Socket */
1481 #ifdef S_IFSOCK
1482 return S_IFSOCK;
1483 #else
1484 /* If not supported, we fall through to IFIFO */
1485 return S_IFIFO;
1486 #endif
1487 case 'D': /* Solaris door */
1488 #ifdef S_IFDOOR
1489 return S_IFDOOR;
1490 #else
1491 return S_IFIFO;
1492 #endif
1493 case 'p': return S_IFIFO;
1494 case 'm': case 'n': /* Don't know what these are :-) */
1495 case '-': case '?': return S_IFREG;
1496 default: return -1;
1500 int vfs_parse_filemode (const char *p)
1501 { /* converts rw-rw-rw- into 0666 */
1502 int res = 0;
1503 switch (*(p++)){
1504 case 'r': res |= 0400; break;
1505 case '-': break;
1506 default: return -1;
1508 switch (*(p++)){
1509 case 'w': res |= 0200; break;
1510 case '-': break;
1511 default: return -1;
1513 switch (*(p++)){
1514 case 'x': res |= 0100; break;
1515 case 's': res |= 0100 | S_ISUID; break;
1516 case 'S': res |= S_ISUID; break;
1517 case '-': break;
1518 default: return -1;
1520 switch (*(p++)){
1521 case 'r': res |= 0040; break;
1522 case '-': break;
1523 default: return -1;
1525 switch (*(p++)){
1526 case 'w': res |= 0020; break;
1527 case '-': break;
1528 default: return -1;
1530 switch (*(p++)){
1531 case 'x': res |= 0010; break;
1532 case 's': res |= 0010 | S_ISGID; break;
1533 case 'l': /* Solaris produces these */
1534 case 'S': res |= S_ISGID; break;
1535 case '-': break;
1536 default: return -1;
1538 switch (*(p++)){
1539 case 'r': res |= 0004; break;
1540 case '-': break;
1541 default: return -1;
1543 switch (*(p++)){
1544 case 'w': res |= 0002; break;
1545 case '-': break;
1546 default: return -1;
1548 switch (*(p++)){
1549 case 'x': res |= 0001; break;
1550 case 't': res |= 0001 | S_ISVTX; break;
1551 case 'T': res |= S_ISVTX; break;
1552 case '-': break;
1553 default: return -1;
1555 return res;
1558 int vfs_parse_filedate(int idx, time_t *t)
1559 { /* This thing parses from idx in columns[] array */
1561 char *p;
1562 struct tm tim;
1563 int d[3];
1564 int got_year = 0;
1566 /* Let's setup default time values */
1567 tim.tm_year = current_year;
1568 tim.tm_mon = current_mon;
1569 tim.tm_mday = current_mday;
1570 tim.tm_hour = 0;
1571 tim.tm_min = 0;
1572 tim.tm_sec = 0;
1573 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
1575 p = columns [idx++];
1577 /* We eat weekday name in case of extfs */
1578 if(is_week(p, &tim))
1579 p = columns [idx++];
1581 /* Month name */
1582 if(is_month(p, &tim)){
1583 /* And we expect, it followed by day number */
1584 if (is_num (idx))
1585 tim.tm_mday = (int)atol (columns [idx++]);
1586 else
1587 return 0; /* No day */
1589 } else {
1590 /* We usually expect:
1591 Mon DD hh:mm
1592 Mon DD YYYY
1593 But in case of extfs we allow these date formats:
1594 Mon DD YYYY hh:mm
1595 Mon DD hh:mm YYYY
1596 Wek Mon DD hh:mm:ss YYYY
1597 MM-DD-YY hh:mm
1598 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1599 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1601 /* Here just this special case with MM-DD-YY */
1602 if (is_dos_date(p)){
1603 p[2] = p[5] = '-';
1605 if(sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
1606 /* We expect to get:
1607 1. MM-DD-YY
1608 2. DD-MM-YY
1609 3. YY-MM-DD
1610 4. YY-DD-MM */
1612 /* Hmm... maybe, next time :)*/
1614 /* At last, MM-DD-YY */
1615 d[0]--; /* Months are zerobased */
1616 /* Y2K madness */
1617 if(d[2] < 70)
1618 d[2] += 100;
1620 tim.tm_mon = d[0];
1621 tim.tm_mday = d[1];
1622 tim.tm_year = d[2];
1623 got_year = 1;
1624 } else
1625 return 0; /* sscanf failed */
1626 } else
1627 return 0; /* unsupported format */
1630 /* Here we expect to find time and/or year */
1632 if (is_num (idx)) {
1633 if(is_time(columns[idx], &tim) || (got_year = is_year(columns[idx], &tim))) {
1634 idx++;
1636 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1637 if(is_num (idx) && (columns[idx+1][0]) &&
1638 ((got_year |= is_year(columns[idx], &tim)) || is_time(columns[idx], &tim)))
1639 idx++; /* time & year or reverse */
1640 } /* only time or date */
1642 else
1643 return 0; /* Nor time or date */
1646 * If the date is less than 6 months in the past, it is shown without year
1647 * other dates in the past or future are shown with year but without time
1648 * This does not check for years before 1900 ... I don't know, how
1649 * to represent them at all
1651 if (!got_year &&
1652 current_mon < 6 && current_mon < tim.tm_mon &&
1653 tim.tm_mon - current_mon >= 6)
1655 tim.tm_year--;
1657 if ((*t = mktime(&tim)) < 0)
1658 *t = 0;
1659 return idx;
1663 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
1665 int idx, idx2, num_cols;
1666 int i;
1667 char *p_copy = NULL;
1668 char *t = NULL;
1669 const char *line = p;
1671 if (strncmp (p, "total", 5) == 0)
1672 return 0;
1674 if ((i = vfs_parse_filetype(*(p++))) == -1)
1675 goto error;
1677 s->st_mode = i;
1678 if (*p == ' ') /* Notwell 4 */
1679 p++;
1680 if (*p == '['){
1681 if (strlen (p) <= 8 || p [8] != ']')
1682 goto error;
1683 /* Should parse here the Notwell permissions :) */
1684 if (S_ISDIR (s->st_mode))
1685 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
1686 else
1687 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
1688 p += 9;
1689 } else {
1690 if ((i = vfs_parse_filemode(p)) == -1)
1691 goto error;
1692 s->st_mode |= i;
1693 p += 9;
1695 /* This is for an extra ACL attribute (HP-UX) */
1696 if (*p == '+')
1697 p++;
1700 p_copy = g_strdup(p);
1701 num_cols = vfs_split_text (p_copy);
1703 s->st_nlink = atol (columns [0]);
1704 if (s->st_nlink <= 0)
1705 goto error;
1707 if (!is_num (1))
1708 s->st_uid = finduid (columns [1]);
1709 else
1710 s->st_uid = (uid_t) atol (columns [1]);
1712 /* Mhm, the ls -lg did not produce a group field */
1713 for (idx = 3; idx <= 5; idx++)
1714 if (is_month(columns [idx], NULL) || is_week(columns [idx], NULL) || is_dos_date(columns[idx]))
1715 break;
1717 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
1718 goto error;
1720 /* We don't have gid */
1721 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
1722 idx2 = 2;
1723 else {
1724 /* We have gid field */
1725 if (is_num (2))
1726 s->st_gid = (gid_t) atol (columns [2]);
1727 else
1728 s->st_gid = findgid (columns [2]);
1729 idx2 = 3;
1732 /* This is device */
1733 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
1734 int maj, min;
1736 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
1737 goto error;
1739 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
1740 goto error;
1742 #ifdef HAVE_ST_RDEV
1743 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
1744 #endif
1745 s->st_size = 0;
1747 } else {
1748 /* Common file size */
1749 if (!is_num (idx2))
1750 goto error;
1752 s->st_size = (size_t) atol (columns [idx2]);
1753 #ifdef HAVE_ST_RDEV
1754 s->st_rdev = 0;
1755 #endif
1758 idx = vfs_parse_filedate(idx, &s->st_mtime);
1759 if (!idx)
1760 goto error;
1761 /* Use resulting time value */
1762 s->st_atime = s->st_ctime = s->st_mtime;
1763 #if 0
1764 /* These variables must be initialized by vfs_s_new_inode () */
1765 s->st_dev = 0;
1766 s->st_ino = 0;
1767 #endif
1768 #ifdef HAVE_ST_BLKSIZE
1769 s->st_blksize = 512;
1770 #endif
1771 #ifdef HAVE_ST_BLOCKS
1772 s->st_blocks = (s->st_size + 511) / 512;
1773 #endif
1775 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
1776 if (strcmp (columns [i], "->") == 0){
1777 idx2 = i;
1778 break;
1781 if (((S_ISLNK (s->st_mode) ||
1782 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
1783 && idx2){
1785 if (filename){
1786 *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
1788 if (linkname){
1789 t = g_strdup (p + column_ptr [idx2+1]);
1790 *linkname = t;
1792 } else {
1793 /* Extract the filename from the string copy, not from the columns
1794 * this way we have a chance of entering hidden directories like ". ."
1796 if (filename){
1798 *filename = g_strdup (columns [idx++]);
1801 t = g_strdup (p + column_ptr [idx]);
1802 *filename = t;
1804 if (linkname)
1805 *linkname = NULL;
1808 if (t) {
1809 int p = strlen (t);
1810 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1811 t [p] = 0;
1812 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1813 t [p] = 0;
1816 g_free (p_copy);
1817 return 1;
1819 error:
1821 static int errorcount = 0;
1823 if (++errorcount < 5) {
1824 message_1s (1, _("Could not parse:"), p_copy ? p_copy : line);
1825 } else if (errorcount == 5)
1826 message_1s (1, _("More parsing errors will be ignored."), _("(sorry)"));
1829 if (p_copy)
1830 g_free (p_copy);
1831 return 0;
1834 void
1835 vfs_die (char *m)
1837 message_1s (1, _("Internal error:"), m);
1838 exit (1);
1841 void
1842 vfs_print_stats (char *fs_name, char *action, char *file_name, int have, int need)
1844 static char *i18n_percent_transf_format = NULL, *i18n_transf_format = NULL;
1846 if (i18n_percent_transf_format == NULL) {
1847 i18n_percent_transf_format = _("%s: %s: %s %3d%% (%ld bytes transfered)");
1848 i18n_transf_format = _("%s: %s: %s %ld bytes transfered");
1851 if (need)
1852 print_vfs_message (i18n_percent_transf_format,
1853 fs_name, action, file_name, have*100/need, have);
1854 else
1855 print_vfs_message (i18n_transf_format,
1856 fs_name, action, file_name, have);
1859 #ifndef VFS_STANDALONE
1860 char *
1861 vfs_get_password (char *msg)
1863 return (char *) input_dialog (msg, _("Password:"), "");
1865 #endif
1868 * Returns vfs path corresponding to given url. If passed string is
1869 * not recognized as url, g_strdup(url) is returned.
1871 char *
1872 vfs_translate_url (char *url)
1874 if (strncmp (url, "ftp://", 6) == 0)
1875 return g_strconcat ("/#ftp:", url + 6, NULL);
1876 else if (strncmp (url, "a:", 2) == 0)
1877 return g_strdup ("/#a");
1878 else
1879 return g_strdup (url);