Committed some stilistic changes to the italian manual pages.
[midnight-commander.git] / vfs / vfs.c
blobea60362d61da28fee1cd3478811868c7e44c6c5c
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/panel.h" /* get_current_panel() */
46 #include "../src/layout.h" /* get_current_type() */
47 #include "../src/wtools.h" /* input_dialog() */
49 #include "xdirentry.h"
50 #include "vfs.h"
51 #include "extfs.h" /* FIXME: we should not know anything about our modules */
52 #include "names.h"
53 #ifdef USE_NETCODE
54 # include "tcputil.h"
55 #endif
57 int vfs_timeout = 60; /* VFS timeout in seconds */
58 static int vfs_flags = 0; /* Flags */
60 /* They keep track of the current directory */
61 static vfs *current_vfs = &vfs_local_ops;
62 static char *current_dir = NULL;
65 * FIXME: this is broken. It depends on mc not crossing border on month!
67 static int current_mday;
68 static int current_mon;
69 static int current_year;
71 /* FIXME: Open files managed by the vfs layer, should be dynamical */
72 #define MAX_VFS_FILES 100
74 static struct {
75 void *fs_info;
76 vfs *operations;
77 } vfs_file_table [MAX_VFS_FILES];
79 static int
80 get_bucket (void)
82 int i;
84 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
85 for (i = 3; i < MAX_VFS_FILES; i++){
86 if (!vfs_file_table [i].fs_info)
87 return i;
90 vfs_die ("No more virtual file handles");
91 return 0;
94 /* vfs_local_ops needs to be the first one */
95 static vfs *vfs_list = &vfs_local_ops;
97 static int
98 vfs_register (vfs *vfs)
100 if (!vfs)
101 vfs_die("You cannot register NULL.");
103 if (vfs->init) /* vfs has own initialization function */
104 if (!(*vfs->init)(vfs)) /* but it failed */
105 return 0;
107 vfs->next = vfs_list;
108 vfs_list = vfs;
110 return 1;
113 static vfs *
114 vfs_type_from_op (char *path)
116 vfs *vfs;
118 if (!path)
119 vfs_die ("vfs_type_from_op got NULL: impossible");
121 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
122 if (vfs->which) {
123 if ((*vfs->which) (vfs, path) == -1)
124 continue;
125 return vfs;
127 if (!strncmp (path, vfs->prefix, strlen (vfs->prefix)))
128 return vfs;
130 return NULL; /* shut up stupid gcc */
133 /* Strip known vfs suffixes from a filename (possible improvement: strip
134 suffix from last path component).
135 Returns a malloced string which has to be freed. */
136 char *
137 vfs_strip_suffix_from_filename (const char *filename)
139 vfs *vfs;
140 char *semi;
141 char *p;
143 if (!filename)
144 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
146 p = g_strdup (filename);
147 if (!(semi = strrchr (p, '#')))
148 return p;
150 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
151 if (vfs->which){
152 if ((*vfs->which) (vfs, semi + 1) == -1)
153 continue;
154 *semi = '\0'; /* Found valid suffix */
155 return p;
157 if (!strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
158 *semi = '\0'; /* Found valid suffix */
159 return p;
162 return p;
165 static int
166 path_magic (const char *path)
168 struct stat buf;
170 if (vfs_flags & FL_ALWAYS_MAGIC)
171 return 1;
173 if (!stat(path, &buf))
174 return 0;
176 return 1;
180 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
181 * What is left in path is p1. You still want to g_free(path), you DON'T
182 * want to free neither *inpath nor *op
184 vfs *
185 vfs_split (char *path, char **inpath, char **op)
187 char *semi;
188 char *slash;
189 vfs *ret;
191 if (!path)
192 vfs_die("Cannot split NULL");
194 semi = strrchr (path, '#');
195 if (!semi || !path_magic(path))
196 return NULL;
198 slash = strchr (semi, PATH_SEP);
199 *semi = 0;
201 if (op)
202 *op = NULL;
204 if (inpath)
205 *inpath = NULL;
207 if (slash)
208 *slash = 0;
210 if ((ret = vfs_type_from_op (semi+1))){
211 if (op)
212 *op = semi + 1;
213 if (inpath)
214 *inpath = slash ? slash + 1 : NULL;
215 return ret;
219 if (slash)
220 *slash = PATH_SEP;
221 ret = vfs_split (path, inpath, op);
222 *semi = '#';
223 return ret;
226 static vfs *
227 vfs_rosplit (char *path)
229 char *semi;
230 char *slash;
231 vfs *ret;
233 g_return_val_if_fail(path, NULL);
235 semi = strrchr (path, '#');
236 if (!semi || !path_magic (path))
237 return NULL;
239 slash = strchr (semi, PATH_SEP);
240 *semi = 0;
241 if (slash)
242 *slash = 0;
244 ret = vfs_type_from_op (semi+1);
245 if (!ret && (vfs_flags & FL_NO_LOCALHASH))
246 return &vfs_nil_ops;
248 if (slash)
249 *slash = PATH_SEP;
250 if (!ret)
251 ret = vfs_rosplit (path);
253 *semi = '#';
254 return ret;
257 vfs *
258 vfs_type (char *path)
260 vfs *vfs;
262 vfs = vfs_rosplit(path);
264 if (!vfs)
265 vfs = &vfs_local_ops;
267 return vfs;
270 static struct vfs_stamping *stamps;
273 * Returns the number of seconds remaining to the vfs timeout
275 * FIXME: currently this is set to 10 seconds. We should compute this.
278 vfs_timeouts ()
280 return stamps ? 10 : 0;
283 static void
284 vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
286 if (v != &vfs_local_ops && id != (vfsid)-1){
287 struct vfs_stamping *stamp;
288 struct vfs_stamping *last_stamp = NULL;
290 for (stamp = stamps; stamp != NULL; stamp = stamp->next) {
291 if (stamp->v == v && stamp->id == id){
292 gettimeofday(&(stamp->time), NULL);
293 return;
295 last_stamp = stamp;
297 stamp = g_new (struct vfs_stamping, 1);
298 stamp->v = v;
299 stamp->id = id;
300 if (parent){
301 struct vfs_stamping *st = stamp;
302 while (parent){
303 st->parent = g_new (struct vfs_stamping, 1);
304 *st->parent = *parent;
305 parent = parent->parent;
306 st = st->parent;
308 st->parent = 0;
310 else
311 stamp->parent = 0;
313 gettimeofday (&(stamp->time), NULL);
314 stamp->next = 0;
316 if (stamps) {
317 /* Add to the end */
318 last_stamp->next = stamp;
319 } else {
320 /* Add first element */
321 stamps = stamp;
326 void
327 vfs_stamp (vfs *v, vfsid id)
329 struct vfs_stamping *stamp;
331 for (stamp = stamps; stamp != NULL; stamp = stamp->next)
332 if (stamp->v == v && stamp->id == id){
334 gettimeofday (&(stamp->time), NULL);
335 if (stamp->parent != NULL)
336 vfs_stamp (stamp->parent->v, stamp->parent->id);
338 return;
342 void
343 vfs_rm_parents (struct vfs_stamping *stamp)
345 struct vfs_stamping *parent;
347 while (stamp) {
348 parent = stamp->parent;
349 g_free (stamp);
350 stamp = parent;
354 void
355 vfs_rmstamp (vfs *v, vfsid id, int removeparents)
357 struct vfs_stamping *stamp, *st1;
359 for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
360 if (stamp->v == v && stamp->id == id){
361 if (stamp->parent != NULL){
362 if (removeparents)
363 vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
364 vfs_rm_parents (stamp->parent);
366 if (st1 == NULL){
367 stamps = stamp->next;
368 } else {
369 st1->next = stamp->next;
371 g_free (stamp);
373 return;
377 static int
378 ferrno (vfs *vfs)
380 return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
381 /* Hope that error message is obscure enough ;-) */
385 mc_open (const char *filename, int flags, ...)
387 int handle;
388 int mode;
389 void *info;
390 va_list ap;
392 char *file = vfs_canon (filename);
393 vfs *vfs = vfs_type (file);
395 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
396 va_start (ap, flags);
397 mode = va_arg (ap, int);
398 va_end (ap);
400 if (!vfs->open) {
401 errno = -EOPNOTSUPP;
402 return -1;
405 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
406 g_free (file);
407 if (!info){
408 errno = ferrno (vfs);
409 return -1;
411 handle = get_bucket ();
412 vfs_file_table [handle].fs_info = info;
413 vfs_file_table [handle].operations = vfs;
415 return handle;
418 #define vfs_op(handle) vfs_file_table [handle].operations
419 #define vfs_info(handle) vfs_file_table [handle].fs_info
420 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
422 #define MC_OP(name, inarg, callarg, pre, post) \
423 int mc_##name inarg \
425 vfs *vfs; \
426 int result; \
428 pre \
429 result = vfs->name ? (*vfs->name)callarg : -1; \
430 post \
431 if (result == -1) \
432 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
433 return result; \
436 #define MC_NAMEOP(name, inarg, callarg) \
437 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
438 #define MC_HANDLEOP(name, inarg, callarg) \
439 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, ;)
441 MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) )
444 mc_ctl (int handle, int ctlop, int arg)
446 vfs *vfs = vfs_op (handle);
448 return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
452 mc_setctl (char *path, int ctlop, char *arg)
454 vfs *vfs;
455 int result;
457 if (!path)
458 vfs_die("You don't want to pass NULL to mc_setctl.");
460 path = vfs_canon (path);
461 vfs = vfs_type (path);
462 result = vfs->setctl ? (*vfs->setctl)(vfs, path, ctlop, arg) : 0;
463 g_free (path);
464 return result;
468 mc_close (int handle)
470 vfs *vfs;
471 int result;
473 if (handle == -1 || !vfs_info (handle))
474 return -1;
476 vfs = vfs_op (handle);
477 if (handle < 3)
478 return close (handle);
480 if (!vfs->close)
481 vfs_die ("VFS must support close.\n");
482 result = (*vfs->close)(vfs_info (handle));
483 vfs_free_bucket (handle);
484 if (result == -1)
485 errno = ferrno (vfs);
487 return result;
490 DIR *
491 mc_opendir (char *dirname)
493 int handle, *handlep;
494 void *info;
495 vfs *vfs;
497 dirname = vfs_canon (dirname);
498 vfs = vfs_type (dirname);
500 info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
501 g_free (dirname);
502 if (!info){
503 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
504 return NULL;
506 handle = get_bucket ();
507 vfs_file_table [handle].fs_info = info;
508 vfs_file_table [handle].operations = vfs;
510 handlep = g_new (int, 1);
511 *handlep = handle;
512 return (DIR *) handlep;
515 void
516 mc_seekdir (DIR *dirp, int offset)
518 int handle;
519 vfs *vfs;
521 if (!dirp){
522 errno = EFAULT;
523 return;
525 handle = *(int *) dirp;
526 vfs = vfs_op (handle);
527 if (vfs->seekdir)
528 (*vfs->seekdir) (vfs_info (handle), offset);
529 else
530 errno = E_NOTSUPP;
533 #define MC_DIROP(name, type, onerr ) \
534 type mc_##name (DIR *dirp) \
536 int handle; \
537 vfs *vfs; \
538 type result; \
540 if (!dirp){ \
541 errno = EFAULT; \
542 return onerr; \
544 handle = *(int *) dirp; \
545 vfs = vfs_op (handle); \
546 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
547 if (result == onerr) \
548 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
549 return result; \
552 MC_DIROP (readdir, struct dirent *, NULL)
553 MC_DIROP (telldir, int, -1)
556 mc_closedir (DIR *dirp)
558 int handle = *(int *) dirp;
559 vfs *vfs = vfs_op (handle);
560 int result;
562 result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
563 vfs_free_bucket (handle);
564 g_free (dirp);
565 return result;
568 int mc_stat (const char *filename, struct stat *buf) {
569 vfs *vfs;
570 int result;
571 char *path;
572 path = vfs_canon (filename); vfs = vfs_type (path);
573 result = vfs->stat ? (*vfs->stat) (vfs, path, buf) : -1;
574 g_free (path);
575 if (result == -1)
576 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
577 return result;
580 int mc_lstat (const char *filename, struct stat *buf) {
581 vfs *vfs;
582 int result;
583 char *path;
584 path = vfs_canon (filename); vfs = vfs_type (path);
585 result = vfs->lstat ? (*vfs->lstat) (vfs, path, buf) : -1;
586 g_free (path);
587 if (result == -1)
588 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
589 return result;
592 int mc_fstat (int handle, struct stat *buf) {
593 vfs *vfs;
594 int result;
596 if (handle == -1)
597 return -1;
598 vfs = vfs_op (handle);
599 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
600 if (result == -1)
601 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
602 return result;
606 * You must g_strdup whatever this function returns.
609 static const char *
610 mc_return_cwd (void)
612 char *p;
613 struct stat my_stat, my_stat2;
615 if (!vfs_rosplit (current_dir)){
616 p = g_get_current_dir ();
617 if (!p) /* One of the directories in the path is not readable */
618 return current_dir;
620 /* Otherwise check if it is O.K. to use the current_dir */
621 if (!cd_symlinks ||
622 mc_stat (p, &my_stat) ||
623 mc_stat (current_dir, &my_stat2) ||
624 my_stat.st_ino != my_stat2.st_ino ||
625 my_stat.st_dev != my_stat2.st_dev){
626 g_free (current_dir);
627 current_dir = p;
628 return p;
629 } /* Otherwise we return current_dir below */
630 g_free (p);
632 return current_dir;
635 char *
636 mc_get_current_wd (char *buffer, int size)
638 const char *cwd = mc_return_cwd();
640 strncpy (buffer, cwd, size - 1);
641 buffer [size - 1] = 0;
642 return buffer;
645 MC_NAMEOP (chmod, (char *path, int mode), (vfs, path, mode))
646 MC_NAMEOP (chown, (char *path, int owner, int group), (vfs, path, owner, group))
647 MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs, path, times))
648 MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs, path, buf, bufsiz))
649 MC_NAMEOP (unlink, (char *path), (vfs, path))
650 MC_NAMEOP (symlink, (char *name1, char *path), (vfs, name1, path))
652 #define MC_RENAMEOP(name) \
653 int mc_##name (const char *fname1, const char *fname2) \
655 vfs *vfs; \
656 int result; \
658 char *name2, *name1 = vfs_canon (fname1); \
659 vfs = vfs_type (name1); \
660 name2 = vfs_canon (fname2); \
661 if (vfs != vfs_type (name2)){ \
662 errno = EXDEV; \
663 g_free (name1); \
664 g_free (name2); \
665 return -1; \
668 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
669 g_free (name1); \
670 g_free (name2); \
671 if (result == -1) \
672 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
673 return result; \
676 MC_RENAMEOP (link)
677 MC_RENAMEOP (rename)
679 MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte))
681 off_t mc_lseek (int fd, off_t offset, int whence)
683 vfs *vfs;
684 int result;
686 if (fd == -1)
687 return -1;
689 vfs = vfs_op (fd);
690 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
691 if (result == -1)
692 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
693 return result;
697 * remove //, /./ and /../, local should point to big enough buffer
700 #define ISSLASH(a) (!a || (a == '/'))
702 char *
703 vfs_canon (const char *path)
705 if (!path)
706 vfs_die("Cannot canonicalize NULL");
708 /* Tilde expansion */
709 if (*path == '~'){
710 char *local, *result;
712 local = tilde_expand (path);
713 if (local){
714 result = vfs_canon (local);
715 g_free (local);
716 return result;
720 /* Relative to current directory */
721 if (*path != PATH_SEP){
722 char *local, *result;
724 local = concat_dir_and_file (current_dir, path);
726 result = vfs_canon (local);
727 g_free (local);
728 return result;
732 * So we have path of following form:
733 * /p1/p2#op/.././././p3#op/p4. Good luck.
736 char *result = g_strdup (path);
737 canonicalize_pathname (result);
738 return result;
742 vfsid
743 vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
745 vfsid nvfsid;
747 dir = concat_dir_and_file (dir, "");
749 nvfsid = (*nvfs->getid)(nvfs, dir, par);
751 g_free (dir);
752 return nvfsid;
755 static int
756 is_parent (vfs * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
758 struct vfs_stamping *stamp;
760 for (stamp = parent; stamp; stamp = stamp->parent)
761 if (stamp->v == nvfs && stamp->id == nvfsid)
762 break;
764 return (stamp ? 1 : 0);
767 void
768 vfs_add_noncurrent_stamps (vfs * oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
770 vfs *nvfs, *n2vfs, *n3vfs;
771 vfsid nvfsid, n2vfsid, n3vfsid;
772 struct vfs_stamping *par, *stamp;
773 int f;
775 /* FIXME: As soon as we convert to multiple panels, this stuff
776 has to change. It works like this: We do not time out the
777 vfs's which are current in any panel and on the other
778 side we add the old directory with all its parents which
779 are not in any panel (if we find such one, we stop adding
780 parents to the time-outing structure. */
782 /* There are three directories we have to take care of: current_dir,
783 cpanel->cwd and opanel->cwd. Athough most of the time either
784 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
785 same, it's possible that all three are different -- Norbert */
787 if (!cpanel)
788 return;
790 nvfs = vfs_type (current_dir);
791 nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
792 vfs_rmstamp (nvfs, nvfsid, 1);
794 f = is_parent (oldvfs, oldvfsid, par);
795 vfs_rm_parents (par);
796 if ((nvfs == oldvfs && nvfsid == oldvfsid) || oldvfsid == (vfsid *)-1 || f){
797 return;
800 if (get_current_type () == view_listing){
801 n2vfs = vfs_type (cpanel->cwd);
802 n2vfsid = vfs_ncs_getid (n2vfs, cpanel->cwd, &par);
803 f = is_parent (oldvfs, oldvfsid, par);
804 vfs_rm_parents (par);
805 if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
806 return;
807 } else {
808 n2vfs = (vfs *) -1;
809 n2vfsid = (vfs *) -1;
812 if (get_other_type () == view_listing){
813 n3vfs = vfs_type (opanel->cwd);
814 n3vfsid = vfs_ncs_getid (n3vfs, opanel->cwd, &par);
815 f = is_parent (oldvfs, oldvfsid, par);
816 vfs_rm_parents (par);
817 if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
818 return;
819 } else {
820 n3vfs = (vfs *)-1;
821 n3vfsid = (vfs *)-1;
824 if ((*oldvfs->nothingisopen) (oldvfsid)){
825 if (oldvfs == &vfs_extfs_ops && ((extfs_archive *) oldvfsid)->name == 0){
826 /* Free the resources immediatly when we leave a mtools fs
827 ('cd a:') instead of waiting for the vfs-timeout */
828 (oldvfs->free) (oldvfsid);
829 } else
830 vfs_addstamp (oldvfs, oldvfsid, parent);
831 for (stamp = parent; stamp != NULL; stamp = stamp->parent){
832 if ((stamp->v == nvfs && stamp->id == nvfsid) ||
833 (stamp->v == n2vfs && stamp->id == n2vfsid) ||
834 (stamp->v == n3vfs && stamp->id == n3vfsid) ||
835 stamp->id == (vfsid) - 1 ||
836 !(*stamp->v->nothingisopen) (stamp->id))
837 break;
838 if (stamp->v == &vfs_extfs_ops && ((extfs_archive *) stamp->id)->name == 0){
839 (stamp->v->free) (stamp->id);
840 vfs_rmstamp (stamp->v, stamp->id, 0);
841 } else
842 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
847 static void
848 vfs_stamp_path (char *path)
850 vfs *vfs;
851 vfsid id;
852 struct vfs_stamping *par, *stamp;
854 vfs = vfs_type (path);
855 id = vfs_ncs_getid (vfs, path, &par);
856 vfs_addstamp (vfs, id, par);
858 for (stamp = par; stamp != NULL; stamp = stamp->parent)
859 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
860 vfs_rm_parents (par);
863 void
864 vfs_add_current_stamps (void)
866 vfs_stamp_path (current_dir);
868 if (cpanel) {
869 if (get_current_type () == view_listing)
870 vfs_stamp_path (cpanel->cwd);
873 if (opanel) {
874 if (get_other_type () == view_listing)
875 vfs_stamp_path (opanel->cwd);
880 * VFS chdir.
881 * Return 0 on success, -1 on failure.
884 mc_chdir (char *path)
886 char *new_dir, *new_dir_copy;
887 vfs *old_vfs, *new_vfs;
888 vfsid old_vfsid;
889 struct vfs_stamping *parent;
890 int result;
892 new_dir = vfs_canon (path);
893 new_vfs = vfs_type (new_dir);
894 if (!new_vfs->chdir)
895 vfs_die ("No chdir function defined");
897 /* new_vfs->chdir can write to the second argument, use a copy */
898 new_dir_copy = g_strdup (new_dir);
899 result = (*new_vfs->chdir) (new_vfs, new_dir_copy);
900 g_free (new_dir_copy);
902 if (result == -1) {
903 errno = ferrno (new_vfs);
904 g_free (new_dir);
905 return -1;
908 old_vfsid = vfs_ncs_getid (current_vfs, current_dir, &parent);
909 old_vfs = current_vfs;
911 /* Actually change directory */
912 g_free (current_dir);
913 current_dir = new_dir;
914 current_vfs = new_vfs;
916 /* This function uses the new current_dir implicitly */
917 vfs_add_noncurrent_stamps (old_vfs, old_vfsid, parent);
918 vfs_rm_parents (parent);
920 /* Sometimes we assume no trailing slash on cwd */
921 if (*current_dir) {
922 char *p;
923 p = strchr (current_dir, 0) - 1;
924 if (*p == PATH_SEP && p > current_dir)
925 *p = 0;
928 return 0;
932 vfs_current_is_local (void)
934 return current_vfs == &vfs_local_ops;
938 vfs_file_is_local (const char *file)
940 char *filename = vfs_canon (file);
941 vfs *vfs = vfs_type (filename);
943 g_free (filename);
944 return vfs == &vfs_local_ops;
948 vfs_file_is_ftp (char *filename)
950 #ifdef USE_NETCODE
951 vfs *vfs;
953 filename = vfs_canon (filename);
954 vfs = vfs_type (filename);
955 g_free (filename);
956 return vfs == &vfs_ftpfs_ops;
957 #else
958 return 0;
959 #endif
963 vfs_file_is_smb (char *filename)
965 #ifdef WITH_SMBFS
966 #ifdef USE_NETCODE
967 vfs *vfs;
969 filename = vfs_canon (filename);
970 vfs = vfs_type (filename);
971 g_free (filename);
972 return vfs == &vfs_smbfs_ops;
973 #endif /* USE_NETCODE */
974 #endif /* WITH_SMBFS */
975 return 0;
978 char *vfs_get_current_dir (void)
980 return current_dir;
983 static void vfs_setup_wd (void)
985 current_dir = g_strdup (PATH_SEP_STR);
986 if (!(vfs_flags & FL_NO_CWDSETUP))
987 mc_return_cwd();
989 if (strlen(current_dir)>MC_MAXPATHLEN-2)
990 vfs_die ("Current dir too long.\n");
993 MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs, path, mode))
994 MC_NAMEOP (rmdir, (char *path), (vfs, path))
995 MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs, path, mode, dev))
997 #ifdef HAVE_MMAP
998 static struct mc_mmapping {
999 caddr_t addr;
1000 void *vfs_info;
1001 vfs *vfs;
1002 struct mc_mmapping *next;
1003 } *mc_mmaparray = NULL;
1005 caddr_t
1006 mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
1008 vfs *vfs;
1009 caddr_t result;
1010 struct mc_mmapping *mcm;
1012 if (fd == -1)
1013 return (caddr_t) -1;
1015 vfs = vfs_op (fd);
1016 result = vfs->mmap ? (*vfs->mmap)(vfs, addr, len, prot, flags, vfs_info (fd), offset) : (caddr_t)-1;
1017 if (result == (caddr_t)-1){
1018 errno = ferrno (vfs);
1019 return (caddr_t)-1;
1021 mcm =g_new (struct mc_mmapping, 1);
1022 mcm->addr = result;
1023 mcm->vfs_info = vfs_info (fd);
1024 mcm->vfs = vfs;
1025 mcm->next = mc_mmaparray;
1026 mc_mmaparray = mcm;
1027 return result;
1031 mc_munmap (caddr_t addr, size_t len)
1033 struct mc_mmapping *mcm, *mcm2 = NULL;
1035 for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
1036 if (mcm->addr == addr){
1037 if (mcm2 == NULL)
1038 mc_mmaparray = mcm->next;
1039 else
1040 mcm2->next = mcm->next;
1041 if (mcm->vfs->munmap)
1042 (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
1043 g_free (mcm);
1044 return 0;
1047 return -1;
1050 #endif
1052 char *
1053 mc_def_getlocalcopy (vfs *vfs, char *filename)
1055 char *tmp;
1056 int fdin, fdout, i;
1057 char buffer[8192];
1058 struct stat mystat;
1059 char *ext = NULL;
1060 char *ptr;
1062 fdin = mc_open (filename, O_RDONLY);
1063 if (fdin == -1)
1064 return NULL;
1066 /* Try to preserve existing extension */
1067 for (ptr = filename + strlen(filename) - 1; ptr >= filename; ptr--) {
1068 if (*ptr == '.') {
1069 ext = ptr;
1070 break;
1073 if (!isalnum((unsigned char) *ptr))
1074 break;
1077 fdout = mc_mkstemps (&tmp, "mclocalcopy", ext);
1078 if (fdout == -1)
1079 goto fail;
1080 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0){
1081 if (write (fdout, buffer, i) != i)
1082 goto fail;
1084 if (i == -1)
1085 goto fail;
1086 i = mc_close (fdin);
1087 fdin = -1;
1088 if (i==-1)
1089 goto fail;
1090 if (close (fdout)==-1)
1091 goto fail;
1093 if (mc_stat (filename, &mystat) != -1){
1094 chmod (tmp, mystat.st_mode);
1096 return tmp;
1098 fail:
1099 if (fdout) close(fdout);
1100 if (fdin) mc_close (fdin);
1101 g_free (tmp);
1102 return NULL;
1105 char *
1106 mc_getlocalcopy (const char *pathname)
1108 char *result;
1109 char *path = vfs_canon (pathname);
1110 vfs *vfs = vfs_type (path);
1112 result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, path) :
1113 mc_def_getlocalcopy (vfs, path);
1114 g_free (path);
1115 if (!result)
1116 errno = ferrno (vfs);
1117 return result;
1121 mc_def_ungetlocalcopy (vfs *vfs, char *filename, char *local, int has_changed)
1122 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1123 int fdin = -1, fdout = -1, i;
1124 if (has_changed){
1125 char buffer [8192];
1127 fdin = open (local, O_RDONLY);
1128 if (fdin == -1)
1129 goto failed;
1130 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1131 if (fdout == -1)
1132 goto failed;
1133 while ((i = read (fdin, buffer, sizeof (buffer))) > 0){
1134 if (mc_write (fdout, buffer, i) != i)
1135 goto failed;
1137 if (i == -1)
1138 goto failed;
1140 if (close (fdin)==-1) {
1141 fdin = -1;
1142 goto failed;
1144 fdin = -1;
1145 if (mc_close (fdout)==-1) {
1146 fdout = -1;
1147 goto failed;
1150 unlink (local);
1151 g_free (local);
1152 return 0;
1154 failed:
1155 message_1s (1, _("Changes to file lost"), filename);
1156 if (fdout!=-1) mc_close(fdout);
1157 if (fdin!=-1) close(fdin);
1158 unlink (local);
1159 g_free (local);
1160 return -1;
1164 mc_ungetlocalcopy (const char *pathname, char *local, int has_changed)
1166 int return_value = 0;
1167 char *path = vfs_canon (pathname);
1168 vfs *vfs = vfs_type (path);
1170 return_value = vfs->ungetlocalcopy ?
1171 (*vfs->ungetlocalcopy)(vfs, path, local, has_changed) :
1172 mc_def_ungetlocalcopy (vfs, path, local, has_changed);
1173 g_free (path);
1174 return return_value;
1178 * Hmm, as timeout is minute or so, do we need to care about usecs?
1180 static inline int
1181 timeoutcmp (struct timeval *t1, struct timeval *t2)
1183 return ((t1->tv_sec < t2->tv_sec)
1184 || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
1187 /* This is called from timeout handler with now = 0, or can be called
1188 with now = 1 to force freeing all filesystems that are not in use */
1190 void
1191 vfs_expire (int now)
1193 static int locked = 0;
1194 struct timeval time;
1195 struct vfs_stamping *stamp, *st;
1197 /* Avoid recursive invocation, e.g. when one of the free functions
1198 calls message_1s */
1199 if (locked)
1200 return;
1201 locked = 1;
1203 gettimeofday (&time, NULL);
1204 time.tv_sec -= vfs_timeout;
1206 for (stamp = stamps; stamp != NULL;){
1207 if (now || (timeoutcmp (&stamp->time, &time))){
1208 st = stamp->next;
1209 (*stamp->v->free) (stamp->id);
1210 vfs_rmstamp (stamp->v, stamp->id, 0);
1211 stamp = st;
1212 } else
1213 stamp = stamp->next;
1215 locked = 0;
1218 void
1219 vfs_timeout_handler (void)
1221 vfs_expire (0);
1224 void
1225 vfs_init (void)
1227 time_t current_time;
1228 struct tm *t;
1230 memset (vfs_file_table, 0, sizeof (vfs_file_table));
1231 current_time = time (NULL);
1232 t = localtime (&current_time);
1233 current_mday = t->tm_mday;
1234 current_mon = t->tm_mon;
1235 current_year = t->tm_year;
1237 /* We do not want to register vfs_local_ops */
1239 #ifdef USE_NETCODE
1240 tcp_init();
1241 vfs_register (&vfs_ftpfs_ops);
1242 vfs_register (&vfs_fish_ops);
1243 #ifdef WITH_SMBFS
1244 vfs_register (&vfs_smbfs_ops);
1245 #endif /* WITH_SMBFS */
1246 #ifdef WITH_MCFS
1247 vfs_register (&vfs_mcfs_ops);
1248 #endif /* WITH_SMBFS */
1249 #endif /* USE_NETCODE */
1251 vfs_register (&vfs_extfs_ops);
1252 vfs_register (&vfs_sfs_ops);
1253 vfs_register (&vfs_tarfs_ops);
1254 vfs_register (&vfs_cpiofs_ops);
1256 #ifdef USE_EXT2FSLIB
1257 vfs_register (&vfs_undelfs_ops);
1258 #endif /* USE_EXT2FSLIB */
1260 vfs_setup_wd ();
1263 void
1264 vfs_shut (void)
1266 struct vfs_stamping *stamp, *st;
1267 vfs *vfs;
1269 for (stamp = stamps, stamps = 0; stamp != NULL;){
1270 (*stamp->v->free)(stamp->id);
1271 st = stamp->next;
1272 g_free (stamp);
1273 stamp = st;
1276 if (stamps)
1277 vfs_rmstamp (stamps->v, stamps->id, 1);
1279 if (current_dir)
1280 g_free (current_dir);
1282 for (vfs=vfs_list; vfs; vfs=vfs->next)
1283 if (vfs->done)
1284 (*vfs->done) (vfs);
1288 * These ones grab information from the VFS
1289 * and handles them to an upper layer
1291 void
1292 vfs_fill_names (void (*func)(char *))
1294 vfs *vfs;
1296 for (vfs=vfs_list; vfs; vfs=vfs->next)
1297 if (vfs->fill_names)
1298 (*vfs->fill_names) (vfs, func);
1301 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1302 #define MAXCOLS 30
1304 static char *columns [MAXCOLS]; /* Points to the string in column n */
1305 static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
1308 vfs_split_text (char *p)
1310 char *original = p;
1311 int numcols;
1313 memset (columns, 0, sizeof (columns));
1315 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
1316 while (*p == ' ' || *p == '\r' || *p == '\n'){
1317 *p = 0;
1318 p++;
1320 columns [numcols] = p;
1321 column_ptr [numcols] = p - original;
1322 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
1323 p++;
1325 return numcols;
1328 static int
1329 is_num (int idx)
1331 char *column = columns[idx];
1333 if (!column || column[0] < '0' || column[0] > '9')
1334 return 0;
1336 return 1;
1339 /* Return 1 for MM-DD-YY and MM-DD-YYYY */
1340 static int
1341 is_dos_date (const char *str)
1343 int len;
1345 if (!str)
1346 return 0;
1348 len = strlen (str);
1349 if (len != 8 && len != 10)
1350 return 0;
1352 if (str[2] != str[5])
1353 return 0;
1355 if (!strchr ("\\-/", (int) str[2]))
1356 return 0;
1358 return 1;
1361 static int
1362 is_week (const char *str, struct tm *tim)
1364 static const char *week = "SunMonTueWedThuFriSat";
1365 char *pos;
1367 if (!str)
1368 return 0;
1370 if ((pos = strstr (week, str)) != NULL) {
1371 if (tim != NULL)
1372 tim->tm_wday = (pos - week) / 3;
1373 return 1;
1375 return 0;
1378 static int
1379 is_month (const char *str, struct tm *tim)
1381 static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
1382 char *pos;
1384 if (!str)
1385 return 0;
1387 if ((pos = strstr (month, str)) != NULL) {
1388 if (tim != NULL)
1389 tim->tm_mon = (pos - month) / 3;
1390 return 1;
1392 return 0;
1396 * Check for possible locale's abbreviated month name (Jan..Dec).
1397 * Any 3 bytes long string without digit and control characters.
1398 * isalpha() is locale specific, so it cannot be used if current
1399 * locale is "C" and ftp server use Cyrillic.
1400 * TODO: Punctuation characters also cannot be part of month name.
1401 * NB: It is assumed there are no whitespaces in month.
1403 static int
1404 is_localized_month (const unsigned char *month)
1406 int i = 0;
1407 while ((i < 3) && *month && !isdigit (*month) && !iscntrl (*month)) {
1408 i++;
1409 month++;
1411 return ((i == 3) && (*month == 0));
1414 static int
1415 is_time (const char *str, struct tm *tim)
1417 char *p, *p2;
1419 if (!str)
1420 return 0;
1422 if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
1423 if (p != p2) {
1424 if (sscanf
1425 (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min,
1426 &tim->tm_sec) != 3)
1427 return 0;
1428 } else {
1429 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
1430 return 0;
1432 } else
1433 return 0;
1435 return 1;
1438 static int is_year (char *str, struct tm *tim)
1440 long year;
1442 if (!str)
1443 return 0;
1445 if (strchr (str, ':'))
1446 return 0;
1448 if (strlen (str) != 4)
1449 return 0;
1451 if (sscanf (str, "%ld", &year) != 1)
1452 return 0;
1454 if (year < 1900 || year > 3000)
1455 return 0;
1457 tim->tm_year = (int) (year - 1900);
1459 return 1;
1463 * FIXME: this is broken. Consider following entry:
1464 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1465 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1469 vfs_parse_filetype (char c)
1471 switch (c) {
1472 case 'd': return S_IFDIR;
1473 case 'b': return S_IFBLK;
1474 case 'c': return S_IFCHR;
1475 case 'l': return S_IFLNK;
1476 case 's': /* Socket */
1477 #ifdef S_IFSOCK
1478 return S_IFSOCK;
1479 #else
1480 /* If not supported, we fall through to IFIFO */
1481 return S_IFIFO;
1482 #endif
1483 case 'D': /* Solaris door */
1484 #ifdef S_IFDOOR
1485 return S_IFDOOR;
1486 #else
1487 return S_IFIFO;
1488 #endif
1489 case 'p': return S_IFIFO;
1490 case 'm': case 'n': /* Don't know what these are :-) */
1491 case '-': case '?': return S_IFREG;
1492 default: return -1;
1496 int vfs_parse_filemode (const char *p)
1497 { /* converts rw-rw-rw- into 0666 */
1498 int res = 0;
1499 switch (*(p++)){
1500 case 'r': res |= 0400; break;
1501 case '-': break;
1502 default: return -1;
1504 switch (*(p++)){
1505 case 'w': res |= 0200; break;
1506 case '-': break;
1507 default: return -1;
1509 switch (*(p++)){
1510 case 'x': res |= 0100; break;
1511 case 's': res |= 0100 | S_ISUID; break;
1512 case 'S': res |= S_ISUID; break;
1513 case '-': break;
1514 default: return -1;
1516 switch (*(p++)){
1517 case 'r': res |= 0040; break;
1518 case '-': break;
1519 default: return -1;
1521 switch (*(p++)){
1522 case 'w': res |= 0020; break;
1523 case '-': break;
1524 default: return -1;
1526 switch (*(p++)){
1527 case 'x': res |= 0010; break;
1528 case 's': res |= 0010 | S_ISGID; break;
1529 case 'l': /* Solaris produces these */
1530 case 'S': res |= S_ISGID; break;
1531 case '-': break;
1532 default: return -1;
1534 switch (*(p++)){
1535 case 'r': res |= 0004; break;
1536 case '-': break;
1537 default: return -1;
1539 switch (*(p++)){
1540 case 'w': res |= 0002; break;
1541 case '-': break;
1542 default: return -1;
1544 switch (*(p++)){
1545 case 'x': res |= 0001; break;
1546 case 't': res |= 0001 | S_ISVTX; break;
1547 case 'T': res |= S_ISVTX; break;
1548 case '-': break;
1549 default: return -1;
1551 return res;
1554 /* This function parses from idx in the columns[] array */
1556 vfs_parse_filedate (int idx, time_t *t)
1558 char *p;
1559 struct tm tim;
1560 int d[3];
1561 int got_year = 0;
1562 int l10n = 0; /* Locale's abbreviated month name */
1564 /* Let's setup default time values */
1565 tim.tm_year = current_year;
1566 tim.tm_mon = current_mon;
1567 tim.tm_mday = current_mday;
1568 tim.tm_hour = 0;
1569 tim.tm_min = 0;
1570 tim.tm_sec = 0;
1571 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
1573 p = columns[idx++];
1575 /* We eat weekday name in case of extfs */
1576 if (is_week (p, &tim))
1577 p = columns[idx++];
1579 /* Month name */
1580 if (is_month (p, &tim)) {
1581 /* And we expect, it followed by day number */
1582 if (is_num (idx))
1583 tim.tm_mday = (int) atol (columns[idx++]);
1584 else
1585 return 0; /* No day */
1587 } else {
1588 /* We usually expect:
1589 Mon DD hh:mm
1590 Mon DD YYYY
1591 But in case of extfs we allow these date formats:
1592 Mon DD YYYY hh:mm
1593 Mon DD hh:mm YYYY
1594 Wek Mon DD hh:mm:ss YYYY
1595 MM-DD-YY hh:mm
1596 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1597 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1599 /* Special case with MM-DD-YY or MM-DD-YYYY */
1600 if (is_dos_date (p)) {
1601 p[2] = p[5] = '-';
1603 if (sscanf (p, "%2d-%2d-%d", &d[0], &d[1], &d[2]) == 3) {
1604 /* Months are zero based */
1605 if (d[0] > 0)
1606 d[0]--;
1608 if (d[2] > 1900) {
1609 d[2] -= 1900;
1610 } else {
1611 /* Y2K madness */
1612 if (d[2] < 70)
1613 d[2] += 100;
1616 tim.tm_mon = d[0];
1617 tim.tm_mday = d[1];
1618 tim.tm_year = d[2];
1619 got_year = 1;
1620 } else
1621 return 0; /* sscanf failed */
1622 } else {
1623 /* Locale's abbreviated month name followed by day number */
1624 if (is_localized_month (p) && (is_num (idx++)))
1625 l10n = 1;
1626 else
1627 return 0; /* unsupported format */
1631 /* Here we expect to find time and/or year */
1633 if (is_num (idx)) {
1634 if (is_time (columns[idx], &tim)
1635 || (got_year = is_year (columns[idx], &tim))) {
1636 idx++;
1638 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1639 if (is_num (idx) && (columns[idx + 1][0])) {
1640 if (got_year) {
1641 if (is_time (columns[idx], &tim))
1642 idx++; /* time also */
1643 } else {
1644 if ((got_year = is_year (columns[idx], &tim)))
1645 idx++; /* year also */
1648 } /* only time or date */
1649 } else
1650 return 0; /* Nor time or date */
1653 * If the date is less than 6 months in the past, it is shown without year
1654 * other dates in the past or future are shown with year but without time
1655 * This does not check for years before 1900 ... I don't know, how
1656 * to represent them at all
1658 if (!got_year && current_mon < 6 && current_mon < tim.tm_mon
1659 && tim.tm_mon - current_mon >= 6)
1661 tim.tm_year--;
1663 if (l10n || (*t = mktime (&tim)) < 0)
1664 *t = 0;
1665 return idx;
1669 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
1671 int idx, idx2, num_cols;
1672 int i;
1673 char *p_copy = NULL;
1674 char *t = NULL;
1675 const char *line = p;
1677 if (strncmp (p, "total", 5) == 0)
1678 return 0;
1680 if ((i = vfs_parse_filetype(*(p++))) == -1)
1681 goto error;
1683 s->st_mode = i;
1684 if (*p == ' ') /* Notwell 4 */
1685 p++;
1686 if (*p == '['){
1687 if (strlen (p) <= 8 || p [8] != ']')
1688 goto error;
1689 /* Should parse here the Notwell permissions :) */
1690 if (S_ISDIR (s->st_mode))
1691 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
1692 else
1693 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
1694 p += 9;
1695 } else {
1696 if ((i = vfs_parse_filemode(p)) == -1)
1697 goto error;
1698 s->st_mode |= i;
1699 p += 9;
1701 /* This is for an extra ACL attribute (HP-UX) */
1702 if (*p == '+')
1703 p++;
1706 p_copy = g_strdup(p);
1707 num_cols = vfs_split_text (p_copy);
1709 s->st_nlink = atol (columns [0]);
1710 if (s->st_nlink <= 0)
1711 goto error;
1713 if (!is_num (1))
1714 s->st_uid = finduid (columns [1]);
1715 else
1716 s->st_uid = (uid_t) atol (columns [1]);
1718 /* Mhm, the ls -lg did not produce a group field */
1719 for (idx = 3; idx <= 5; idx++)
1720 if (is_month (columns[idx], NULL) || is_week (columns[idx], NULL)
1721 || is_dos_date (columns[idx]) || is_localized_month (columns[idx]))
1722 break;
1724 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
1725 goto error;
1727 /* We don't have gid */
1728 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
1729 idx2 = 2;
1730 else {
1731 /* We have gid field */
1732 if (is_num (2))
1733 s->st_gid = (gid_t) atol (columns [2]);
1734 else
1735 s->st_gid = findgid (columns [2]);
1736 idx2 = 3;
1739 /* This is device */
1740 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
1741 int maj, min;
1743 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
1744 goto error;
1746 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
1747 goto error;
1749 #ifdef HAVE_ST_RDEV
1750 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
1751 #endif
1752 s->st_size = 0;
1754 } else {
1755 /* Common file size */
1756 if (!is_num (idx2))
1757 goto error;
1759 s->st_size = (size_t) atol (columns [idx2]);
1760 #ifdef HAVE_ST_RDEV
1761 s->st_rdev = 0;
1762 #endif
1765 idx = vfs_parse_filedate (idx, &s->st_mtime);
1766 if (!idx)
1767 goto error;
1768 /* Use resulting time value */
1769 s->st_atime = s->st_ctime = s->st_mtime;
1770 /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
1771 #ifdef HAVE_ST_BLKSIZE
1772 s->st_blksize = 512;
1773 #endif
1774 #ifdef HAVE_ST_BLOCKS
1775 s->st_blocks = (s->st_size + 511) / 512;
1776 #endif
1778 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
1779 if (strcmp (columns [i], "->") == 0){
1780 idx2 = i;
1781 break;
1784 if (((S_ISLNK (s->st_mode) ||
1785 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
1786 && idx2){
1788 if (filename){
1789 *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
1791 if (linkname){
1792 t = g_strdup (p + column_ptr [idx2+1]);
1793 *linkname = t;
1795 } else {
1796 /* Extract the filename from the string copy, not from the columns
1797 * this way we have a chance of entering hidden directories like ". ."
1799 if (filename){
1801 * filename = g_strdup (columns [idx++]);
1804 t = g_strdup (p + column_ptr [idx]);
1805 *filename = t;
1807 if (linkname)
1808 *linkname = NULL;
1811 if (t) {
1812 int p = strlen (t);
1813 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1814 t [p] = 0;
1815 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1816 t [p] = 0;
1819 g_free (p_copy);
1820 return 1;
1822 error:
1824 static int errorcount = 0;
1826 if (++errorcount < 5) {
1827 message_1s (1, _("Cannot parse:"), (p_copy && *p_copy) ? p_copy : line);
1828 } else if (errorcount == 5)
1829 message_1s (1, _("Error"), _("More parsing errors will be ignored."));
1832 g_free (p_copy);
1833 return 0;
1836 void
1837 vfs_die (const char *m)
1839 message_1s (1, _("Internal error:"), m);
1840 exit (1);
1843 void
1844 vfs_print_stats (const char *fs_name, const char *action, const char *file_name, off_t have, off_t need)
1846 static char *i18n_percent_transf_format = NULL, *i18n_transf_format = NULL;
1848 if (i18n_percent_transf_format == NULL) {
1849 i18n_percent_transf_format = _("%s: %s: %s %3d%% (%lu bytes transferred)");
1850 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
1853 if (need)
1854 print_vfs_message (i18n_percent_transf_format, fs_name, action,
1855 file_name, (int)((double)have*100/need), (unsigned long) have);
1856 else
1857 print_vfs_message (i18n_transf_format,
1858 fs_name, action, file_name, (unsigned long) have);
1861 char *
1862 vfs_get_password (char *msg)
1864 return (char *) input_dialog (msg, _("Password:"), INPUT_PASSWORD);
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);