mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / vfs.c
blobf069cb88d2a89b8d86df9ed80c540b09577659a0
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 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* Warning: funtions like extfs_lstat() have right to destroy any
23 * strings you pass to them. This is acutally ok as you g_strdup what
24 * you are passing to them, anyway; still, beware. */
26 /* Namespace: exports *many* functions with vfs_ prefix; exports
27 parse_ls_lga and friends which do not have that prefix. */
29 #include <config.h>
31 #ifndef NO_SYSLOG_H
32 # include <syslog.h>
33 #endif
35 #include <stdio.h>
36 #include <stdlib.h> /* For atol() */
37 #include <stdarg.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <signal.h>
43 #include "utilvfs.h"
45 #include "../src/dir.h"
46 #include "../src/main.h"
47 #include "../src/panel.h"
48 #include "../src/key.h" /* Required for the async alarm handler */
49 #include "../src/layout.h" /* For get_panel_widget and get_other_index */
50 #include "../src/wtools.h" /* input_dialog() */
52 #include "xdirentry.h"
53 #include "vfs.h"
54 #include "extfs.h" /* FIXME: we should not know anything about our modules */
55 #include "names.h"
56 #ifdef USE_NETCODE
57 # include "tcputil.h"
58 #endif
60 extern int get_other_type (void);
62 int vfs_timeout = 60; /* VFS timeout in seconds */
63 static int vfs_flags = 0; /* Flags */
65 extern int cd_symlinks; /* Defined in main.c */
67 /* They keep track of the current directory */
68 static vfs *current_vfs = &vfs_local_ops;
69 static char *current_dir = NULL;
72 * FIXME: this is broken. It depends on mc not crossing border on month!
74 static int current_mday;
75 static int current_mon;
76 static int current_year;
78 /* FIXME: Open files managed by the vfs layer, should be dynamical */
79 #define MAX_VFS_FILES 100
81 static struct {
82 void *fs_info;
83 vfs *operations;
84 } vfs_file_table [MAX_VFS_FILES];
86 static int
87 get_bucket (void)
89 int i;
91 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
92 for (i = 3; i < MAX_VFS_FILES; i++){
93 if (!vfs_file_table [i].fs_info)
94 return i;
97 vfs_die ("No more virtual file handles");
98 return 0;
101 /* vfs_local_ops needs to be the first one */
102 static vfs *vfs_list = &vfs_local_ops;
104 static int
105 vfs_register (vfs *vfs)
107 if (!vfs)
108 vfs_die("You can not register NULL.");
110 if (vfs->init) /* vfs has own initialization function */
111 if (!(*vfs->init)(vfs)) /* but it failed */
112 return 0;
114 vfs->next = vfs_list;
115 vfs_list = vfs;
117 return 1;
120 static vfs *
121 vfs_type_from_op (char *path)
123 vfs *vfs;
125 if (!path)
126 vfs_die ("vfs_type_from_op got NULL: impossible");
128 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
129 if (vfs->which) {
130 if ((*vfs->which) (vfs, path) == -1)
131 continue;
132 return vfs;
134 if (!strncmp (path, vfs->prefix, strlen (vfs->prefix)))
135 return vfs;
137 return NULL; /* shut up stupid gcc */
140 /* Strip known vfs suffixes from a filename (possible improvement: strip
141 suffix from last path component).
142 Returns a malloced string which has to be freed. */
143 char *
144 vfs_strip_suffix_from_filename (const char *filename)
146 vfs *vfs;
147 char *semi;
148 char *p;
150 if (!filename)
151 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
153 p = g_strdup (filename);
154 if (!(semi = strrchr (p, '#')))
155 return p;
157 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
158 if (vfs->which){
159 if ((*vfs->which) (vfs, semi + 1) == -1)
160 continue;
161 *semi = '\0'; /* Found valid suffix */
162 return p;
164 if (!strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
165 *semi = '\0'; /* Found valid suffix */
166 return p;
169 return p;
172 static int
173 path_magic (const char *path)
175 struct stat buf;
177 if (vfs_flags & FL_ALWAYS_MAGIC)
178 return 1;
180 if (!stat(path, &buf))
181 return 0;
183 return 1;
187 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
188 * What is left in path is p1. You still want to g_free(path), you DON'T
189 * want to free neither *inpath nor *op
191 vfs *
192 vfs_split (char *path, char **inpath, char **op)
194 char *semi;
195 char *slash;
196 vfs *ret;
198 if (!path)
199 vfs_die("Can not split NULL");
201 semi = strrchr (path, '#');
202 if (!semi || !path_magic(path))
203 return NULL;
205 slash = strchr (semi, PATH_SEP);
206 *semi = 0;
208 if (op)
209 *op = NULL;
211 if (inpath)
212 *inpath = NULL;
214 if (slash)
215 *slash = 0;
217 if ((ret = vfs_type_from_op (semi+1))){
218 if (op)
219 *op = semi + 1;
220 if (inpath)
221 *inpath = slash ? slash + 1 : NULL;
222 return ret;
226 if (slash)
227 *slash = PATH_SEP;
228 ret = vfs_split (path, inpath, op);
229 *semi = '#';
230 return ret;
233 static vfs *
234 vfs_rosplit (char *path)
236 char *semi;
237 char *slash;
238 vfs *ret;
240 g_return_val_if_fail(path, NULL);
242 semi = strrchr (path, '#');
243 if (!semi || !path_magic (path))
244 return NULL;
246 slash = strchr (semi, PATH_SEP);
247 *semi = 0;
248 if (slash)
249 *slash = 0;
251 ret = vfs_type_from_op (semi+1);
252 if (!ret && (vfs_flags & FL_NO_LOCALHASH))
253 return &vfs_nil_ops;
255 if (slash)
256 *slash = PATH_SEP;
257 if (!ret)
258 ret = vfs_rosplit (path);
260 *semi = '#';
261 return ret;
264 vfs *
265 vfs_type (char *path)
267 vfs *vfs;
269 vfs = vfs_rosplit(path);
271 if (!vfs)
272 vfs = &vfs_local_ops;
274 return vfs;
277 static struct vfs_stamping *stamps;
280 * Returns the number of seconds remaining to the vfs timeout
282 * FIXME: currently this is set to 10 seconds. We should compute this.
285 vfs_timeouts ()
287 return stamps ? 10 : 0;
290 static void
291 vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
293 if (v != &vfs_local_ops && id != (vfsid)-1){
294 struct vfs_stamping *stamp;
295 struct vfs_stamping *last_stamp = NULL;
297 for (stamp = stamps; stamp != NULL; stamp = stamp->next) {
298 if (stamp->v == v && stamp->id == id){
299 gettimeofday(&(stamp->time), NULL);
300 return;
302 last_stamp = stamp;
304 stamp = g_new (struct vfs_stamping, 1);
305 stamp->v = v;
306 stamp->id = id;
307 if (parent){
308 struct vfs_stamping *st = stamp;
309 while (parent){
310 st->parent = g_new (struct vfs_stamping, 1);
311 *st->parent = *parent;
312 parent = parent->parent;
313 st = st->parent;
315 st->parent = 0;
317 else
318 stamp->parent = 0;
320 gettimeofday (&(stamp->time), NULL);
321 stamp->next = 0;
323 if (stamps) {
324 /* Add to the end */
325 last_stamp->next = stamp;
326 } else {
327 /* Add first element */
328 stamps = stamp;
333 void
334 vfs_stamp (vfs *v, vfsid id)
336 struct vfs_stamping *stamp;
338 for (stamp = stamps; stamp != NULL; stamp = stamp->next)
339 if (stamp->v == v && stamp->id == id){
341 gettimeofday (&(stamp->time), NULL);
342 if (stamp->parent != NULL)
343 vfs_stamp (stamp->parent->v, stamp->parent->id);
345 return;
349 void
350 vfs_rm_parents (struct vfs_stamping *stamp)
352 struct vfs_stamping *parent;
354 while (stamp) {
355 parent = stamp->parent;
356 g_free (stamp);
357 stamp = parent;
361 void
362 vfs_rmstamp (vfs *v, vfsid id, int removeparents)
364 struct vfs_stamping *stamp, *st1;
366 for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
367 if (stamp->v == v && stamp->id == id){
368 if (stamp->parent != NULL){
369 if (removeparents)
370 vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
371 vfs_rm_parents (stamp->parent);
373 if (st1 == NULL){
374 stamps = stamp->next;
375 } else {
376 st1->next = stamp->next;
378 g_free (stamp);
380 return;
384 static int
385 ferrno (vfs *vfs)
387 return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
388 /* Hope that error message is obscure enough ;-) */
392 mc_open (const char *filename, int flags, ...)
394 int handle;
395 int mode;
396 void *info;
397 va_list ap;
399 char *file = vfs_canon (filename);
400 vfs *vfs = vfs_type (file);
402 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
403 va_start (ap, flags);
404 mode = va_arg (ap, int);
405 va_end (ap);
407 if (!vfs->open) {
408 errno = -EOPNOTSUPP;
409 return -1;
412 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
413 g_free (file);
414 if (!info){
415 errno = ferrno (vfs);
416 return -1;
418 handle = get_bucket ();
419 vfs_file_table [handle].fs_info = info;
420 vfs_file_table [handle].operations = vfs;
422 return handle;
425 #define vfs_op(handle) vfs_file_table [handle].operations
426 #define vfs_info(handle) vfs_file_table [handle].fs_info
427 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
429 #define MC_OP(name, inarg, callarg, pre, post) \
430 int mc_##name inarg \
432 vfs *vfs; \
433 int result; \
435 pre \
436 result = vfs->name ? (*vfs->name)callarg : -1; \
437 post \
438 if (result == -1) \
439 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
440 return result; \
443 #define MC_NAMEOP(name, inarg, callarg) \
444 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
445 #define MC_HANDLEOP(name, inarg, callarg) \
446 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
448 MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) )
451 mc_ctl (int handle, int ctlop, int arg)
453 vfs *vfs = vfs_op (handle);
455 return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
459 mc_setctl (char *path, int ctlop, char *arg)
461 vfs *vfs;
462 int result;
464 if (!path)
465 vfs_die("You don't want to pass NULL to mc_setctl.");
467 path = vfs_canon (path);
468 vfs = vfs_type (path);
469 result = vfs->setctl ? (*vfs->setctl)(vfs, path, ctlop, arg) : 0;
470 g_free (path);
471 return result;
475 mc_close (int handle)
477 vfs *vfs;
478 int result;
480 if (handle == -1 || !vfs_info (handle))
481 return -1;
483 vfs = vfs_op (handle);
484 if (handle < 3)
485 return close (handle);
487 if (!vfs->close)
488 vfs_die ("VFS must support close.\n");
489 result = (*vfs->close)(vfs_info (handle));
490 vfs_free_bucket (handle);
491 if (result == -1)
492 errno = ferrno (vfs);
494 return result;
497 DIR *
498 mc_opendir (char *dirname)
500 int handle, *handlep;
501 void *info;
502 vfs *vfs;
504 dirname = vfs_canon (dirname);
505 vfs = vfs_type (dirname);
507 info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
508 g_free (dirname);
509 if (!info){
510 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
511 return NULL;
513 handle = get_bucket ();
514 vfs_file_table [handle].fs_info = info;
515 vfs_file_table [handle].operations = vfs;
517 handlep = g_new (int, 1);
518 *handlep = handle;
519 return (DIR *) handlep;
522 /* This should strip the non needed part of a path name */
523 #define vfs_name(x) x
525 void
526 mc_seekdir (DIR *dirp, int offset)
528 int handle;
529 vfs *vfs;
531 if (!dirp){
532 errno = EFAULT;
533 return;
535 handle = *(int *) dirp;
536 vfs = vfs_op (handle);
537 if (vfs->seekdir)
538 (*vfs->seekdir) (vfs_info (handle), offset);
539 else
540 errno = E_NOTSUPP;
543 #define MC_DIROP(name, type, onerr ) \
544 type mc_##name (DIR *dirp) \
546 int handle; \
547 vfs *vfs; \
548 type result; \
550 if (!dirp){ \
551 errno = EFAULT; \
552 return onerr; \
554 handle = *(int *) dirp; \
555 vfs = vfs_op (handle); \
556 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
557 if (result == onerr) \
558 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
559 return result; \
562 MC_DIROP (readdir, struct dirent *, NULL)
563 MC_DIROP (telldir, int, -1)
566 mc_closedir (DIR *dirp)
568 int handle = *(int *) dirp;
569 vfs *vfs = vfs_op (handle);
570 int result;
572 result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
573 vfs_free_bucket (handle);
574 g_free (dirp);
575 return result;
578 int mc_stat (char *path, struct stat *buf) {
579 vfs *vfs;
580 int result;
582 path = vfs_canon (path); vfs = vfs_type (path);
583 result = vfs->stat ? (*vfs->stat) (vfs, vfs_name (path), buf) : -1;
584 g_free (path);
585 if (result == -1)
586 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
587 return result;
590 int mc_lstat (char *path, struct stat *buf) {
591 vfs *vfs;
592 int result;
594 path = vfs_canon (path); vfs = vfs_type (path);
595 result = vfs->lstat ? (*vfs->lstat) (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_fstat (int handle, struct stat *buf) {
603 vfs *vfs;
604 int result;
606 if (handle == -1)
607 return -1;
608 vfs = vfs_op (handle);
609 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
610 if (result == -1)
611 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
612 return result;
616 * You must g_strdup whatever this function returns.
619 static const char *
620 mc_return_cwd (void)
622 char *p;
623 struct stat my_stat, my_stat2;
625 if (!vfs_rosplit (current_dir)){
626 p = g_get_current_dir ();
627 if (!p) /* One of the directories in the path is not readable */
628 return current_dir;
630 /* Otherwise check if it is O.K. to use the current_dir */
631 if (!cd_symlinks ||
632 mc_stat (p, &my_stat) ||
633 mc_stat (current_dir, &my_stat2) ||
634 my_stat.st_ino != my_stat2.st_ino ||
635 my_stat.st_dev != my_stat2.st_dev){
636 g_free (current_dir);
637 current_dir = p;
638 return p;
639 } /* Otherwise we return current_dir below */
640 g_free (p);
642 return current_dir;
645 char *
646 mc_get_current_wd (char *buffer, int size)
648 const char *cwd = mc_return_cwd();
650 strncpy (buffer, cwd, size);
651 return buffer;
654 MC_NAMEOP (chmod, (char *path, int mode), (vfs, vfs_name (path), mode))
655 MC_NAMEOP (chown, (char *path, int owner, int group), (vfs, vfs_name (path), owner, group))
656 MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs, vfs_name (path), times))
657 MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs, vfs_name (path), buf, bufsiz))
658 MC_NAMEOP (unlink, (char *path), (vfs, vfs_name (path)))
659 MC_NAMEOP (symlink, (char *name1, char *path), (vfs, vfs_name (name1), vfs_name (path)))
661 #define MC_RENAMEOP(name) \
662 int mc_##name (const char *fname1, const char *fname2) \
664 vfs *vfs; \
665 int result; \
667 char *name2, *name1 = vfs_canon (fname1); \
668 vfs = vfs_type (name1); \
669 name2 = vfs_canon (fname2); \
670 if (vfs != vfs_type (name2)){ \
671 errno = EXDEV; \
672 g_free (name1); \
673 g_free (name2); \
674 return -1; \
677 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
678 g_free (name1); \
679 g_free (name2); \
680 if (result == -1) \
681 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
682 return result; \
685 MC_RENAMEOP (link)
686 MC_RENAMEOP (rename)
688 MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte))
690 off_t mc_lseek (int fd, off_t offset, int whence)
692 vfs *vfs;
693 int result;
695 if (fd == -1)
696 return -1;
698 vfs = vfs_op (fd);
699 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
700 if (result == -1)
701 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
702 return result;
706 * remove //, /./ and /../, local should point to big enough buffer
709 #define ISSLASH(a) (!a || (a == '/'))
711 char *
712 vfs_canon (const char *path)
714 if (!path)
715 vfs_die("Cannot canonicalize NULL");
717 /* Tilde expansion */
718 if (*path == '~'){
719 char *local, *result;
721 local = tilde_expand (path);
722 if (local){
723 result = vfs_canon (local);
724 g_free (local);
725 return result;
729 /* Relative to current directory */
730 if (*path != PATH_SEP){
731 char *local, *result;
733 local = concat_dir_and_file (current_dir, path);
735 result = vfs_canon (local);
736 g_free (local);
737 return result;
741 * So we have path of following form:
742 * /p1/p2#op/.././././p3#op/p4. Good luck.
745 char *result = g_strdup (path);
746 canonicalize_pathname (result);
747 return result;
751 vfsid
752 vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
754 vfsid nvfsid;
756 dir = concat_dir_and_file (dir, "");
758 nvfsid = (*nvfs->getid)(nvfs, dir, par);
760 g_free (dir);
761 return nvfsid;
764 static int
765 is_parent (vfs * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
767 struct vfs_stamping *stamp;
769 for (stamp = parent; stamp; stamp = stamp->parent)
770 if (stamp->v == nvfs && stamp->id == nvfsid)
771 break;
773 return (stamp ? 1 : 0);
776 void
777 vfs_add_noncurrent_stamps (vfs * oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
779 vfs *nvfs, *n2vfs, *n3vfs;
780 vfsid nvfsid, n2vfsid, n3vfsid;
781 struct vfs_stamping *par, *stamp;
782 int f;
784 /* FIXME: As soon as we convert to multiple panels, this stuff
785 has to change. It works like this: We do not time out the
786 vfs's which are current in any panel and on the other
787 side we add the old directory with all its parents which
788 are not in any panel (if we find such one, we stop adding
789 parents to the time-outing structure. */
791 /* There are three directories we have to take care of: current_dir,
792 cpanel->cwd and opanel->cwd. Athough most of the time either
793 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
794 same, it's possible that all three are different -- Norbert */
796 if (!cpanel)
797 return;
799 nvfs = vfs_type (current_dir);
800 nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
801 vfs_rmstamp (nvfs, nvfsid, 1);
803 f = is_parent (oldvfs, oldvfsid, par);
804 vfs_rm_parents (par);
805 if ((nvfs == oldvfs && nvfsid == oldvfsid) || oldvfsid == (vfsid *)-1 || f){
806 return;
809 if (get_current_type () == view_listing){
810 n2vfs = vfs_type (cpanel->cwd);
811 n2vfsid = vfs_ncs_getid (n2vfs, cpanel->cwd, &par);
812 f = is_parent (oldvfs, oldvfsid, par);
813 vfs_rm_parents (par);
814 if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
815 return;
816 } else {
817 n2vfs = (vfs *) -1;
818 n2vfsid = (vfs *) -1;
821 if (get_other_type () == view_listing){
822 n3vfs = vfs_type (opanel->cwd);
823 n3vfsid = vfs_ncs_getid (n3vfs, opanel->cwd, &par);
824 f = is_parent (oldvfs, oldvfsid, par);
825 vfs_rm_parents (par);
826 if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
827 return;
828 } else {
829 n3vfs = (vfs *)-1;
830 n3vfsid = (vfs *)-1;
833 if ((*oldvfs->nothingisopen) (oldvfsid)){
834 if (oldvfs == &vfs_extfs_ops && ((extfs_archive *) oldvfsid)->name == 0){
835 /* Free the resources immediatly when we leave a mtools fs
836 ('cd a:') instead of waiting for the vfs-timeout */
837 (oldvfs->free) (oldvfsid);
838 } else
839 vfs_addstamp (oldvfs, oldvfsid, parent);
840 for (stamp = parent; stamp != NULL; stamp = stamp->parent){
841 if ((stamp->v == nvfs && stamp->id == nvfsid) ||
842 (stamp->v == n2vfs && stamp->id == n2vfsid) ||
843 (stamp->v == n3vfs && stamp->id == n3vfsid) ||
844 stamp->id == (vfsid) - 1 ||
845 !(*stamp->v->nothingisopen) (stamp->id))
846 break;
847 if (stamp->v == &vfs_extfs_ops && ((extfs_archive *) stamp->id)->name == 0){
848 (stamp->v->free) (stamp->id);
849 vfs_rmstamp (stamp->v, stamp->id, 0);
850 } else
851 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
856 static void
857 vfs_stamp_path (char *path)
859 vfs *vfs;
860 vfsid id;
861 struct vfs_stamping *par, *stamp;
863 vfs = vfs_type (path);
864 id = vfs_ncs_getid (vfs, path, &par);
865 vfs_addstamp (vfs, id, par);
867 for (stamp = par; stamp != NULL; stamp = stamp->parent)
868 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
869 vfs_rm_parents (par);
872 void
873 vfs_add_current_stamps (void)
875 vfs_stamp_path (current_dir);
877 if (cpanel) {
878 if (get_current_type () == view_listing)
879 vfs_stamp_path (cpanel->cwd);
882 if (opanel) {
883 if (get_other_type () == view_listing)
884 vfs_stamp_path (opanel->cwd);
888 /* This function is really broken */
890 mc_chdir (char *path)
892 char *a, *b;
893 int result;
894 char *p = NULL;
895 vfs *oldvfs;
896 vfsid oldvfsid;
897 struct vfs_stamping *parent;
899 a = current_dir; /* Save a copy for case of failure */
900 current_dir = vfs_canon (path);
901 current_vfs = vfs_type (current_dir);
902 b = g_strdup (current_dir);
903 result = (*current_vfs->chdir) ? (*current_vfs->chdir)(current_vfs, vfs_name (b)) : -1;
904 g_free (b);
905 if (result == -1){
906 errno = ferrno (current_vfs);
907 g_free (current_dir);
908 current_vfs = vfs_type (a);
909 current_dir = a;
910 } else {
911 oldvfs = vfs_type (a);
912 oldvfsid = vfs_ncs_getid (oldvfs, a, &parent);
913 g_free (a);
914 vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
915 vfs_rm_parents (parent);
918 if (*current_dir){
919 p = strchr (current_dir, 0) - 1;
920 if (*p == PATH_SEP && p > current_dir)
921 *p = 0; /* Sometimes we assume no trailing slash on cwd */
923 return result;
927 vfs_current_is_local (void)
929 return current_vfs == &vfs_local_ops;
933 vfs_file_is_local (const char *file)
935 char *filename = vfs_canon (file);
936 vfs *vfs = vfs_type (filename);
938 g_free (filename);
939 return vfs == &vfs_local_ops;
943 vfs_file_is_ftp (char *filename)
945 #ifdef USE_NETCODE
946 vfs *vfs;
948 filename = vfs_canon (filename);
949 vfs = vfs_type (filename);
950 g_free (filename);
951 return vfs == &vfs_ftpfs_ops;
952 #else
953 return 0;
954 #endif
958 vfs_file_is_smb (char *filename)
960 #ifdef WITH_SMBFS
961 #ifdef USE_NETCODE
962 vfs *vfs;
964 filename = vfs_canon (filename);
965 vfs = vfs_type (filename);
966 g_free (filename);
967 return vfs == &vfs_smbfs_ops;
968 #endif /* USE_NETCODE */
969 #endif /* WITH_SMBFS */
970 return 0;
973 char *vfs_get_current_dir (void)
975 return current_dir;
978 static void vfs_setup_wd (void)
980 current_dir = g_strdup (PATH_SEP_STR);
981 if (!(vfs_flags & FL_NO_CWDSETUP))
982 mc_return_cwd();
984 if (strlen(current_dir)>MC_MAXPATHLEN-2)
985 vfs_die ("Current dir too long.\n");
988 MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs, vfs_name (path), mode))
989 MC_NAMEOP (rmdir, (char *path), (vfs, vfs_name (path)))
990 MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs, vfs_name (path), mode, dev))
992 #ifdef HAVE_MMAP
993 static struct mc_mmapping {
994 caddr_t addr;
995 void *vfs_info;
996 vfs *vfs;
997 struct mc_mmapping *next;
998 } *mc_mmaparray = NULL;
1000 caddr_t
1001 mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
1003 vfs *vfs;
1004 caddr_t result;
1005 struct mc_mmapping *mcm;
1007 if (fd == -1)
1008 return (caddr_t) -1;
1010 vfs = vfs_op (fd);
1011 result = vfs->mmap ? (*vfs->mmap)(vfs, addr, len, prot, flags, vfs_info (fd), offset) : (caddr_t)-1;
1012 if (result == (caddr_t)-1){
1013 errno = ferrno (vfs);
1014 return (caddr_t)-1;
1016 mcm =g_new (struct mc_mmapping, 1);
1017 mcm->addr = result;
1018 mcm->vfs_info = vfs_info (fd);
1019 mcm->vfs = vfs;
1020 mcm->next = mc_mmaparray;
1021 mc_mmaparray = mcm;
1022 return result;
1026 mc_munmap (caddr_t addr, size_t len)
1028 struct mc_mmapping *mcm, *mcm2 = NULL;
1030 for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
1031 if (mcm->addr == addr){
1032 if (mcm2 == NULL)
1033 mc_mmaparray = mcm->next;
1034 else
1035 mcm2->next = mcm->next;
1036 if (mcm->vfs->munmap)
1037 (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
1038 g_free (mcm);
1039 return 0;
1042 return -1;
1045 #endif
1047 char *
1048 mc_def_getlocalcopy (vfs *vfs, char *filename)
1050 char *tmp;
1051 int fdin, fdout, i;
1052 char buffer[8192];
1053 struct stat mystat;
1054 char *ext = NULL;
1055 char *ptr;
1057 fdin = mc_open (filename, O_RDONLY);
1058 if (fdin == -1)
1059 return NULL;
1061 /* Try to preserve existing extension */
1062 for (ptr = filename + strlen(filename) - 1; ptr >= filename; ptr--) {
1063 if (*ptr == '.') {
1064 ext = ptr;
1065 break;
1068 if (!isalnum((unsigned char) *ptr))
1069 break;
1072 fdout = mc_mkstemps (&tmp, "mclocalcopy", ext);
1073 if (fdout == -1)
1074 goto fail;
1075 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0){
1076 if (write (fdout, buffer, i) != i)
1077 goto fail;
1079 if (i == -1)
1080 goto fail;
1081 i = mc_close (fdin);
1082 fdin = -1;
1083 if (i==-1)
1084 goto fail;
1085 if (close (fdout)==-1)
1086 goto fail;
1088 if (mc_stat (filename, &mystat) != -1){
1089 chmod (tmp, mystat.st_mode);
1091 return tmp;
1093 fail:
1094 if (fdout) close(fdout);
1095 if (fdin) mc_close (fdin);
1096 g_free (tmp);
1097 return NULL;
1100 char *
1101 mc_getlocalcopy (const char *pathname)
1103 char *result;
1104 char *path = vfs_canon (pathname);
1105 vfs *vfs = vfs_type (path);
1107 result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, vfs_name (path)) :
1108 mc_def_getlocalcopy (vfs, vfs_name (path));
1109 g_free (path);
1110 if (!result)
1111 errno = ferrno (vfs);
1112 return result;
1116 mc_def_ungetlocalcopy (vfs *vfs, char *filename, char *local, int has_changed)
1117 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1118 int fdin = -1, fdout = -1, i;
1119 if (has_changed){
1120 char buffer [8192];
1122 fdin = open (local, O_RDONLY);
1123 if (fdin == -1)
1124 goto failed;
1125 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1126 if (fdout == -1)
1127 goto failed;
1128 while ((i = read (fdin, buffer, sizeof (buffer))) > 0){
1129 if (mc_write (fdout, buffer, i) != i)
1130 goto failed;
1132 if (i == -1)
1133 goto failed;
1135 if (close (fdin)==-1) {
1136 fdin = -1;
1137 goto failed;
1139 fdin = -1;
1140 if (mc_close (fdout)==-1) {
1141 fdout = -1;
1142 goto failed;
1145 unlink (local);
1146 g_free (local);
1147 return 0;
1149 failed:
1150 message_1s (1, _("Changes to file lost"), filename);
1151 if (fdout!=-1) mc_close(fdout);
1152 if (fdin!=-1) close(fdin);
1153 unlink (local);
1154 g_free (local);
1155 return -1;
1159 mc_ungetlocalcopy (const char *pathname, char *local, int has_changed)
1161 int return_value = 0;
1162 char *path = vfs_canon (pathname);
1163 vfs *vfs = vfs_type (path);
1165 return_value = vfs->ungetlocalcopy ?
1166 (*vfs->ungetlocalcopy)(vfs, vfs_name (path), local, has_changed) :
1167 mc_def_ungetlocalcopy (vfs, vfs_name (path), local, has_changed);
1168 g_free (path);
1169 return return_value;
1173 * Hmm, as timeout is minute or so, do we need to care about usecs?
1175 static inline int
1176 timeoutcmp (struct timeval *t1, struct timeval *t2)
1178 return ((t1->tv_sec < t2->tv_sec)
1179 || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
1182 /* This is called from timeout handler with now = 0, or can be called
1183 with now = 1 to force freeing all filesystems that are not in use */
1185 void
1186 vfs_expire (int now)
1188 static int locked = 0;
1189 struct timeval time;
1190 struct vfs_stamping *stamp, *st;
1192 /* Avoid recursive invocation, e.g. when one of the free functions
1193 calls message_1s */
1194 if (locked)
1195 return;
1196 locked = 1;
1198 gettimeofday (&time, NULL);
1199 time.tv_sec -= vfs_timeout;
1201 for (stamp = stamps; stamp != NULL;){
1202 if (now || (timeoutcmp (&stamp->time, &time))){
1203 st = stamp->next;
1204 (*stamp->v->free) (stamp->id);
1205 vfs_rmstamp (stamp->v, stamp->id, 0);
1206 stamp = st;
1207 } else
1208 stamp = stamp->next;
1210 locked = 0;
1213 void
1214 vfs_timeout_handler (void)
1216 vfs_expire (0);
1219 void
1220 vfs_init (void)
1222 time_t current_time;
1223 struct tm *t;
1225 memset (vfs_file_table, 0, sizeof (vfs_file_table));
1226 current_time = time (NULL);
1227 t = localtime (&current_time);
1228 current_mday = t->tm_mday;
1229 current_mon = t->tm_mon;
1230 current_year = t->tm_year;
1232 /* We do not want to register vfs_local_ops */
1234 #ifdef USE_NETCODE
1235 tcp_init();
1236 vfs_register (&vfs_ftpfs_ops);
1237 vfs_register (&vfs_fish_ops);
1238 #ifdef WITH_SMBFS
1239 vfs_register (&vfs_smbfs_ops);
1240 #endif /* WITH_SMBFS */
1241 #ifdef WITH_MCFS
1242 vfs_register (&vfs_mcfs_ops);
1243 #endif /* WITH_SMBFS */
1244 #endif /* USE_NETCODE */
1246 vfs_register (&vfs_extfs_ops);
1247 vfs_register (&vfs_sfs_ops);
1248 vfs_register (&vfs_tarfs_ops);
1249 vfs_register (&vfs_cpiofs_ops);
1251 #ifdef USE_EXT2FSLIB
1252 vfs_register (&vfs_undelfs_ops);
1253 #endif /* USE_EXT2FSLIB */
1255 vfs_setup_wd ();
1258 void
1259 vfs_shut (void)
1261 struct vfs_stamping *stamp, *st;
1262 vfs *vfs;
1264 for (stamp = stamps, stamps = 0; stamp != NULL;){
1265 (*stamp->v->free)(stamp->id);
1266 st = stamp->next;
1267 g_free (stamp);
1268 stamp = st;
1271 if (stamps)
1272 vfs_rmstamp (stamps->v, stamps->id, 1);
1274 if (current_dir)
1275 g_free (current_dir);
1277 for (vfs=vfs_list; vfs; vfs=vfs->next)
1278 if (vfs->done)
1279 (*vfs->done) (vfs);
1283 * These ones grab information from the VFS
1284 * and handles them to an upper layer
1286 void
1287 vfs_fill_names (void (*func)(char *))
1289 vfs *vfs;
1291 for (vfs=vfs_list; vfs; vfs=vfs->next)
1292 if (vfs->fill_names)
1293 (*vfs->fill_names) (vfs, func);
1296 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1297 #define MAXCOLS 30
1299 static char *columns [MAXCOLS]; /* Points to the string in column n */
1300 static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
1303 vfs_split_text (char *p)
1305 char *original = p;
1306 int numcols;
1308 memset (columns, 0, sizeof (columns));
1310 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
1311 while (*p == ' ' || *p == '\r' || *p == '\n'){
1312 *p = 0;
1313 p++;
1315 columns [numcols] = p;
1316 column_ptr [numcols] = p - original;
1317 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
1318 p++;
1320 return numcols;
1323 static int
1324 is_num (int idx)
1326 char *column = columns[idx];
1328 if (!column || column[0] < '0' || column[0] > '9')
1329 return 0;
1331 return 1;
1334 static int
1335 is_dos_date (char *str)
1337 if (!str)
1338 return 0;
1340 if (strlen (str) == 8 && str[2] == str[5]
1341 && strchr ("\\-/", (int) str[2]) != NULL)
1342 return 1;
1344 return 0;
1347 static int
1348 is_week (char *str, struct tm *tim)
1350 static const char *week = "SunMonTueWedThuFriSat";
1351 char *pos;
1353 if (!str)
1354 return 0;
1356 if ((pos = strstr (week, str)) != NULL) {
1357 if (tim != NULL)
1358 tim->tm_wday = (pos - week) / 3;
1359 return 1;
1361 return 0;
1364 static int
1365 is_month (char *str, struct tm *tim)
1367 static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
1368 char *pos;
1370 if (!str)
1371 return 0;
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 (!str)
1387 return 0;
1389 if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
1390 if (p != p2) {
1391 if (sscanf
1392 (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min,
1393 &tim->tm_sec) != 3)
1394 return 0;
1395 } else {
1396 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
1397 return 0;
1399 } else
1400 return 0;
1402 return 1;
1405 static int is_year (char *str, struct tm *tim)
1407 long year;
1409 if (!str)
1410 return 0;
1412 if (strchr (str, ':'))
1413 return 0;
1415 if (strlen (str) != 4)
1416 return 0;
1418 if (sscanf (str, "%ld", &year) != 1)
1419 return 0;
1421 if (year < 1900 || year > 3000)
1422 return 0;
1424 tim->tm_year = (int) (year - 1900);
1426 return 1;
1430 * FIXME: this is broken. Consider following entry:
1431 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1432 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1436 vfs_parse_filetype (char c)
1438 switch (c) {
1439 case 'd': return S_IFDIR;
1440 case 'b': return S_IFBLK;
1441 case 'c': return S_IFCHR;
1442 case 'l': return S_IFLNK;
1443 case 's': /* Socket */
1444 #ifdef S_IFSOCK
1445 return S_IFSOCK;
1446 #else
1447 /* If not supported, we fall through to IFIFO */
1448 return S_IFIFO;
1449 #endif
1450 case 'D': /* Solaris door */
1451 #ifdef S_IFDOOR
1452 return S_IFDOOR;
1453 #else
1454 return S_IFIFO;
1455 #endif
1456 case 'p': return S_IFIFO;
1457 case 'm': case 'n': /* Don't know what these are :-) */
1458 case '-': case '?': return S_IFREG;
1459 default: return -1;
1463 int vfs_parse_filemode (const char *p)
1464 { /* converts rw-rw-rw- into 0666 */
1465 int res = 0;
1466 switch (*(p++)){
1467 case 'r': res |= 0400; break;
1468 case '-': break;
1469 default: return -1;
1471 switch (*(p++)){
1472 case 'w': res |= 0200; break;
1473 case '-': break;
1474 default: return -1;
1476 switch (*(p++)){
1477 case 'x': res |= 0100; break;
1478 case 's': res |= 0100 | S_ISUID; break;
1479 case 'S': res |= S_ISUID; break;
1480 case '-': break;
1481 default: return -1;
1483 switch (*(p++)){
1484 case 'r': res |= 0040; break;
1485 case '-': break;
1486 default: return -1;
1488 switch (*(p++)){
1489 case 'w': res |= 0020; break;
1490 case '-': break;
1491 default: return -1;
1493 switch (*(p++)){
1494 case 'x': res |= 0010; break;
1495 case 's': res |= 0010 | S_ISGID; break;
1496 case 'l': /* Solaris produces these */
1497 case 'S': res |= S_ISGID; break;
1498 case '-': break;
1499 default: return -1;
1501 switch (*(p++)){
1502 case 'r': res |= 0004; break;
1503 case '-': break;
1504 default: return -1;
1506 switch (*(p++)){
1507 case 'w': res |= 0002; break;
1508 case '-': break;
1509 default: return -1;
1511 switch (*(p++)){
1512 case 'x': res |= 0001; break;
1513 case 't': res |= 0001 | S_ISVTX; break;
1514 case 'T': res |= S_ISVTX; break;
1515 case '-': break;
1516 default: return -1;
1518 return res;
1521 /* This function parses from idx in the columns[] array */
1523 vfs_parse_filedate (int idx, time_t *t)
1525 char *p;
1526 struct tm tim;
1527 int d[3];
1528 int got_year = 0;
1530 /* Let's setup default time values */
1531 tim.tm_year = current_year;
1532 tim.tm_mon = current_mon;
1533 tim.tm_mday = current_mday;
1534 tim.tm_hour = 0;
1535 tim.tm_min = 0;
1536 tim.tm_sec = 0;
1537 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
1539 p = columns[idx++];
1541 /* We eat weekday name in case of extfs */
1542 if (is_week (p, &tim))
1543 p = columns[idx++];
1545 /* Month name */
1546 if (is_month (p, &tim)) {
1547 /* And we expect, it followed by day number */
1548 if (is_num (idx))
1549 tim.tm_mday = (int) atol (columns[idx++]);
1550 else
1551 return 0; /* No day */
1553 } else {
1554 /* We usually expect:
1555 Mon DD hh:mm
1556 Mon DD YYYY
1557 But in case of extfs we allow these date formats:
1558 Mon DD YYYY hh:mm
1559 Mon DD hh:mm YYYY
1560 Wek Mon DD hh:mm:ss YYYY
1561 MM-DD-YY hh:mm
1562 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1563 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1565 /* Here just this special case with MM-DD-YY */
1566 if (is_dos_date (p)) {
1567 p[2] = p[5] = '-';
1569 if (sscanf (p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3) {
1570 /* We expect to get:
1571 1. MM-DD-YY
1572 2. DD-MM-YY
1573 3. YY-MM-DD
1574 4. YY-DD-MM */
1576 /* Hmm... maybe, next time :) */
1578 /* At last, MM-DD-YY */
1579 d[0]--; /* Months are zerobased */
1580 /* Y2K madness */
1581 if (d[2] < 70)
1582 d[2] += 100;
1584 tim.tm_mon = d[0];
1585 tim.tm_mday = d[1];
1586 tim.tm_year = d[2];
1587 got_year = 1;
1588 } else
1589 return 0; /* sscanf failed */
1590 } else
1591 return 0; /* unsupported format */
1594 /* Here we expect to find time and/or year */
1596 if (is_num (idx)) {
1597 if (is_time (columns[idx], &tim)
1598 || (got_year = is_year (columns[idx], &tim))) {
1599 idx++;
1601 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1602 if (is_num (idx) && (columns[idx + 1][0])) {
1603 if (got_year) {
1604 if (is_time (columns[idx], &tim))
1605 idx++; /* time also */
1606 } else {
1607 if ((got_year = is_year (columns[idx], &tim)))
1608 idx++; /* year also */
1611 } /* only time or date */
1612 } else
1613 return 0; /* Nor time or date */
1616 * If the date is less than 6 months in the past, it is shown without year
1617 * other dates in the past or future are shown with year but without time
1618 * This does not check for years before 1900 ... I don't know, how
1619 * to represent them at all
1621 if (!got_year && current_mon < 6 && current_mon < tim.tm_mon
1622 && tim.tm_mon - current_mon >= 6)
1624 tim.tm_year--;
1626 if ((*t = mktime (&tim)) < 0)
1627 *t = 0;
1628 return idx;
1632 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
1634 int idx, idx2, num_cols;
1635 int i;
1636 char *p_copy = NULL;
1637 char *t = NULL;
1638 const char *line = p;
1640 if (strncmp (p, "total", 5) == 0)
1641 return 0;
1643 if ((i = vfs_parse_filetype(*(p++))) == -1)
1644 goto error;
1646 s->st_mode = i;
1647 if (*p == ' ') /* Notwell 4 */
1648 p++;
1649 if (*p == '['){
1650 if (strlen (p) <= 8 || p [8] != ']')
1651 goto error;
1652 /* Should parse here the Notwell permissions :) */
1653 if (S_ISDIR (s->st_mode))
1654 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
1655 else
1656 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
1657 p += 9;
1658 } else {
1659 if ((i = vfs_parse_filemode(p)) == -1)
1660 goto error;
1661 s->st_mode |= i;
1662 p += 9;
1664 /* This is for an extra ACL attribute (HP-UX) */
1665 if (*p == '+')
1666 p++;
1669 p_copy = g_strdup(p);
1670 num_cols = vfs_split_text (p_copy);
1672 s->st_nlink = atol (columns [0]);
1673 if (s->st_nlink <= 0)
1674 goto error;
1676 if (!is_num (1))
1677 s->st_uid = finduid (columns [1]);
1678 else
1679 s->st_uid = (uid_t) atol (columns [1]);
1681 /* Mhm, the ls -lg did not produce a group field */
1682 for (idx = 3; idx <= 5; idx++)
1683 if (is_month(columns [idx], NULL) || is_week(columns [idx], NULL) || is_dos_date(columns[idx]))
1684 break;
1686 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
1687 goto error;
1689 /* We don't have gid */
1690 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
1691 idx2 = 2;
1692 else {
1693 /* We have gid field */
1694 if (is_num (2))
1695 s->st_gid = (gid_t) atol (columns [2]);
1696 else
1697 s->st_gid = findgid (columns [2]);
1698 idx2 = 3;
1701 /* This is device */
1702 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
1703 int maj, min;
1705 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
1706 goto error;
1708 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
1709 goto error;
1711 #ifdef HAVE_ST_RDEV
1712 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
1713 #endif
1714 s->st_size = 0;
1716 } else {
1717 /* Common file size */
1718 if (!is_num (idx2))
1719 goto error;
1721 s->st_size = (size_t) atol (columns [idx2]);
1722 #ifdef HAVE_ST_RDEV
1723 s->st_rdev = 0;
1724 #endif
1727 idx = vfs_parse_filedate(idx, &s->st_mtime);
1728 if (!idx)
1729 goto error;
1730 /* Use resulting time value */
1731 s->st_atime = s->st_ctime = s->st_mtime;
1732 #ifdef HAVE_ST_BLKSIZE
1733 s->st_blksize = 512;
1734 #endif
1735 #ifdef HAVE_ST_BLOCKS
1736 s->st_blocks = (s->st_size + 511) / 512;
1737 #endif
1739 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
1740 if (strcmp (columns [i], "->") == 0){
1741 idx2 = i;
1742 break;
1745 if (((S_ISLNK (s->st_mode) ||
1746 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
1747 && idx2){
1749 if (filename){
1750 *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
1752 if (linkname){
1753 t = g_strdup (p + column_ptr [idx2+1]);
1754 *linkname = t;
1756 } else {
1757 /* Extract the filename from the string copy, not from the columns
1758 * this way we have a chance of entering hidden directories like ". ."
1760 if (filename){
1762 *filename = g_strdup (columns [idx++]);
1765 t = g_strdup (p + column_ptr [idx]);
1766 *filename = t;
1768 if (linkname)
1769 *linkname = NULL;
1772 if (t) {
1773 int p = strlen (t);
1774 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1775 t [p] = 0;
1776 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1777 t [p] = 0;
1780 g_free (p_copy);
1781 return 1;
1783 error:
1785 static int errorcount = 0;
1787 if (++errorcount < 5) {
1788 message_1s (1, _("Could not parse:"), (p_copy && *p_copy) ? p_copy : line);
1789 } else if (errorcount == 5)
1790 message_1s (1, _("Error"), _("More parsing errors will be ignored."));
1793 g_free (p_copy);
1794 return 0;
1797 void
1798 vfs_die (char *m)
1800 message_1s (1, _("Internal error:"), m);
1801 exit (1);
1804 void
1805 vfs_print_stats (const char *fs_name, const char *action, const char *file_name, off_t have, off_t need)
1807 static char *i18n_percent_transf_format = NULL, *i18n_transf_format = NULL;
1809 if (i18n_percent_transf_format == NULL) {
1810 i18n_percent_transf_format = _("%s: %s: %s %3d%% (%lu bytes transferred)");
1811 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
1814 if (need)
1815 print_vfs_message (i18n_percent_transf_format, fs_name, action,
1816 file_name, (int)((double)have*100/need), (unsigned long) have);
1817 else
1818 print_vfs_message (i18n_transf_format,
1819 fs_name, action, file_name, (unsigned long) have);
1822 char *
1823 vfs_get_password (char *msg)
1825 return (char *) input_dialog (msg, _("Password:"), INPUT_PASSWORD);
1829 * Returns vfs path corresponding to given url. If passed string is
1830 * not recognized as url, g_strdup(url) is returned.
1832 char *
1833 vfs_translate_url (char *url)
1835 if (strncmp (url, "ftp://", 6) == 0)
1836 return g_strconcat ("/#ftp:", url + 6, NULL);
1837 else if (strncmp (url, "a:", 2) == 0)
1838 return g_strdup ("/#a");
1839 else
1840 return g_strdup (url);