*** empty log message ***
[midnight-commander.git] / vfs / vfs.c
blobfe01c1602dbdb17c15bd5f635c2c1f240da1c2fa
1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
5 1995 Jakub Jelinek
6 1998 Pavel Machek
8 $Id$
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* Warning: funtions like extfs_lstat() have right to destroy any
25 * strings you pass to them. This is acutally ok as you g_strdup what
26 * you are passing to them, anyway; still, beware. */
28 /* Namespace: exports *many* functions with vfs_ prefix; exports
29 parse_ls_lga and friends which do not have that prefix. */
31 #include <config.h>
33 #ifndef NO_SYSLOG_H
34 # include <syslog.h>
35 #endif
37 #include <stdio.h>
38 #include <stdlib.h> /* For atol() */
39 #include <stdarg.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <sys/types.h>
43 #include <fcntl.h>
44 #include <signal.h>
46 #include "utilvfs.h"
48 #include "../src/dir.h"
49 #include "../src/main.h"
50 #ifndef VFS_STANDALONE
51 #include "../src/panel.h"
52 #include "../src/key.h" /* Required for the async alarm handler */
53 #include "../src/layout.h" /* For get_panel_widget and get_other_index */
54 #include "../src/dialog.h"
55 #endif
57 #include "xdirentry.h"
58 #include "vfs.h"
59 #include "extfs.h" /* FIXME: we should not know anything about our modules */
60 #include "names.h"
61 #ifdef USE_NETCODE
62 # include "tcputil.h"
63 #endif
65 #ifdef VFS_STANDALONE
66 #undef WITH_SMBFS
67 #endif
69 extern int get_other_type (void);
71 int vfs_timeout = 60; /* VFS timeout in seconds */
72 int vfs_flags = 0; /* Flags */
74 extern int cd_symlinks; /* Defined in main.c */
76 /* They keep track of the current directory */
77 static vfs *current_vfs = &vfs_local_ops;
78 static char *current_dir = NULL;
81 * FIXME: this is broken. It depends on mc not crossing border on month!
83 static int current_mday;
84 static int current_mon;
85 static int current_year;
87 uid_t vfs_uid = 0;
88 gid_t vfs_gid = 0;
90 /* FIXME: Open files managed by the vfs layer, should be dynamical */
91 #define MAX_VFS_FILES 100
93 static struct {
94 void *fs_info;
95 vfs *operations;
96 } vfs_file_table [MAX_VFS_FILES];
98 static int
99 get_bucket (void)
101 int i;
103 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
104 for (i = 3; i < MAX_VFS_FILES; i++){
105 if (!vfs_file_table [i].fs_info)
106 return i;
109 vfs_die ("No more virtual file handles");
110 return 0;
113 /* vfs_local_ops needs to be the first one */
114 static vfs *vfs_list = &vfs_local_ops;
116 static int
117 vfs_register (vfs *vfs)
119 if (!vfs)
120 vfs_die("You can not register NULL.");
122 if (vfs->init) /* vfs has own initialization function */
123 if (!(*vfs->init)(vfs)) /* but it failed */
124 return 0;
126 vfs->next = vfs_list;
127 vfs_list = vfs;
129 return 1;
132 static vfs *
133 vfs_type_from_op (char *path)
135 vfs *vfs;
137 if (!path)
138 vfs_die ("vfs_type_from_op got NULL: impossible");
140 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
141 if (vfs->which) {
142 if ((*vfs->which) (vfs, path) == -1)
143 continue;
144 return vfs;
146 if (!strncmp (path, vfs->prefix, strlen (vfs->prefix)))
147 return vfs;
149 return NULL; /* shut up stupid gcc */
152 /* Strip known vfs suffixes from a filename (possible improvement: strip
153 suffix from last path component).
154 Returns a malloced string which has to be freed. */
155 char *
156 vfs_strip_suffix_from_filename (const char *filename)
158 vfs *vfs;
159 char *semi;
160 char *p;
162 if (!filename)
163 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
165 p = g_strdup (filename);
166 if (!(semi = strrchr (p, '#')))
167 return p;
169 for (vfs = vfs_list; vfs != &vfs_local_ops; vfs = vfs->next){
170 if (vfs->which){
171 if ((*vfs->which) (vfs, semi + 1) == -1)
172 continue;
173 *semi = '\0'; /* Found valid suffix */
174 return p;
176 if (!strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix))) {
177 *semi = '\0'; /* Found valid suffix */
178 return p;
181 return p;
184 static int
185 path_magic (const char *path)
187 struct stat buf;
189 if (vfs_flags & FL_ALWAYS_MAGIC)
190 return 1;
192 if (!stat(path, &buf))
193 return 0;
195 return 1;
199 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
200 * What is left in path is p1. You still want to g_free(path), you DON'T
201 * want to free neither *inpath nor *op
203 vfs *
204 vfs_split (char *path, char **inpath, char **op)
206 char *semi;
207 char *slash;
208 vfs *ret;
210 if (!path)
211 vfs_die("Can not split NULL");
213 semi = strrchr (path, '#');
214 if (!semi || !path_magic(path))
215 return NULL;
217 slash = strchr (semi, PATH_SEP);
218 *semi = 0;
220 if (op)
221 *op = NULL;
223 if (inpath)
224 *inpath = NULL;
226 if (slash)
227 *slash = 0;
229 if ((ret = vfs_type_from_op (semi+1))){
230 if (op)
231 *op = semi + 1;
232 if (inpath)
233 *inpath = slash ? slash + 1 : NULL;
234 return ret;
238 if (slash)
239 *slash = PATH_SEP;
240 ret = vfs_split (path, inpath, op);
241 *semi = '#';
242 return ret;
245 static vfs *
246 vfs_rosplit (char *path)
248 char *semi;
249 char *slash;
250 vfs *ret;
252 g_return_val_if_fail(path, NULL);
254 semi = strrchr (path, '#');
255 if (!semi || !path_magic (path))
256 return NULL;
258 slash = strchr (semi, PATH_SEP);
259 *semi = 0;
260 if (slash)
261 *slash = 0;
263 ret = vfs_type_from_op (semi+1);
264 if (!ret && (vfs_flags & FL_NO_LOCALHASH))
265 return &vfs_nil_ops;
267 if (slash)
268 *slash = PATH_SEP;
269 if (!ret)
270 ret = vfs_rosplit (path);
272 *semi = '#';
273 return ret;
276 vfs *
277 vfs_type (char *path)
279 vfs *vfs;
281 vfs = vfs_rosplit(path);
283 if (!vfs)
284 vfs = &vfs_local_ops;
286 return vfs;
289 static struct vfs_stamping *stamps;
292 * Returns the number of seconds remaining to the vfs timeout
294 * FIXME: currently this is set to 10 seconds. We should compute this.
297 vfs_timeouts ()
299 return stamps ? 10 : 0;
302 void
303 vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
305 if (v != &vfs_local_ops && id != (vfsid)-1){
306 struct vfs_stamping *stamp;
307 struct vfs_stamping *last_stamp = NULL;
309 for (stamp = stamps; stamp != NULL; stamp = stamp->next) {
310 if (stamp->v == v && stamp->id == id){
311 gettimeofday(&(stamp->time), NULL);
312 return;
314 last_stamp = stamp;
316 stamp = g_new (struct vfs_stamping, 1);
317 stamp->v = v;
318 stamp->id = id;
319 if (parent){
320 struct vfs_stamping *st = stamp;
321 while (parent){
322 st->parent = g_new (struct vfs_stamping, 1);
323 *st->parent = *parent;
324 parent = parent->parent;
325 st = st->parent;
327 st->parent = 0;
329 else
330 stamp->parent = 0;
332 gettimeofday (&(stamp->time), NULL);
333 stamp->next = 0;
335 if (stamps) {
336 /* Add to the end */
337 last_stamp->next = stamp;
338 } else {
339 /* Add first element */
340 stamps = stamp;
345 void
346 vfs_stamp (vfs *v, vfsid id)
348 struct vfs_stamping *stamp;
350 for (stamp = stamps; stamp != NULL; stamp = stamp->next)
351 if (stamp->v == v && stamp->id == id){
353 gettimeofday (&(stamp->time), NULL);
354 if (stamp->parent != NULL)
355 vfs_stamp (stamp->parent->v, stamp->parent->id);
357 return;
361 void
362 vfs_rm_parents (struct vfs_stamping *stamp)
364 struct vfs_stamping *parent;
366 while (stamp) {
367 parent = stamp->parent;
368 g_free (stamp);
369 stamp = parent;
373 void
374 vfs_rmstamp (vfs *v, vfsid id, int removeparents)
376 struct vfs_stamping *stamp, *st1;
378 for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
379 if (stamp->v == v && stamp->id == id){
380 if (stamp->parent != NULL){
381 if (removeparents)
382 vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
383 vfs_rm_parents (stamp->parent);
385 if (st1 == NULL){
386 stamps = stamp->next;
387 } else {
388 st1->next = stamp->next;
390 g_free (stamp);
392 return;
396 static int
397 ferrno (vfs *vfs)
399 return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN;
400 /* Hope that error message is obscure enough ;-) */
404 mc_open (const char *filename, int flags, ...)
406 int handle;
407 int mode;
408 void *info;
409 va_list ap;
411 char *file = vfs_canon (filename);
412 vfs *vfs = vfs_type (file);
414 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
415 va_start (ap, flags);
416 mode = va_arg (ap, int);
417 va_end (ap);
419 if (!vfs->open) {
420 errno = -EOPNOTSUPP;
421 return -1;
424 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
425 g_free (file);
426 if (!info){
427 errno = ferrno (vfs);
428 return -1;
430 handle = get_bucket ();
431 vfs_file_table [handle].fs_info = info;
432 vfs_file_table [handle].operations = vfs;
434 return handle;
437 #define vfs_op(handle) vfs_file_table [handle].operations
438 #define vfs_info(handle) vfs_file_table [handle].fs_info
439 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
441 #define MC_OP(name, inarg, callarg, pre, post) \
442 int mc_##name inarg \
444 vfs *vfs; \
445 int result; \
447 pre \
448 result = vfs->name ? (*vfs->name)callarg : -1; \
449 post \
450 if (result == -1) \
451 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
452 return result; \
455 #define MC_NAMEOP(name, inarg, callarg) \
456 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
457 #define MC_HANDLEOP(name, inarg, callarg) \
458 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
460 MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) )
463 mc_ctl (int handle, int ctlop, int arg)
465 vfs *vfs = vfs_op (handle);
467 return vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
471 mc_setctl (char *path, int ctlop, char *arg)
473 vfs *vfs;
474 int result;
476 if (!path)
477 vfs_die("You don't want to pass NULL to mc_setctl.");
479 path = vfs_canon (path);
480 vfs = vfs_type (path);
481 result = vfs->setctl ? (*vfs->setctl)(vfs, path, ctlop, arg) : 0;
482 g_free (path);
483 return result;
487 mc_close (int handle)
489 vfs *vfs;
490 int result;
492 if (handle == -1 || !vfs_info (handle))
493 return -1;
495 vfs = vfs_op (handle);
496 if (handle < 3)
497 return close (handle);
499 if (!vfs->close)
500 vfs_die ("VFS must support close.\n");
501 result = (*vfs->close)(vfs_info (handle));
502 vfs_free_bucket (handle);
503 if (result == -1)
504 errno = ferrno (vfs);
506 return result;
509 DIR *
510 mc_opendir (char *dirname)
512 int handle, *handlep;
513 void *info;
514 vfs *vfs;
516 dirname = vfs_canon (dirname);
517 vfs = vfs_type (dirname);
519 info = vfs->opendir ? (*vfs->opendir)(vfs, dirname) : NULL;
520 g_free (dirname);
521 if (!info){
522 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
523 return NULL;
525 handle = get_bucket ();
526 vfs_file_table [handle].fs_info = info;
527 vfs_file_table [handle].operations = vfs;
529 handlep = g_new (int, 1);
530 *handlep = handle;
531 return (DIR *) handlep;
534 /* This should strip the non needed part of a path name */
535 #define vfs_name(x) x
537 void
538 mc_seekdir (DIR *dirp, int offset)
540 int handle;
541 vfs *vfs;
543 if (!dirp){
544 errno = EFAULT;
545 return;
547 handle = *(int *) dirp;
548 vfs = vfs_op (handle);
549 if (vfs->seekdir)
550 (*vfs->seekdir) (vfs_info (handle), offset);
551 else
552 errno = E_NOTSUPP;
555 #define MC_DIROP(name, type, onerr ) \
556 type mc_##name (DIR *dirp) \
558 int handle; \
559 vfs *vfs; \
560 type result; \
562 if (!dirp){ \
563 errno = EFAULT; \
564 return onerr; \
566 handle = *(int *) dirp; \
567 vfs = vfs_op (handle); \
568 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
569 if (result == onerr) \
570 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
571 return result; \
574 MC_DIROP (readdir, struct dirent *, NULL)
575 MC_DIROP (telldir, int, -1)
578 mc_closedir (DIR *dirp)
580 int handle = *(int *) dirp;
581 vfs *vfs = vfs_op (handle);
582 int result;
584 result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
585 vfs_free_bucket (handle);
586 g_free (dirp);
587 return result;
590 int mc_stat (char *path, struct stat *buf) {
591 vfs *vfs;
592 int result;
594 path = vfs_canon (path); vfs = vfs_type (path);
595 result = vfs->stat ? (*vfs->stat) (vfs, vfs_name (path), buf) : -1;
596 g_free (path);
597 if (result == -1)
598 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
599 return result;
602 int mc_lstat (char *path, struct stat *buf) {
603 vfs *vfs;
604 int result;
606 path = vfs_canon (path); vfs = vfs_type (path);
607 result = vfs->lstat ? (*vfs->lstat) (vfs, vfs_name (path), buf) : -1;
608 g_free (path);
609 if (result == -1)
610 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
611 return result;
614 int mc_fstat (int handle, struct stat *buf) {
615 vfs *vfs;
616 int result;
618 if (handle == -1)
619 return -1;
620 vfs = vfs_op (handle);
621 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
622 if (result == -1)
623 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
624 return result;
628 * You must g_strdup whatever this function returns.
631 static const char *
632 mc_return_cwd (void)
634 char *p;
635 struct stat my_stat, my_stat2;
637 if (!vfs_rosplit (current_dir)){
638 p = g_get_current_dir ();
639 if (!p) /* One of the directories in the path is not readable */
640 return current_dir;
642 /* Otherwise check if it is O.K. to use the current_dir */
643 if (!cd_symlinks ||
644 mc_stat (p, &my_stat) ||
645 mc_stat (current_dir, &my_stat2) ||
646 my_stat.st_ino != my_stat2.st_ino ||
647 my_stat.st_dev != my_stat2.st_dev){
648 g_free (current_dir);
649 current_dir = p;
650 return p;
651 } /* Otherwise we return current_dir below */
652 g_free (p);
654 return current_dir;
657 char *
658 mc_get_current_wd (char *buffer, int size)
660 const char *cwd = mc_return_cwd();
662 strncpy (buffer, cwd, size);
663 return buffer;
666 MC_NAMEOP (chmod, (char *path, int mode), (vfs, vfs_name (path), mode))
667 MC_NAMEOP (chown, (char *path, int owner, int group), (vfs, vfs_name (path), owner, group))
668 MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs, vfs_name (path), times))
669 MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs, vfs_name (path), buf, bufsiz))
670 MC_NAMEOP (unlink, (char *path), (vfs, vfs_name (path)))
671 MC_NAMEOP (symlink, (char *name1, char *path), (vfs, vfs_name (name1), vfs_name (path)))
673 #define MC_RENAMEOP(name) \
674 int mc_##name (const char *fname1, const char *fname2) \
676 vfs *vfs; \
677 int result; \
679 char *name2, *name1 = vfs_canon (fname1); \
680 vfs = vfs_type (name1); \
681 name2 = vfs_canon (fname2); \
682 if (vfs != vfs_type (name2)){ \
683 errno = EXDEV; \
684 g_free (name1); \
685 g_free (name2); \
686 return -1; \
689 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
690 g_free (name1); \
691 g_free (name2); \
692 if (result == -1) \
693 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
694 return result; \
697 MC_RENAMEOP (link)
698 MC_RENAMEOP (rename)
700 MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte))
702 off_t mc_lseek (int fd, off_t offset, int whence)
704 vfs *vfs;
705 int result;
707 if (fd == -1)
708 return -1;
710 vfs = vfs_op (fd);
711 result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1;
712 if (result == -1)
713 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
714 return result;
718 * remove //, /./ and /../, local should point to big enough buffer
721 #define ISSLASH(a) (!a || (a == '/'))
723 char *
724 vfs_canon (const char *path)
726 if (!path)
727 vfs_die("Cannot canonicalize NULL");
729 /* Tilde expansion */
730 if (*path == '~'){
731 char *local, *result;
733 local = tilde_expand (path);
734 if (local){
735 result = vfs_canon (local);
736 g_free (local);
737 return result;
741 /* Relative to current directory */
742 if (*path != PATH_SEP){
743 char *local, *result;
745 local = concat_dir_and_file (current_dir, path);
747 result = vfs_canon (local);
748 g_free (local);
749 return result;
753 * So we have path of following form:
754 * /p1/p2#op/.././././p3#op/p4. Good luck.
757 char *result = g_strdup (path);
758 canonicalize_pathname (result);
759 return result;
763 vfsid
764 vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
766 vfsid nvfsid;
768 dir = concat_dir_and_file (dir, "");
770 nvfsid = (*nvfs->getid)(nvfs, dir, par);
772 g_free (dir);
773 return nvfsid;
776 static int
777 is_parent (vfs * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
779 struct vfs_stamping *stamp;
781 for (stamp = parent; stamp; stamp = stamp->parent)
782 if (stamp->v == nvfs && stamp->id == nvfsid)
783 break;
785 return (stamp ? 1 : 0);
788 void
789 vfs_add_noncurrent_stamps (vfs * oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
791 #ifndef VFS_STANDALONE
792 vfs *nvfs, *n2vfs, *n3vfs;
793 vfsid nvfsid, n2vfsid, n3vfsid;
794 struct vfs_stamping *par, *stamp;
795 int f;
797 /* FIXME: As soon as we convert to multiple panels, this stuff
798 has to change. It works like this: We do not time out the
799 vfs's which are current in any panel and on the other
800 side we add the old directory with all its parents which
801 are not in any panel (if we find such one, we stop adding
802 parents to the time-outing structure. */
804 /* There are three directories we have to take care of: current_dir,
805 cpanel->cwd and opanel->cwd. Athough most of the time either
806 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
807 same, it's possible that all three are different -- Norbert */
809 if (!cpanel)
810 return;
812 nvfs = vfs_type (current_dir);
813 nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
814 vfs_rmstamp (nvfs, nvfsid, 1);
816 f = is_parent (oldvfs, oldvfsid, par);
817 vfs_rm_parents (par);
818 if ((nvfs == oldvfs && nvfsid == oldvfsid) || oldvfsid == (vfsid *)-1 || f){
819 return;
822 if (get_current_type () == view_listing){
823 n2vfs = vfs_type (cpanel->cwd);
824 n2vfsid = vfs_ncs_getid (n2vfs, cpanel->cwd, &par);
825 f = is_parent (oldvfs, oldvfsid, par);
826 vfs_rm_parents (par);
827 if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
828 return;
829 } else {
830 n2vfs = (vfs *) -1;
831 n2vfsid = (vfs *) -1;
834 if (get_other_type () == view_listing){
835 n3vfs = vfs_type (opanel->cwd);
836 n3vfsid = vfs_ncs_getid (n3vfs, opanel->cwd, &par);
837 f = is_parent (oldvfs, oldvfsid, par);
838 vfs_rm_parents (par);
839 if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
840 return;
841 } else {
842 n3vfs = (vfs *)-1;
843 n3vfsid = (vfs *)-1;
846 if ((*oldvfs->nothingisopen) (oldvfsid)){
847 if (oldvfs == &vfs_extfs_ops && ((extfs_archive *) oldvfsid)->name == 0){
848 /* Free the resources immediatly when we leave a mtools fs
849 ('cd a:') instead of waiting for the vfs-timeout */
850 (oldvfs->free) (oldvfsid);
851 } else
852 vfs_addstamp (oldvfs, oldvfsid, parent);
853 for (stamp = parent; stamp != NULL; stamp = stamp->parent){
854 if ((stamp->v == nvfs && stamp->id == nvfsid) ||
855 (stamp->v == n2vfs && stamp->id == n2vfsid) ||
856 (stamp->v == n3vfs && stamp->id == n3vfsid) ||
857 stamp->id == (vfsid) - 1 ||
858 !(*stamp->v->nothingisopen) (stamp->id))
859 break;
860 if (stamp->v == &vfs_extfs_ops && ((extfs_archive *) stamp->id)->name == 0){
861 (stamp->v->free) (stamp->id);
862 vfs_rmstamp (stamp->v, stamp->id, 0);
863 } else
864 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
867 #else
868 vfs_addstamp (oldvfs, oldvfsid, parent);
869 #endif
872 static void
873 vfs_stamp_path (char *path)
875 vfs *vfs;
876 vfsid id;
877 struct vfs_stamping *par, *stamp;
879 vfs = vfs_type (path);
880 id = vfs_ncs_getid (vfs, path, &par);
881 vfs_addstamp (vfs, id, par);
883 for (stamp = par; stamp != NULL; stamp = stamp->parent)
884 vfs_addstamp (stamp->v, stamp->id, stamp->parent);
885 vfs_rm_parents (par);
888 #ifndef VFS_STANDALONE
889 void
890 vfs_add_current_stamps (void)
892 vfs_stamp_path (current_dir);
894 if (cpanel) {
895 if (get_current_type () == view_listing)
896 vfs_stamp_path (cpanel->cwd);
899 if (opanel) {
900 if (get_other_type () == view_listing)
901 vfs_stamp_path (opanel->cwd);
904 #endif
906 /* This function is really broken */
908 mc_chdir (char *path)
910 char *a, *b;
911 int result;
912 char *p = NULL;
913 vfs *oldvfs;
914 vfsid oldvfsid;
915 struct vfs_stamping *parent;
917 a = current_dir; /* Save a copy for case of failure */
918 current_dir = vfs_canon (path);
919 current_vfs = vfs_type (current_dir);
920 b = g_strdup (current_dir);
921 result = (*current_vfs->chdir) ? (*current_vfs->chdir)(current_vfs, vfs_name (b)) : -1;
922 g_free (b);
923 if (result == -1){
924 errno = ferrno (current_vfs);
925 g_free (current_dir);
926 current_vfs = vfs_type (a);
927 current_dir = a;
928 } else {
929 oldvfs = vfs_type (a);
930 oldvfsid = vfs_ncs_getid (oldvfs, a, &parent);
931 g_free (a);
932 vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
933 vfs_rm_parents (parent);
936 if (*current_dir){
937 p = strchr (current_dir, 0) - 1;
938 if (*p == PATH_SEP && p > current_dir)
939 *p = 0; /* Sometimes we assume no trailing slash on cwd */
941 return result;
945 vfs_current_is_local (void)
947 return current_vfs == &vfs_local_ops;
950 #if 0
951 /* External world should not do differences between VFS-s */
953 vfs_current_is_extfs (void)
955 return current_vfs == &vfs_extfs_ops;
959 vfs_current_is_tarfs (void)
961 return current_vfs == &vfs_tarfs_ops;
965 vfs_current_is_cpiofs (void)
967 return current_vfs == &vfs_cpiofs_ops;
969 #endif
972 vfs_file_is_local (const char *file)
974 char *filename = vfs_canon (file);
975 vfs *vfs = vfs_type (filename);
977 g_free (filename);
978 return vfs == &vfs_local_ops;
982 vfs_file_is_ftp (char *filename)
984 #ifdef USE_NETCODE
985 vfs *vfs;
987 filename = vfs_canon (filename);
988 vfs = vfs_type (filename);
989 g_free (filename);
990 return vfs == &vfs_ftpfs_ops;
991 #else
992 return 0;
993 #endif
997 vfs_file_is_smb (char *filename)
999 #ifdef WITH_SMBFS
1000 #ifdef USE_NETCODE
1001 vfs *vfs;
1003 filename = vfs_canon (filename);
1004 vfs = vfs_type (filename);
1005 g_free (filename);
1006 return vfs == &vfs_smbfs_ops;
1007 #endif /* USE_NETCODE */
1008 #endif /* WITH_SMBFS */
1009 return 0;
1012 char *vfs_get_current_dir (void)
1014 return current_dir;
1017 static void vfs_setup_wd (void)
1019 current_dir = g_strdup (PATH_SEP_STR);
1020 if (!(vfs_flags & FL_NO_CWDSETUP))
1021 mc_return_cwd();
1023 if (strlen(current_dir)>MC_MAXPATHLEN-2)
1024 vfs_die ("Current dir too long.\n");
1027 MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs, vfs_name (path), mode))
1028 MC_NAMEOP (rmdir, (char *path), (vfs, vfs_name (path)))
1029 MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs, vfs_name (path), mode, dev))
1031 #ifdef HAVE_MMAP
1032 static struct mc_mmapping {
1033 caddr_t addr;
1034 void *vfs_info;
1035 vfs *vfs;
1036 struct mc_mmapping *next;
1037 } *mc_mmaparray = NULL;
1039 caddr_t
1040 mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
1042 vfs *vfs;
1043 caddr_t result;
1044 struct mc_mmapping *mcm;
1046 if (fd == -1)
1047 return (caddr_t) -1;
1049 vfs = vfs_op (fd);
1050 result = vfs->mmap ? (*vfs->mmap)(vfs, addr, len, prot, flags, vfs_info (fd), offset) : (caddr_t)-1;
1051 if (result == (caddr_t)-1){
1052 errno = ferrno (vfs);
1053 return (caddr_t)-1;
1055 mcm =g_new (struct mc_mmapping, 1);
1056 mcm->addr = result;
1057 mcm->vfs_info = vfs_info (fd);
1058 mcm->vfs = vfs;
1059 mcm->next = mc_mmaparray;
1060 mc_mmaparray = mcm;
1061 return result;
1065 mc_munmap (caddr_t addr, size_t len)
1067 struct mc_mmapping *mcm, *mcm2 = NULL;
1069 for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
1070 if (mcm->addr == addr){
1071 if (mcm2 == NULL)
1072 mc_mmaparray = mcm->next;
1073 else
1074 mcm2->next = mcm->next;
1075 if (mcm->vfs->munmap)
1076 (*mcm->vfs->munmap)(mcm->vfs, addr, len, mcm->vfs_info);
1077 g_free (mcm);
1078 return 0;
1081 return -1;
1084 #endif
1086 char *
1087 mc_def_getlocalcopy (vfs *vfs, char *filename)
1089 char *tmp;
1090 int fdin, fdout, i;
1091 char buffer[8192];
1092 struct stat mystat;
1093 char *ext = NULL;
1094 char *ptr;
1096 fdin = mc_open (filename, O_RDONLY);
1097 if (fdin == -1)
1098 return NULL;
1100 /* Try to preserve existing extension */
1101 for (ptr = filename + strlen(filename) - 1; ptr >= filename; ptr--) {
1102 if (*ptr == '.') {
1103 ext = ptr;
1104 break;
1107 if (!isalnum((unsigned char) *ptr))
1108 break;
1111 fdout = mc_mkstemps (&tmp, "mclocalcopy", ext);
1112 if (fdout == -1)
1113 goto fail;
1114 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0){
1115 if (write (fdout, buffer, i) != i)
1116 goto fail;
1118 if (i == -1)
1119 goto fail;
1120 i = mc_close (fdin);
1121 fdin = -1;
1122 if (i==-1)
1123 goto fail;
1124 if (close (fdout)==-1)
1125 goto fail;
1127 if (mc_stat (filename, &mystat) != -1){
1128 chmod (tmp, mystat.st_mode);
1130 return tmp;
1132 fail:
1133 if (fdout) close(fdout);
1134 if (fdin) mc_close (fdin);
1135 g_free (tmp);
1136 return NULL;
1139 char *
1140 mc_getlocalcopy (const char *pathname)
1142 char *result;
1143 char *path = vfs_canon (pathname);
1144 vfs *vfs = vfs_type (path);
1146 result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs, vfs_name (path)) :
1147 mc_def_getlocalcopy (vfs, vfs_name (path));
1148 g_free (path);
1149 if (!result)
1150 errno = ferrno (vfs);
1151 return result;
1155 mc_def_ungetlocalcopy (vfs *vfs, char *filename, char *local, int has_changed)
1156 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1157 int fdin = -1, fdout = -1, i;
1158 if (has_changed){
1159 char buffer [8192];
1161 fdin = open (local, O_RDONLY);
1162 if (fdin == -1)
1163 goto failed;
1164 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1165 if (fdout == -1)
1166 goto failed;
1167 while ((i = read (fdin, buffer, sizeof (buffer))) > 0){
1168 if (mc_write (fdout, buffer, i) != i)
1169 goto failed;
1171 if (i == -1)
1172 goto failed;
1174 if (close (fdin)==-1) {
1175 fdin = -1;
1176 goto failed;
1178 fdin = -1;
1179 if (mc_close (fdout)==-1) {
1180 fdout = -1;
1181 goto failed;
1184 unlink (local);
1185 g_free (local);
1186 return 0;
1188 failed:
1189 message_1s (1, _("Changes to file lost"), filename);
1190 if (fdout!=-1) mc_close(fdout);
1191 if (fdin!=-1) close(fdin);
1192 unlink (local);
1193 g_free (local);
1194 return -1;
1198 mc_ungetlocalcopy (const char *pathname, char *local, int has_changed)
1200 int return_value = 0;
1201 char *path = vfs_canon (pathname);
1202 vfs *vfs = vfs_type (path);
1204 return_value = vfs->ungetlocalcopy ?
1205 (*vfs->ungetlocalcopy)(vfs, vfs_name (path), local, has_changed) :
1206 mc_def_ungetlocalcopy (vfs, vfs_name (path), local, has_changed);
1207 g_free (path);
1208 return return_value;
1212 * Hmm, as timeout is minute or so, do we need to care about usecs?
1214 static inline int
1215 timeoutcmp (struct timeval *t1, struct timeval *t2)
1217 return ((t1->tv_sec < t2->tv_sec)
1218 || ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
1221 /* This is called from timeout handler with now = 0, or can be called
1222 with now = 1 to force freeing all filesystems that are not in use */
1224 void
1225 vfs_expire (int now)
1227 static int locked = 0;
1228 struct timeval time;
1229 struct vfs_stamping *stamp, *st;
1231 /* Avoid recursive invocation, e.g. when one of the free functions
1232 calls message_1s */
1233 if (locked)
1234 return;
1235 locked = 1;
1237 gettimeofday (&time, NULL);
1238 time.tv_sec -= vfs_timeout;
1240 for (stamp = stamps; stamp != NULL;){
1241 if (now || (timeoutcmp (&stamp->time, &time))){
1242 st = stamp->next;
1243 (*stamp->v->free) (stamp->id);
1244 vfs_rmstamp (stamp->v, stamp->id, 0);
1245 stamp = st;
1246 } else
1247 stamp = stamp->next;
1249 locked = 0;
1252 void
1253 vfs_timeout_handler (void)
1255 vfs_expire (0);
1258 void
1259 vfs_init (void)
1261 time_t current_time;
1262 struct tm *t;
1264 memset (vfs_file_table, 0, sizeof (vfs_file_table));
1265 current_time = time (NULL);
1266 t = localtime (&current_time);
1267 current_mday = t->tm_mday;
1268 current_mon = t->tm_mon;
1269 current_year = t->tm_year;
1271 /* We do not want to register vfs_local_ops */
1273 #ifdef USE_NETCODE
1274 tcp_init();
1275 vfs_register (&vfs_ftpfs_ops);
1276 #ifdef WITH_SMBFS
1277 vfs_register (&vfs_smbfs_ops);
1278 #endif
1279 #ifdef WITH_MCFS
1280 vfs_register (&vfs_mcfs_ops);
1281 #endif
1282 #endif
1284 vfs_register (&vfs_fish_ops);
1285 vfs_register (&vfs_extfs_ops);
1286 vfs_register (&vfs_sfs_ops);
1287 vfs_register (&vfs_tarfs_ops);
1288 vfs_register (&vfs_cpiofs_ops);
1290 #ifdef USE_EXT2FSLIB
1291 vfs_register (&vfs_undelfs_ops);
1292 #endif
1294 vfs_setup_wd ();
1297 void
1298 vfs_free_resources (char *path)
1300 vfs *vfs;
1301 vfsid vid;
1302 struct vfs_stamping *parent;
1304 vfs = vfs_type (path);
1305 vid = vfs_ncs_getid (vfs, path, &parent);
1306 if (vid != (vfsid) -1)
1307 (*vfs->free)(vid);
1308 vfs_rm_parents (parent);
1311 #if 0
1312 /* Shutdown a vfs given a path name */
1313 void
1314 vfs_shut_path (char *p)
1316 vfs *the_vfs;
1317 struct vfs_stamping *par;
1319 the_vfs = vfs_type (p);
1320 vfs_ncs_getid (the_vfs, p, &par);
1321 (*par->v->free)(par->id);
1322 vfs_rm_parents (par);
1324 #endif
1326 void
1327 vfs_shut (void)
1329 struct vfs_stamping *stamp, *st;
1330 vfs *vfs;
1332 for (stamp = stamps, stamps = 0; stamp != NULL;){
1333 (*stamp->v->free)(stamp->id);
1334 st = stamp->next;
1335 g_free (stamp);
1336 stamp = st;
1339 if (stamps)
1340 vfs_rmstamp (stamps->v, stamps->id, 1);
1342 if (current_dir)
1343 g_free (current_dir);
1345 for (vfs=vfs_list; vfs; vfs=vfs->next)
1346 if (vfs->done)
1347 (*vfs->done) (vfs);
1351 * These ones grab information from the VFS
1352 * and handles them to an upper layer
1354 void
1355 vfs_fill_names (void (*func)(char *))
1357 vfs *vfs;
1359 for (vfs=vfs_list; vfs; vfs=vfs->next)
1360 if (vfs->fill_names)
1361 (*vfs->fill_names) (vfs, func);
1364 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1365 #define MAXCOLS 30
1367 static char *columns [MAXCOLS]; /* Points to the string in column n */
1368 static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
1371 vfs_split_text (char *p)
1373 char *original = p;
1374 int numcols;
1376 memset (columns, 0, sizeof (columns));
1378 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
1379 while (*p == ' ' || *p == '\r' || *p == '\n'){
1380 *p = 0;
1381 p++;
1383 columns [numcols] = p;
1384 column_ptr [numcols] = p - original;
1385 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
1386 p++;
1388 return numcols;
1391 static int
1392 is_num (int idx)
1394 char *column = columns[idx];
1396 if (!column || column[0] < '0' || column[0] > '9')
1397 return 0;
1399 return 1;
1402 static int
1403 is_dos_date (char *str)
1405 if (!str)
1406 return 0;
1408 if (strlen (str) == 8 && str[2] == str[5]
1409 && strchr ("\\-/", (int) str[2]) != NULL)
1410 return 1;
1412 return 0;
1415 static int
1416 is_week (char *str, struct tm *tim)
1418 static const char *week = "SunMonTueWedThuFriSat";
1419 char *pos;
1421 if (!str)
1422 return 0;
1424 if ((pos = strstr (week, str)) != NULL) {
1425 if (tim != NULL)
1426 tim->tm_wday = (pos - week) / 3;
1427 return 1;
1429 return 0;
1432 static int
1433 is_month (char *str, struct tm *tim)
1435 static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
1436 char *pos;
1438 if (!str)
1439 return 0;
1441 if ((pos = strstr (month, str)) != NULL) {
1442 if (tim != NULL)
1443 tim->tm_mon = (pos - month) / 3;
1444 return 1;
1446 return 0;
1449 static int
1450 is_time (char *str, struct tm *tim)
1452 char *p, *p2;
1454 if (!str)
1455 return 0;
1457 if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
1458 if (p != p2) {
1459 if (sscanf
1460 (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min,
1461 &tim->tm_sec) != 3)
1462 return 0;
1463 } else {
1464 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
1465 return 0;
1467 } else
1468 return 0;
1470 return 1;
1473 static int is_year (char *str, struct tm *tim)
1475 long year;
1477 if (!str)
1478 return 0;
1480 if (strchr (str, ':'))
1481 return 0;
1483 if (strlen (str) != 4)
1484 return 0;
1486 if (sscanf (str, "%ld", &year) != 1)
1487 return 0;
1489 if (year < 1900 || year > 3000)
1490 return 0;
1492 tim->tm_year = (int) (year - 1900);
1494 return 1;
1498 * FIXME: this is broken. Consider following entry:
1499 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1500 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1504 vfs_parse_filetype (char c)
1506 switch (c) {
1507 case 'd': return S_IFDIR;
1508 case 'b': return S_IFBLK;
1509 case 'c': return S_IFCHR;
1510 case 'l': return S_IFLNK;
1511 case 's': /* Socket */
1512 #ifdef S_IFSOCK
1513 return S_IFSOCK;
1514 #else
1515 /* If not supported, we fall through to IFIFO */
1516 return S_IFIFO;
1517 #endif
1518 case 'D': /* Solaris door */
1519 #ifdef S_IFDOOR
1520 return S_IFDOOR;
1521 #else
1522 return S_IFIFO;
1523 #endif
1524 case 'p': return S_IFIFO;
1525 case 'm': case 'n': /* Don't know what these are :-) */
1526 case '-': case '?': return S_IFREG;
1527 default: return -1;
1531 int vfs_parse_filemode (const char *p)
1532 { /* converts rw-rw-rw- into 0666 */
1533 int res = 0;
1534 switch (*(p++)){
1535 case 'r': res |= 0400; break;
1536 case '-': break;
1537 default: return -1;
1539 switch (*(p++)){
1540 case 'w': res |= 0200; break;
1541 case '-': break;
1542 default: return -1;
1544 switch (*(p++)){
1545 case 'x': res |= 0100; break;
1546 case 's': res |= 0100 | S_ISUID; break;
1547 case 'S': res |= S_ISUID; break;
1548 case '-': break;
1549 default: return -1;
1551 switch (*(p++)){
1552 case 'r': res |= 0040; break;
1553 case '-': break;
1554 default: return -1;
1556 switch (*(p++)){
1557 case 'w': res |= 0020; break;
1558 case '-': break;
1559 default: return -1;
1561 switch (*(p++)){
1562 case 'x': res |= 0010; break;
1563 case 's': res |= 0010 | S_ISGID; break;
1564 case 'l': /* Solaris produces these */
1565 case 'S': res |= S_ISGID; break;
1566 case '-': break;
1567 default: return -1;
1569 switch (*(p++)){
1570 case 'r': res |= 0004; break;
1571 case '-': break;
1572 default: return -1;
1574 switch (*(p++)){
1575 case 'w': res |= 0002; break;
1576 case '-': break;
1577 default: return -1;
1579 switch (*(p++)){
1580 case 'x': res |= 0001; break;
1581 case 't': res |= 0001 | S_ISVTX; break;
1582 case 'T': res |= S_ISVTX; break;
1583 case '-': break;
1584 default: return -1;
1586 return res;
1589 int vfs_parse_filedate(int idx, time_t *t)
1590 { /* This thing parses from idx in columns[] array */
1592 char *p;
1593 struct tm tim;
1594 int d[3];
1595 int got_year = 0;
1597 /* Let's setup default time values */
1598 tim.tm_year = current_year;
1599 tim.tm_mon = current_mon;
1600 tim.tm_mday = current_mday;
1601 tim.tm_hour = 0;
1602 tim.tm_min = 0;
1603 tim.tm_sec = 0;
1604 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
1606 p = columns [idx++];
1608 /* We eat weekday name in case of extfs */
1609 if(is_week(p, &tim))
1610 p = columns [idx++];
1612 /* Month name */
1613 if(is_month(p, &tim)){
1614 /* And we expect, it followed by day number */
1615 if (is_num (idx))
1616 tim.tm_mday = (int)atol (columns [idx++]);
1617 else
1618 return 0; /* No day */
1620 } else {
1621 /* We usually expect:
1622 Mon DD hh:mm
1623 Mon DD YYYY
1624 But in case of extfs we allow these date formats:
1625 Mon DD YYYY hh:mm
1626 Mon DD hh:mm YYYY
1627 Wek Mon DD hh:mm:ss YYYY
1628 MM-DD-YY hh:mm
1629 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1630 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1632 /* Here just this special case with MM-DD-YY */
1633 if (is_dos_date(p)){
1634 p[2] = p[5] = '-';
1636 if(sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
1637 /* We expect to get:
1638 1. MM-DD-YY
1639 2. DD-MM-YY
1640 3. YY-MM-DD
1641 4. YY-DD-MM */
1643 /* Hmm... maybe, next time :)*/
1645 /* At last, MM-DD-YY */
1646 d[0]--; /* Months are zerobased */
1647 /* Y2K madness */
1648 if(d[2] < 70)
1649 d[2] += 100;
1651 tim.tm_mon = d[0];
1652 tim.tm_mday = d[1];
1653 tim.tm_year = d[2];
1654 got_year = 1;
1655 } else
1656 return 0; /* sscanf failed */
1657 } else
1658 return 0; /* unsupported format */
1661 /* Here we expect to find time and/or year */
1663 if (is_num (idx)) {
1664 if(is_time(columns[idx], &tim) || (got_year = is_year(columns[idx], &tim))) {
1665 idx++;
1667 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1668 if(is_num (idx) && (columns[idx+1][0]) &&
1669 ((got_year |= is_year(columns[idx], &tim)) || is_time(columns[idx], &tim)))
1670 idx++; /* time & year or reverse */
1671 } /* only time or date */
1673 else
1674 return 0; /* Nor time or date */
1677 * If the date is less than 6 months in the past, it is shown without year
1678 * other dates in the past or future are shown with year but without time
1679 * This does not check for years before 1900 ... I don't know, how
1680 * to represent them at all
1682 if (!got_year &&
1683 current_mon < 6 && current_mon < tim.tm_mon &&
1684 tim.tm_mon - current_mon >= 6)
1686 tim.tm_year--;
1688 if ((*t = mktime(&tim)) < 0)
1689 *t = 0;
1690 return idx;
1694 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname)
1696 int idx, idx2, num_cols;
1697 int i;
1698 char *p_copy = NULL;
1699 char *t = NULL;
1700 const char *line = p;
1702 if (strncmp (p, "total", 5) == 0)
1703 return 0;
1705 if ((i = vfs_parse_filetype(*(p++))) == -1)
1706 goto error;
1708 s->st_mode = i;
1709 if (*p == ' ') /* Notwell 4 */
1710 p++;
1711 if (*p == '['){
1712 if (strlen (p) <= 8 || p [8] != ']')
1713 goto error;
1714 /* Should parse here the Notwell permissions :) */
1715 if (S_ISDIR (s->st_mode))
1716 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
1717 else
1718 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
1719 p += 9;
1720 } else {
1721 if ((i = vfs_parse_filemode(p)) == -1)
1722 goto error;
1723 s->st_mode |= i;
1724 p += 9;
1726 /* This is for an extra ACL attribute (HP-UX) */
1727 if (*p == '+')
1728 p++;
1731 p_copy = g_strdup(p);
1732 num_cols = vfs_split_text (p_copy);
1734 s->st_nlink = atol (columns [0]);
1735 if (s->st_nlink <= 0)
1736 goto error;
1738 if (!is_num (1))
1739 s->st_uid = finduid (columns [1]);
1740 else
1741 s->st_uid = (uid_t) atol (columns [1]);
1743 /* Mhm, the ls -lg did not produce a group field */
1744 for (idx = 3; idx <= 5; idx++)
1745 if (is_month(columns [idx], NULL) || is_week(columns [idx], NULL) || is_dos_date(columns[idx]))
1746 break;
1748 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
1749 goto error;
1751 /* We don't have gid */
1752 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
1753 idx2 = 2;
1754 else {
1755 /* We have gid field */
1756 if (is_num (2))
1757 s->st_gid = (gid_t) atol (columns [2]);
1758 else
1759 s->st_gid = findgid (columns [2]);
1760 idx2 = 3;
1763 /* This is device */
1764 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
1765 int maj, min;
1767 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
1768 goto error;
1770 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
1771 goto error;
1773 #ifdef HAVE_ST_RDEV
1774 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
1775 #endif
1776 s->st_size = 0;
1778 } else {
1779 /* Common file size */
1780 if (!is_num (idx2))
1781 goto error;
1783 s->st_size = (size_t) atol (columns [idx2]);
1784 #ifdef HAVE_ST_RDEV
1785 s->st_rdev = 0;
1786 #endif
1789 idx = vfs_parse_filedate(idx, &s->st_mtime);
1790 if (!idx)
1791 goto error;
1792 /* Use resulting time value */
1793 s->st_atime = s->st_ctime = s->st_mtime;
1794 #if 0
1795 /* These variables must be initialized by vfs_s_new_inode () */
1796 s->st_dev = 0;
1797 s->st_ino = 0;
1798 #endif
1799 #ifdef HAVE_ST_BLKSIZE
1800 s->st_blksize = 512;
1801 #endif
1802 #ifdef HAVE_ST_BLOCKS
1803 s->st_blocks = (s->st_size + 511) / 512;
1804 #endif
1806 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
1807 if (strcmp (columns [i], "->") == 0){
1808 idx2 = i;
1809 break;
1812 if (((S_ISLNK (s->st_mode) ||
1813 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
1814 && idx2){
1816 if (filename){
1817 *filename = g_strndup (p + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
1819 if (linkname){
1820 t = g_strdup (p + column_ptr [idx2+1]);
1821 *linkname = t;
1823 } else {
1824 /* Extract the filename from the string copy, not from the columns
1825 * this way we have a chance of entering hidden directories like ". ."
1827 if (filename){
1829 *filename = g_strdup (columns [idx++]);
1832 t = g_strdup (p + column_ptr [idx]);
1833 *filename = t;
1835 if (linkname)
1836 *linkname = NULL;
1839 if (t) {
1840 int p = strlen (t);
1841 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1842 t [p] = 0;
1843 if ((--p > 0) && (t [p] == '\r' || t [p] == '\n'))
1844 t [p] = 0;
1847 g_free (p_copy);
1848 return 1;
1850 error:
1852 static int errorcount = 0;
1854 if (++errorcount < 5) {
1855 message_1s (1, _("Could not parse:"), (p_copy && *p_copy) ? p_copy : line);
1856 } else if (errorcount == 5)
1857 message_1s (1, _(" Error "), _("More parsing errors will be ignored."));
1860 g_free (p_copy);
1861 return 0;
1864 void
1865 vfs_die (char *m)
1867 message_1s (1, _("Internal error:"), m);
1868 exit (1);
1871 void
1872 vfs_print_stats (const char *fs_name, const char *action, const char *file_name, off_t have, off_t need)
1874 static char *i18n_percent_transf_format = NULL, *i18n_transf_format = NULL;
1876 if (i18n_percent_transf_format == NULL) {
1877 i18n_percent_transf_format = _("%s: %s: %s %3d%% (%lu bytes transferred)");
1878 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
1881 if (need)
1882 print_vfs_message (i18n_percent_transf_format, fs_name, action,
1883 file_name, (int)((double)have*100/need), (unsigned long) have);
1884 else
1885 print_vfs_message (i18n_transf_format,
1886 fs_name, action, file_name, (unsigned long) have);
1889 #ifndef VFS_STANDALONE
1890 char *
1891 vfs_get_password (char *msg)
1893 return (char *) input_dialog (msg, _("Password:"), "");
1895 #endif
1898 * Returns vfs path corresponding to given url. If passed string is
1899 * not recognized as url, g_strdup(url) is returned.
1901 char *
1902 vfs_translate_url (char *url)
1904 if (strncmp (url, "ftp://", 6) == 0)
1905 return g_strconcat ("/#ftp:", url + 6, NULL);
1906 else if (strncmp (url, "a:", 2) == 0)
1907 return g_strdup ("/#a");
1908 else
1909 return g_strdup (url);