Another little trim on key setup + author email updated...
[midnight-commander.git] / vfs / vfs.c
bloba5ab5645c3ff639143b2ab08a6bf9a531e5e2b24
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 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;
83 uid_t vfs_uid = 0;
84 gid_t vfs_gid = 0;
86 /* FIXME: Open files managed by the vfs layer, should be dynamical */
87 #define MAX_VFS_FILES 100
89 static struct {
90 void *fs_info;
91 vfs *operations;
92 } vfs_file_table [MAX_VFS_FILES];
94 static int
95 get_bucket (void)
97 int i;
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)
102 return i;
105 vfs_die ("No more virtual file handles");
106 return 0;
109 /* vfs_local_ops needs to be the first one */
110 static vfs *vfs_list = &vfs_local_ops;
113 vfs_register (vfs *vfs)
115 if (!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 */
120 return 0;
122 vfs->next = vfs_list;
123 vfs_list = vfs;
125 return 1;
128 vfs *
129 vfs_type_from_op (char *path)
131 vfs *vfs;
133 if (!path)
134 vfs_die ("vfs_type_from_op got NULL: impossible");
136 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
137 if (vfs->which) {
138 if ((*vfs->which) (vfs, path) == -1)
139 continue;
140 return vfs;
142 if (!strncmp (path, vfs->prefix, strlen (vfs->prefix)))
143 return vfs;
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. */
151 char *
152 vfs_strip_suffix_from_filename (const char *filename)
154 vfs *vfs;
155 char *semi;
156 char *p;
158 if (!filename)
159 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
161 p = g_strdup (filename);
162 if (!(semi = strrchr (p, '#')))
163 return p;
165 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
166 if (vfs->which){
167 if ((*vfs->which) (vfs, semi + 1) == -1)
168 continue;
169 *semi = '\0'; /* Found valid suffix */
170 return p;
172 if (!strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
173 *semi = '\0'; /* Found valid suffix */
174 return p;
177 return p;
180 static int
181 path_magic (const char *path)
183 struct stat buf;
185 if (vfs_flags & FL_ALWAYS_MAGIC)
186 return 1;
188 if (!stat(path, &buf))
189 return 0;
191 return 1;
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
199 vfs *
200 vfs_split (char *path, char **inpath, char **op)
202 char *semi;
203 char *slash;
204 vfs *ret;
206 if (!path)
207 vfs_die("Can not split NULL");
209 semi = strrchr (path, '#');
210 if (!semi || !path_magic(path))
211 return NULL;
213 slash = strchr (semi, PATH_SEP);
214 *semi = 0;
216 if (op)
217 *op = NULL;
219 if (inpath)
220 *inpath = NULL;
222 if (slash)
223 *slash = 0;
225 if ((ret = vfs_type_from_op (semi+1))){
226 if (op)
227 *op = semi + 1;
228 if (inpath)
229 *inpath = slash ? slash + 1 : NULL;
230 return ret;
234 if (slash)
235 *slash = PATH_SEP;
236 ret = vfs_split (path, inpath, op);
237 *semi = '#';
238 return ret;
241 vfs *
242 vfs_rosplit (char *path)
244 char *semi;
245 char *slash;
246 vfs *ret;
248 g_return_val_if_fail(path, NULL);
250 semi = strrchr (path, '#');
251 if (!semi || !path_magic (path))
252 return NULL;
254 slash = strchr (semi, PATH_SEP);
255 *semi = 0;
256 if (slash)
257 *slash = 0;
259 ret = vfs_type_from_op (semi+1);
260 if (!ret && (vfs_flags & FL_NO_LOCALHASH))
261 return &vfs_nil_ops;
263 if (slash)
264 *slash = PATH_SEP;
265 if (!ret)
266 ret = vfs_rosplit (path);
268 *semi = '#';
269 return ret;
272 vfs *
273 vfs_type (char *path)
275 vfs *vfs;
277 vfs = vfs_rosplit(path);
279 if (!vfs)
280 vfs = &vfs_local_ops;
282 return vfs;
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.
293 vfs_timeouts ()
295 return stamps ? 10 : 0;
298 void
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);
307 return;
309 stamp = g_new (struct vfs_stamping, 1);
310 stamp->v = v;
311 stamp->id = id;
312 if (parent){
313 struct vfs_stamping *st = stamp;
314 while (parent){
315 st->parent = g_new (struct vfs_stamping, 1);
316 *st->parent = *parent;
317 parent = parent->parent;
318 st = st->parent;
320 st->parent = 0;
322 else
323 stamp->parent = 0;
325 gettimeofday (&(stamp->time), NULL);
326 stamp->next = 0;
328 if (stamps)
329 st1->next = stamp;
330 else
331 stamps = stamp;
335 void
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);
347 return;
351 void
352 vfs_rm_parents (struct vfs_stamping *stamp)
354 struct vfs_stamping *parent;
356 while (stamp) {
357 parent = stamp->parent;
358 g_free (stamp);
359 stamp = parent;
363 void
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){
371 if (removeparents)
372 vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
373 vfs_rm_parents (stamp->parent);
375 if (st1 == NULL){
376 stamps = stamp->next;
377 } else {
378 st1->next = stamp->next;
380 g_free (stamp);
382 return;
386 static int
387 ferrno (vfs *vfs)
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, ...)
396 int handle;
397 int mode;
398 void *info;
399 va_list ap;
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);
407 va_end (ap);
409 if (!vfs->open) {
410 errno = -EOPNOTSUPP;
411 return -1;
414 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
415 g_free (file);
416 if (!info){
417 errno = ferrno (vfs);
418 return -1;
420 handle = get_bucket ();
421 vfs_file_table [handle].fs_info = info;
422 vfs_file_table [handle].operations = vfs;
424 return handle;
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 \
434 vfs *vfs; \
435 int result; \
437 pre \
438 result = vfs->name ? (*vfs->name)callarg : -1; \
439 post \
440 if (result == -1) \
441 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
442 return result; \
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)
463 vfs *vfs;
464 int result;
466 if (!path)
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;
472 g_free (path);
473 return result;
477 mc_close (int handle)
479 vfs *vfs;
480 int result;
482 if (handle == -1 || !vfs_info (handle))
483 return -1;
485 vfs = vfs_op (handle);
486 if (handle < 3)
487 return close (handle);
489 if (!vfs->close)
490 vfs_die ("VFS must support close.\n");
491 result = (*vfs->close)(vfs_info (handle));
492 vfs_free_bucket (handle);
493 if (result == -1)
494 errno = ferrno (vfs);
496 return result;
499 DIR *
500 mc_opendir (char *dirname)
502 int handle, *handlep;
503 void *info;
504 vfs *vfs;
506 dirname = vfs_canon (dirname);
507 vfs = vfs_type (dirname);
509 info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
510 g_free (dirname);
511 if (!info){
512 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
513 return NULL;
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);
520 *handlep = handle;
521 return (DIR *) handlep;
524 /* This should strip the non needed part of a path name */
525 #define vfs_name(x) x
527 void
528 mc_seekdir (DIR *dirp, int offset)
530 int handle;
531 vfs *vfs;
533 if (!dirp){
534 errno = EFAULT;
535 return;
537 handle = *(int *) dirp;
538 vfs = vfs_op (handle);
539 if (vfs->seekdir)
540 (*vfs->seekdir) (vfs_info (handle), offset);
541 else
542 errno = E_NOTSUPP;
545 #define MC_DIROP(name, type, onerr ) \
546 type mc_##name (DIR *dirp) \
548 int handle; \
549 vfs *vfs; \
550 type result; \
552 if (!dirp){ \
553 errno = EFAULT; \
554 return onerr; \
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; \
561 return result; \
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);
572 int result;
574 result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
575 vfs_free_bucket (handle);
576 g_free (dirp);
577 return result;
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
588 char *
589 mc_return_cwd (void)
591 char *p;
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 */
599 return current_dir;
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 ||
606 !cd_symlinks){
607 g_free (current_dir);
608 current_dir = g_strdup (p);
609 return p;
610 } /* Otherwise we return current_dir below */
612 return current_dir;
615 char *
616 mc_get_current_wd (char *buffer, int size)
618 char *cwd = mc_return_cwd();
620 strncpy (buffer, cwd, size);
621 return buffer;
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) \
634 vfs *vfs; \
635 int result; \
637 name1 = vfs_canon (name1); \
638 vfs = vfs_type (name1); \
639 name2 = vfs_canon (name2); \
640 if (vfs != vfs_type (name2)){ \
641 errno = EXDEV; \
642 g_free (name1); \
643 g_free (name2); \
644 return -1; \
647 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
648 g_free (name1); \
649 g_free (name2); \
650 if (result == -1) \
651 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
652 return result; \
655 MC_RENAMEOP (link)
656 MC_RENAMEOP (rename)
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)
662 vfs *vfs;
663 int result;
665 if (fd == -1)
666 return -1;
668 vfs = vfs_op (fd);
669 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
670 if (result == -1)
671 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
672 return result;
676 * remove //, /./ and /../, local should point to big enough buffer
679 #define ISSLASH(a) (!a || (a == '/'))
681 char *
682 vfs_canon (const char *path)
684 if (!path)
685 vfs_die("Can not canonize NULL");
687 /* Tilde expansion */
688 if (*path == '~'){
689 char *local, *result;
691 local = tilde_expand (path);
692 if (local){
693 result = vfs_canon (local);
694 g_free (local);
695 return result;
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);
706 g_free (local);
707 return result;
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);
721 return result;
725 vfsid
726 vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
728 vfsid nvfsid;
730 dir = concat_dir_and_file (dir, "");
732 nvfsid = (*nvfs->getid)(nvfs, dir, par);
734 g_free (dir);
735 return nvfsid;
738 static int
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)
745 break;
747 return (stamp ? 1 : 0);
750 void
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;
757 int f;
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 */
771 if (!cpanel)
772 return;
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){
781 return;
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)
790 return;
791 } else {
792 n2vfs = (vfs *) -1;
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)
802 return;
803 } else {
804 n3vfs = (vfs *)-1;
805 n3vfsid = (vfs *)-1;
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);
813 } else
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))
821 break;
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);
825 } else
826 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
829 #else
830 vfs_addstamp (oldvfs, oldvfsid, parent);
831 #endif
834 static void
835 vfs_stamp_path (char *path)
837 vfs *vfs;
838 vfsid id;
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
851 void
852 vfs_add_current_stamps (void)
854 vfs_stamp_path (current_dir);
856 if (cpanel) {
857 if (get_current_type () == view_listing)
858 vfs_stamp_path (cpanel->cwd);
861 if (opanel) {
862 if (get_other_type () == view_listing)
863 vfs_stamp_path (opanel->cwd);
866 #endif
868 /* This function is really broken */
870 mc_chdir (char *path)
872 char *a, *b;
873 int result;
874 char *p = NULL;
875 vfs *oldvfs;
876 vfsid oldvfsid;
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;
884 g_free (b);
885 if (result == -1){
886 errno = ferrno (current_vfs);
887 g_free (current_dir);
888 current_vfs = vfs_type (a);
889 current_dir = a;
890 } else {
891 oldvfs = vfs_type (a);
892 oldvfsid = vfs_ncs_getid (oldvfs, a, &parent);
893 g_free (a);
894 vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
895 vfs_rm_parents (parent);
898 if (*current_dir){
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 */
903 return result;
907 vfs_current_is_local (void)
909 return current_vfs == &vfs_local_ops;
912 #if 0
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;
931 #endif
934 vfs_file_is_local (const char *file)
936 char *filename = vfs_canon (file);
937 vfs *vfs = vfs_type (filename);
939 g_free (filename);
940 return vfs == &vfs_local_ops;
944 vfs_file_is_ftp (char *filename)
946 #ifdef USE_NETCODE
947 vfs *vfs;
949 filename = vfs_canon (filename);
950 vfs = vfs_type (filename);
951 g_free (filename);
952 return vfs == &vfs_ftpfs_ops;
953 #else
954 return 0;
955 #endif
959 vfs_file_is_smb (char *filename)
961 #ifdef WITH_SMBFS
962 #ifdef USE_NETCODE
963 vfs *vfs;
965 filename = vfs_canon (filename);
966 vfs = vfs_type (filename);
967 g_free (filename);
968 return vfs == &vfs_smbfs_ops;
969 #endif /* USE_NETCODE */
970 #endif /* WITH_SMBFS */
971 return 0;
974 char *vfs_get_current_dir (void)
976 return current_dir;
979 static void vfs_setup_wd (void)
981 current_dir = g_strdup (PATH_SEP_STR);
982 if (!(vfs_flags & FL_NO_CWDSETUP))
983 mc_return_cwd();
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))
993 #ifdef HAVE_MMAP
994 static struct mc_mmapping {
995 caddr_t addr;
996 void *vfs_info;
997 vfs *vfs;
998 struct mc_mmapping *next;
999 } *mc_mmaparray = NULL;
1001 caddr_t
1002 mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
1004 vfs *vfs;
1005 caddr_t result;
1006 struct mc_mmapping *mcm;
1008 if (fd == -1)
1009 return (caddr_t) -1;
1011 vfs = vfs_op (fd);
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);
1015 return (caddr_t)-1;
1017 mcm =g_new (struct mc_mmapping, 1);
1018 mcm->addr = result;
1019 mcm->vfs_info = vfs_info (fd);
1020 mcm->vfs = vfs;
1021 mcm->next = mc_mmaparray;
1022 mc_mmaparray = mcm;
1023 return result;
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){
1033 if (mcm2 == NULL)
1034 mc_mmaparray = mcm->next;
1035 else
1036 mcm2->next = mcm->next;
1037 if (mcm->vfs->munmap)
1038 (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
1039 g_free (mcm);
1040 return 0;
1043 return -1;
1046 #endif
1048 char *
1049 mc_def_getlocalcopy (vfs *vfs, char *filename)
1051 char *tmp;
1052 int fdin, fdout, i;
1053 char buffer[8192];
1054 struct stat mystat;
1056 fdin = mc_open (filename, O_RDONLY);
1057 if (fdin == -1)
1058 return NULL;
1059 tmp = g_tempnam (NULL, "mclocalcopy");
1060 fdout = open (tmp, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0600);
1061 if (fdout == -1)
1062 goto fail;
1063 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0){
1064 if (write (fdout, buffer, i) != i)
1065 goto fail;
1067 if (i == -1)
1068 goto fail;
1069 i = mc_close (fdin);
1070 fdin = -1;
1071 if (i==-1)
1072 goto fail;
1073 if (close (fdout)==-1)
1074 goto fail;
1076 if (mc_stat (filename, &mystat) != -1){
1077 chmod (tmp, mystat.st_mode);
1079 return tmp;
1081 fail:
1082 if (fdout) close(fdout);
1083 if (fdin) mc_close (fdin);
1084 g_free (tmp);
1085 return NULL;
1088 char *
1089 mc_getlocalcopy (const char *pathname)
1091 char *result;
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));
1097 g_free (path);
1098 if (!result)
1099 errno = ferrno (vfs);
1100 return result;
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;
1107 if (has_changed){
1108 char buffer [8192];
1110 fdin = open (local, O_RDONLY);
1111 if (fdin == -1)
1112 goto failed;
1113 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1114 if (fdout == -1)
1115 goto failed;
1116 while ((i = read (fdin, buffer, sizeof (buffer))) > 0){
1117 if (mc_write (fdout, buffer, i) != i)
1118 goto failed;
1120 if (i == -1)
1121 goto failed;
1123 if (close (fdin)==-1) {
1124 fdin = -1;
1125 goto failed;
1127 fdin = -1;
1128 if (mc_close (fdout)==-1) {
1129 fdout = -1;
1130 goto failed;
1133 unlink (local);
1134 g_free (local);
1135 return 0;
1137 failed:
1138 message_1s (1, _("Changes to file lost"), filename);
1139 if (fdout!=-1) mc_close(fdout);
1140 if (fdin!=-1) close(fdin);
1141 unlink (local);
1142 g_free (local);
1143 return -1;
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);
1156 g_free (path);
1157 return return_value;
1161 * Hmm, as timeout is minute or so, do we need to care about usecs?
1163 inline int
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 */
1173 void
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
1181 calls message_1s */
1182 if (locked)
1183 return;
1184 locked = 1;
1186 gettimeofday (&time, NULL);
1187 time.tv_sec -= vfs_timeout;
1189 for (stamp = stamps; stamp != NULL;){
1190 if (now || (timeoutcmp (&stamp->time, &time))){
1191 st = stamp->next;
1192 (*stamp->v->free) (stamp->id);
1193 vfs_rmstamp (stamp->v, stamp->id, 0);
1194 stamp = st;
1195 } else
1196 stamp = stamp->next;
1198 locked = 0;
1201 void
1202 vfs_timeout_handler (void)
1204 vfs_expire (0);
1207 void
1208 vfs_init (void)
1210 time_t current_time;
1211 struct tm *t;
1213 memset (vfs_file_table, 0, sizeof (vfs_file_table));
1214 current_time = time (NULL);
1215 t = localtime (&current_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 */
1222 #ifdef USE_NETCODE
1223 tcp_init();
1224 vfs_register (&vfs_ftpfs_ops);
1225 #ifdef WITH_SMBFS
1226 vfs_register (&vfs_smbfs_ops);
1227 #endif
1228 vfs_register (&vfs_mcfs_ops);
1229 #endif
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);
1239 #endif
1241 vfs_setup_wd ();
1244 void
1245 vfs_free_resources (char *path)
1247 vfs *vfs;
1248 vfsid vid;
1249 struct vfs_stamping *parent;
1251 vfs = vfs_type (path);
1252 vid = vfs_ncs_getid (vfs, path, &parent);
1253 if (vid != (vfsid) -1)
1254 (*vfs->free)(vid);
1255 vfs_rm_parents (parent);
1258 #if 0
1259 /* Shutdown a vfs given a path name */
1260 void
1261 vfs_shut_path (char *p)
1263 vfs *the_vfs;
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);
1271 #endif
1273 void
1274 vfs_shut (void)
1276 struct vfs_stamping *stamp, *st;
1277 vfs *vfs;
1279 for (stamp = stamps, stamps = 0; stamp != NULL;){
1280 (*stamp->v->free)(stamp->id);
1281 st = stamp->next;
1282 g_free (stamp);
1283 stamp = st;
1286 if (stamps)
1287 vfs_rmstamp (stamps->v, stamps->id, 1);
1289 if (current_dir)
1290 g_free (current_dir);
1292 for (vfs=vfs_list; vfs; vfs=vfs->next)
1293 if (vfs->done)
1294 (*vfs->done) (vfs);
1298 * These ones grab information from the VFS
1299 * and handles them to an upper layer
1301 void
1302 vfs_fill_names (void (*func)(char *))
1304 vfs *vfs;
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 */
1312 #define MAXCOLS 30
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)
1320 char *original = p;
1321 int numcols;
1323 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
1324 while (*p == ' ' || *p == '\r' || *p == '\n'){
1325 *p = 0;
1326 p++;
1328 columns [numcols] = p;
1329 column_ptr [numcols] = p - original;
1330 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
1331 p++;
1333 return numcols;
1336 static int
1337 is_num (int idx)
1339 if (!columns [idx] || columns [idx][0] < '0' || columns [idx][0] > '9')
1340 return 0;
1341 return 1;
1344 static int
1345 is_dos_date(char *str)
1347 if (strlen(str) == 8 && str[2] == str[5] && strchr("\\-/", (int)str[2]) != NULL)
1348 return (1);
1350 return (0);
1353 static int
1354 is_week (char *str, struct tm *tim)
1356 static char *week = "SunMonTueWedThuFriSat";
1357 char *pos;
1359 if((pos=strstr(week, str)) != NULL){
1360 if(tim != NULL)
1361 tim->tm_wday = (pos - week)/3;
1362 return (1);
1364 return (0);
1367 static int
1368 is_month (char *str, struct tm *tim)
1370 static char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
1371 char *pos;
1373 if((pos=strstr(month, str)) != NULL){
1374 if(tim != NULL)
1375 tim->tm_mon = (pos - month)/3;
1376 return (1);
1378 return (0);
1381 static int
1382 is_time (char *str, struct tm *tim)
1384 char *p, *p2;
1386 if ((p=strchr(str, ':')) && (p2=strrchr(str, ':'))) {
1387 if (p != p2) {
1388 if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
1389 return (0);
1391 else {
1392 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
1393 return (0);
1396 else
1397 return (0);
1399 return (1);
1402 static int is_year(char *str, struct tm *tim)
1404 long year;
1406 if (strchr(str,':'))
1407 return (0);
1409 if (strlen(str)!=4)
1410 return (0);
1412 if (sscanf(str, "%ld", &year) != 1)
1413 return (0);
1415 if (year < 1900 || year > 3000)
1416 return (0);
1418 tim->tm_year = (int) (year - 1900);
1420 return (1);
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)
1432 switch (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;
1437 case 's':
1438 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty close */
1439 return S_IFSOCK;
1440 #endif
1441 case 'p': return S_IFIFO;
1442 case 'm': case 'n': /* Don't know what these are :-) */
1443 case '-': case '?': return S_IFREG;
1444 default: return -1;
1448 int vfs_parse_filemode (const char *p)
1449 { /* converts rw-rw-rw- into 0666 */
1450 int res = 0;
1451 switch (*(p++)){
1452 case 'r': res |= 0400; break;
1453 case '-': break;
1454 default: return -1;
1456 switch (*(p++)){
1457 case 'w': res |= 0200; break;
1458 case '-': break;
1459 default: return -1;
1461 switch (*(p++)){
1462 case 'x': res |= 0100; break;
1463 case 's': res |= 0100 | S_ISUID; break;
1464 case 'S': res |= S_ISUID; break;
1465 case '-': break;
1466 default: return -1;
1468 switch (*(p++)){
1469 case 'r': res |= 0040; break;
1470 case '-': break;
1471 default: return -1;
1473 switch (*(p++)){
1474 case 'w': res |= 0020; break;
1475 case '-': break;
1476 default: return -1;
1478 switch (*(p++)){
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;
1483 case '-': break;
1484 default: return -1;
1486 switch (*(p++)){
1487 case 'r': res |= 0004; break;
1488 case '-': break;
1489 default: return -1;
1491 switch (*(p++)){
1492 case 'w': res |= 0002; break;
1493 case '-': break;
1494 default: return -1;
1496 switch (*(p++)){
1497 case 'x': res |= 0001; break;
1498 case 't': res |= 0001 | S_ISVTX; break;
1499 case 'T': res |= S_ISVTX; break;
1500 case '-': break;
1501 default: return -1;
1503 return res;
1506 int vfs_parse_filedate(int idx, time_t *t)
1507 { /* This thing parses from idx in columns[] array */
1509 char *p;
1510 struct tm tim;
1511 int d[3];
1512 int got_year = 0;
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;
1518 tim.tm_hour = 0;
1519 tim.tm_min = 0;
1520 tim.tm_sec = 0;
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++];
1529 /* Month name */
1530 if(is_month(p, &tim)){
1531 /* And we expect, it followed by day number */
1532 if (is_num (idx))
1533 tim.tm_mday = (int)atol (columns [idx++]);
1534 else
1535 return 0; /* No day */
1537 } else {
1538 /* We usually expect:
1539 Mon DD hh:mm
1540 Mon DD YYYY
1541 But in case of extfs we allow these date formats:
1542 Mon DD YYYY hh:mm
1543 Mon DD hh:mm YYYY
1544 Wek Mon DD hh:mm:ss YYYY
1545 MM-DD-YY hh:mm
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)){
1551 p[2] = p[5] = '-';
1553 if(sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
1554 /* We expect to get:
1555 1. MM-DD-YY
1556 2. DD-MM-YY
1557 3. YY-MM-DD
1558 4. YY-DD-MM */
1560 /* Hmm... maybe, next time :)*/
1562 /* At last, MM-DD-YY */
1563 d[0]--; /* Months are zerobased */
1564 /* Y2K madness */
1565 if(d[2] < 70)
1566 d[2] += 100;
1568 tim.tm_mon = d[0];
1569 tim.tm_mday = d[1];
1570 tim.tm_year = d[2];
1571 got_year = 1;
1572 } else
1573 return 0; /* sscanf failed */
1574 } else
1575 return 0; /* unsupported format */
1578 /* Here we expect to find time and/or year */
1580 if (is_num (idx)) {
1581 if(is_time(columns[idx], &tim) || (got_year = is_year(columns[idx], &tim))) {
1582 idx++;
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 */
1590 else
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
1599 if (!got_year &&
1600 current_mon < 6 && current_mon < tim.tm_mon &&
1601 tim.tm_mon - current_mon >= 6)
1603 tim.tm_year--;
1605 if ((*t = mktime(&tim)) < 0)
1606 *t = 0;
1607 return idx;
1611 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
1613 int idx, idx2, num_cols;
1614 int i;
1615 char *p_copy;
1616 char *t = NULL;
1618 if (strncmp (p, "total", 5) == 0)
1619 return 0;
1621 p_copy = g_strdup(p);
1622 if ((i = vfs_parse_filetype(*(p++))) == -1)
1623 goto error;
1625 s->st_mode = i;
1626 if (*p == ' ') /* Notwell 4 */
1627 p++;
1628 if (*p == '['){
1629 if (strlen (p) <= 8 || p [8] != ']')
1630 goto error;
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);
1634 else
1635 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
1636 p += 9;
1637 } else {
1638 if ((i = vfs_parse_filemode(p)) == -1)
1639 goto error;
1640 s->st_mode |= i;
1641 p += 9;
1643 /* This is for an extra ACL attribute (HP-UX) */
1644 if (*p == '+')
1645 p++;
1648 g_free(p_copy);
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)
1654 goto error;
1656 if (!is_num (1))
1657 s->st_uid = finduid (columns [1]);
1658 else
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]))
1664 break;
1666 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
1667 goto error;
1669 /* We don't have gid */
1670 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
1671 idx2 = 2;
1672 else {
1673 /* We have gid field */
1674 if (is_num (2))
1675 s->st_gid = (gid_t) atol (columns [2]);
1676 else
1677 s->st_gid = findgid (columns [2]);
1678 idx2 = 3;
1681 /* This is device */
1682 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
1683 int maj, min;
1685 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
1686 goto error;
1688 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
1689 goto error;
1691 #ifdef HAVE_ST_RDEV
1692 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
1693 #endif
1694 s->st_size = 0;
1696 } else {
1697 /* Common file size */
1698 if (!is_num (idx2))
1699 goto error;
1701 s->st_size = (size_t) atol (columns [idx2]);
1702 #ifdef HAVE_ST_RDEV
1703 s->st_rdev = 0;
1704 #endif
1707 idx = vfs_parse_filedate(idx, &s->st_mtime);
1708 if (!idx)
1709 goto error;
1710 /* Use resulting time value */
1711 s->st_atime = s->st_ctime = s->st_mtime;
1712 #if 0
1713 /* These variables must be initialized by vfs_s_new_inode () */
1714 s->st_dev = 0;
1715 s->st_ino = 0;
1716 #endif
1717 #ifdef HAVE_ST_BLKSIZE
1718 s->st_blksize = 512;
1719 #endif
1720 #ifdef HAVE_ST_BLOCKS
1721 s->st_blocks = (s->st_size + 511) / 512;
1722 #endif
1724 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
1725 if (strcmp (columns [i], "->") == 0){
1726 idx2 = i;
1727 break;
1730 if (((S_ISLNK (s->st_mode) ||
1731 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
1732 && idx2){
1734 if (filename){
1735 *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
1737 if (linkname){
1738 t = g_strdup (p + column_ptr [idx2+1]);
1739 *linkname = t;
1741 } else {
1742 /* Extract the filename from the string copy, not from the columns
1743 * this way we have a chance of entering hidden directories like ". ."
1745 if (filename){
1747 *filename = g_strdup (columns [idx++]);
1750 t = g_strdup (p + column_ptr [idx]);
1751 *filename = t;
1753 if (linkname)
1754 *linkname = NULL;
1757 if (t) {
1758 int p = strlen (t);
1759 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1760 t [p] = 0;
1761 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1762 t [p] = 0;
1765 g_free (p_copy);
1766 return 1;
1768 error:
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)"));
1778 g_free (p_copy);
1779 return 0;
1782 void
1783 vfs_die (char *m)
1785 message_1s (1, _("Internal error:"), m);
1786 exit (1);
1789 void
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");
1799 if (need)
1800 print_vfs_message (i18n_percent_transf_format,
1801 fs_name, action, file_name, have*100/need, have);
1802 else
1803 print_vfs_message (i18n_transf_format,
1804 fs_name, action, file_name, have);
1807 #ifndef VFS_STANDALONE
1808 char *
1809 vfs_get_password (char *msg)
1811 return (char *) input_dialog (msg, _("Password:"), "");
1813 #endif
1816 * Returns vfs path corresponding to given url. If passed string is
1817 * not recognized as url, g_strdup(url) is returned.
1819 char *
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");
1826 else
1827 return g_strdup (url);