Document "Only directories" checkbox
[midnight-commander/osp/petrarad.git] / vfs / utilvfs.c
bloba2000a7edccc47ac9310129ed7443c20196db75d
1 /* Utilities for VFS modules.
3 Copyright (C) 1988, 1992, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007 Free Software Foundation, Inc.
5 Copyright (C) 1995, 1996 Miguel de Icaza
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License
9 as published by the Free Software Foundation; either version 2 of
10 the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21 #include <config.h>
22 #include <ctype.h>
24 #include "../src/global.h"
25 #include "../src/tty.h" /* enable/disable interrupt key */
26 #include "../src/wtools.h" /* message() */
27 #include "../src/main.h" /* print_vfs_message */
28 #include "utilvfs.h"
29 #include "vfs.h"
30 #include "../src/unixcompat.h"
31 #include "../src/history.h"
33 /* Extract the hostname and username from the path */
34 /* path is in the form: [user@]hostname:port/remote-dir, e.g.:
36 * ftp://sunsite.unc.edu/pub/linux
37 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
38 * ftp://tsx-11.mit.edu:8192/
39 * ftp://joe@foo.edu:11321/private
40 * ftp://joe:password@foo.se
42 * Returns g_malloc()ed host, user and pass they are present.
43 * If the user is empty, e.g. ftp://@roxanne/private, and URL_ALLOW_ANON
44 * is not set, then the current login name is supplied.
46 * Return value is a g_malloc()ed string with the pathname relative to the
47 * host.
50 #ifdef USE_NETCODE
51 char *
52 vfs_split_url (const char *path, char **host, char **user, int *port,
53 char **pass, int default_port, int flags)
55 struct passwd *passwd_info;
56 char *dir, *colon, *inner_colon, *at, *rest;
57 char *retval;
58 char * const pcopy = g_strdup (path);
59 const char *pend = pcopy + strlen (pcopy);
61 if (pass)
62 *pass = NULL;
63 *port = default_port;
64 *user = NULL;
65 retval = NULL;
67 dir = pcopy;
68 if (!(flags & URL_NOSLASH)) {
69 /* locate path component */
70 while (*dir != PATH_SEP && *dir)
71 dir++;
72 if (*dir) {
73 retval = g_strdup (dir);
74 *dir = 0;
75 } else
76 retval = g_strdup (PATH_SEP_STR);
79 /* search for any possible user */
80 at = strrchr (pcopy, '@');
82 /* We have a username */
83 if (at) {
84 *at = 0;
85 inner_colon = strchr (pcopy, ':');
86 if (inner_colon) {
87 *inner_colon = 0;
88 inner_colon++;
89 if (pass)
90 *pass = g_strdup (inner_colon);
92 if (*pcopy != 0)
93 *user = g_strdup (pcopy);
95 if (pend == at + 1)
96 rest = at;
97 else
98 rest = at + 1;
99 } else
100 rest = pcopy;
102 if (!*user && !(flags & URL_ALLOW_ANON)) {
103 passwd_info = getpwuid (geteuid ());
104 if (passwd_info && passwd_info->pw_name)
105 *user = g_strdup (passwd_info->pw_name);
106 else {
107 /* This is very unlikely to happen */
108 *user = g_strdup ("anonymous");
110 endpwent ();
113 /* Check if the host comes with a port spec, if so, chop it */
114 colon = strchr (rest, ':');
115 if (colon) {
116 *colon = 0;
117 if (sscanf (colon + 1, "%d", port) == 1) {
118 if (*port <= 0 || *port >= 65536)
119 *port = default_port;
120 } else {
121 while (*(++colon)) {
122 switch (*colon) {
123 case 'C':
124 *port = 1;
125 break;
126 case 'r':
127 *port = 2;
128 break;
133 if (host)
134 *host = g_strdup (rest);
136 g_free (pcopy);
137 return retval;
139 #endif /* USE_NETCODE */
142 * Look up a user or group name from a uid/gid, maintaining a cache.
143 * FIXME, for now it's a one-entry cache.
144 * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
145 * This file should be modified for non-unix systems to do something
146 * reasonable.
149 #ifndef TUNMLEN
150 #define TUNMLEN 256
151 #endif
152 #ifndef TGNMLEN
153 #define TGNMLEN 256
154 #endif
156 #define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
157 #define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
160 vfs_finduid (const char *uname)
162 static int saveuid = -993;
163 static char saveuname[TUNMLEN];
164 static int my_uid = -993;
166 struct passwd *pw;
168 if (uname[0] != saveuname[0] /* Quick test w/o proc call */
169 ||0 != strncmp (uname, saveuname, TUNMLEN)) {
170 g_strlcpy (saveuname, uname, TUNMLEN);
171 pw = getpwnam (uname);
172 if (pw) {
173 saveuid = pw->pw_uid;
174 } else {
175 saveuid = myuid;
178 return saveuid;
182 vfs_findgid (const char *gname)
184 static int savegid = -993;
185 static char savegname[TGNMLEN];
186 static int my_gid = -993;
188 struct group *gr;
190 if (gname[0] != savegname[0] /* Quick test w/o proc call */
191 ||0 != strncmp (gname, savegname, TUNMLEN)) {
192 g_strlcpy (savegname, gname, TUNMLEN);
193 gr = getgrnam (gname);
194 if (gr) {
195 savegid = gr->gr_gid;
196 } else {
197 savegid = mygid;
200 return savegid;
204 * Create a temporary file with a name resembling the original.
205 * This is needed e.g. for local copies requested by extfs.
206 * Some extfs scripts may look at the extension.
207 * We also protect stupid scripts agains dangerous names.
210 vfs_mkstemps (char **pname, const char *prefix, const char *basename)
212 const char *p;
213 char *suffix, *q;
214 int shift;
215 int fd;
217 /* Strip directories */
218 p = strrchr (basename, PATH_SEP);
219 if (!p)
220 p = basename;
221 else
222 p++;
224 /* Protection against very long names */
225 shift = strlen (p) - (MC_MAXPATHLEN - 16);
226 if (shift > 0)
227 p += shift;
229 suffix = g_malloc (MC_MAXPATHLEN);
231 /* Protection against unusual characters */
232 q = suffix;
233 while (*p && (*p != '#')) {
234 if (strchr (".-_@", *p) || isalnum ((unsigned char) *p))
235 *q++ = *p;
236 p++;
238 *q = 0;
240 fd = mc_mkstemps (pname, prefix, suffix);
241 g_free (suffix);
242 return fd;
245 /* Parsing code is used by ftpfs, fish and extfs */
246 #define MAXCOLS 30
248 static char *columns[MAXCOLS]; /* Points to the string in column n */
249 static int column_ptr[MAXCOLS]; /* Index from 0 to the starting positions of the columns */
252 vfs_split_text (char *p)
254 char *original = p;
255 int numcols;
257 memset (columns, 0, sizeof (columns));
259 for (numcols = 0; *p && numcols < MAXCOLS; numcols++) {
260 while (*p == ' ' || *p == '\r' || *p == '\n') {
261 *p = 0;
262 p++;
264 columns[numcols] = p;
265 column_ptr[numcols] = p - original;
266 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
267 p++;
269 return numcols;
272 static int
273 is_num (int idx)
275 char *column = columns[idx];
277 if (!column || column[0] < '0' || column[0] > '9')
278 return 0;
280 return 1;
283 /* Return 1 for MM-DD-YY and MM-DD-YYYY */
284 static int
285 is_dos_date (const char *str)
287 int len;
289 if (!str)
290 return 0;
292 len = strlen (str);
293 if (len != 8 && len != 10)
294 return 0;
296 if (str[2] != str[5])
297 return 0;
299 if (!strchr ("\\-/", (int) str[2]))
300 return 0;
302 return 1;
305 static int
306 is_week (const char *str, struct tm *tim)
308 static const char *week = "SunMonTueWedThuFriSat";
309 const char *pos;
311 if (!str)
312 return 0;
314 if ((pos = strstr (week, str)) != NULL) {
315 if (tim != NULL)
316 tim->tm_wday = (pos - week) / 3;
317 return 1;
319 return 0;
322 static int
323 is_month (const char *str, struct tm *tim)
325 static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
326 const char *pos;
328 if (!str)
329 return 0;
331 if ((pos = strstr (month, str)) != NULL) {
332 if (tim != NULL)
333 tim->tm_mon = (pos - month) / 3;
334 return 1;
336 return 0;
340 * Check for possible locale's abbreviated month name (Jan..Dec).
341 * Any 3 bytes long string without digit, control and punctuation characters.
342 * isalpha() is locale specific, so it cannot be used if current
343 * locale is "C" and ftp server use Cyrillic.
344 * NB: It is assumed there are no whitespaces in month.
346 static int
347 is_localized_month (const char *month)
349 int i = 0;
351 if (!month)
352 return 0;
354 while ((i < 3) && *month && !isdigit ((unsigned char) *month)
355 && !iscntrl ((unsigned char) *month)
356 && !ispunct ((unsigned char) *month)) {
357 i++;
358 month++;
360 return ((i == 3) && (*month == 0));
363 static int
364 is_time (const char *str, struct tm *tim)
366 const char *p, *p2;
368 if (!str)
369 return 0;
371 if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
372 if (p != p2) {
373 if (sscanf
374 (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min,
375 &tim->tm_sec) != 3)
376 return 0;
377 } else {
378 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
379 return 0;
381 } else
382 return 0;
384 return 1;
387 static int
388 is_year (char *str, struct tm *tim)
390 long year;
392 if (!str)
393 return 0;
395 if (strchr (str, ':'))
396 return 0;
398 if (strlen (str) != 4)
399 return 0;
401 if (sscanf (str, "%ld", &year) != 1)
402 return 0;
404 if (year < 1900 || year > 3000)
405 return 0;
407 tim->tm_year = (int) (year - 1900);
409 return 1;
412 gboolean
413 vfs_parse_filetype (const char *s, size_t *ret_skipped, mode_t *ret_type)
415 mode_t type;
417 switch (*s) {
418 case 'd': type = S_IFDIR; break;
419 case 'b': type = S_IFBLK; break;
420 case 'c': type = S_IFCHR; break;
421 case 'l': type = S_IFLNK; break;
422 #ifdef S_IFSOCK
423 case 's': type = S_IFSOCK; break;
424 #else
425 case 's': type = S_IFIFO; break;
426 #endif
427 #ifdef S_IFDOOR /* Solaris door */
428 case 'D': type = S_IFDOOR; break;
429 #else
430 case 'D': type = S_IFIFO; break;
431 #endif
432 case 'p': type = S_IFIFO; break;
433 #ifdef S_IFNAM /* Special named files */
434 case 'n': type = S_IFNAM; break;
435 #else
436 case 'n': type = S_IFREG; break;
437 #endif
438 case 'm': /* Don't know what these are :-) */
439 case '-':
440 case '?': type = S_IFREG; break;
441 default: return FALSE;
444 *ret_type = type;
445 *ret_skipped = 1;
446 return TRUE;
449 gboolean
450 vfs_parse_fileperms (const char *s, size_t *ret_skipped, mode_t *ret_perms)
452 const char *p;
453 mode_t perms;
455 p = s;
456 perms = 0;
458 switch (*p++) {
459 case '-': break;
460 case 'r': perms |= S_IRUSR; break;
461 default: return FALSE;
463 switch (*p++) {
464 case '-': break;
465 case 'w': perms |= S_IWUSR; break;
466 default: return FALSE;
468 switch (*p++) {
469 case '-': break;
470 case 'S': perms |= S_ISUID; break;
471 case 's': perms |= S_IXUSR | S_ISUID; break;
472 case 'x': perms |= S_IXUSR; break;
473 default: return FALSE;
475 switch (*p++) {
476 case '-': break;
477 case 'r': perms |= S_IRGRP; break;
478 default: return FALSE;
480 switch (*p++) {
481 case '-': break;
482 case 'w': perms |= S_IWGRP; break;
483 default: return FALSE;
485 switch (*p++) {
486 case '-': break;
487 case 'S': perms |= S_ISGID; break;
488 case 'l': perms |= S_ISGID; break; /* found on Solaris */
489 case 's': perms |= S_IXGRP | S_ISGID; break;
490 case 'x': perms |= S_IXGRP; break;
491 default: return FALSE;
493 switch (*p++) {
494 case '-': break;
495 case 'r': perms |= S_IROTH; break;
496 default: return FALSE;
498 switch (*p++) {
499 case '-': break;
500 case 'w': perms |= S_IWOTH; break;
501 default: return FALSE;
503 switch (*p++) {
504 case '-': break;
505 case 'T': perms |= S_ISVTX; break;
506 case 't': perms |= S_IXOTH | S_ISVTX; break;
507 case 'x': perms |= S_IXOTH; break;
508 default: return FALSE;
510 if (*p == '+') { /* ACLs on Solaris, HP-UX and others */
511 p++;
514 *ret_skipped = p - s;
515 *ret_perms = perms;
516 return TRUE;
519 gboolean
520 vfs_parse_filemode (const char *s, size_t *ret_skipped,
521 mode_t *ret_mode)
523 const char *p;
524 mode_t type, perms;
525 size_t skipped;
527 p = s;
529 if (!vfs_parse_filetype (p, &skipped, &type))
530 return FALSE;
531 p += skipped;
533 if (!vfs_parse_fileperms (p, &skipped, &perms))
534 return FALSE;
535 p += skipped;
537 *ret_skipped = p - s;
538 *ret_mode = type | perms;
539 return TRUE;
542 gboolean
543 vfs_parse_raw_filemode (const char *s, size_t *ret_skipped,
544 mode_t *ret_mode)
546 const char *p;
547 mode_t remote_type = 0, local_type, perms = 0;
549 p = s;
551 // isoctal
552 while(*p >= '0' && *p <= '7')
554 perms *= 010;
555 perms += (*p - '0');
556 ++p;
559 if (*p++ != ' ')
560 return FALSE;
562 while(*p >= '0' && *p <= '7')
564 remote_type *= 010;
565 remote_type += (*p - '0');
566 ++p;
569 if (*p++ != ' ')
570 return FALSE;
572 /* generated with:
573 $ perl -e 'use Fcntl ":mode";
574 my @modes = (S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFREG);
575 foreach $t (@modes) { printf ("%o\n", $t); };'
576 TODO: S_IFDOOR, S_IFIFO, S_IFSOCK (if supported by os)
577 (see vfs_parse_filetype)
580 switch (remote_type)
582 case 020000:
583 local_type = S_IFCHR; break;
584 case 040000:
585 local_type = S_IFDIR; break;
586 case 060000:
587 local_type = S_IFBLK; break;
588 case 0120000:
589 local_type = S_IFLNK; break;
590 case 0100000:
591 default:// don't know what is it
592 local_type = S_IFREG; break;
595 *ret_skipped = p - s;
596 *ret_mode = local_type | perms;
597 return TRUE;
600 /* This function parses from idx in the columns[] array */
602 vfs_parse_filedate (int idx, time_t *t)
604 char *p;
605 struct tm tim;
606 int d[3];
607 int got_year = 0;
608 int l10n = 0; /* Locale's abbreviated month name */
609 time_t current_time;
610 struct tm *local_time;
612 /* Let's setup default time values */
613 current_time = time (NULL);
614 local_time = localtime (&current_time);
615 tim.tm_mday = local_time->tm_mday;
616 tim.tm_mon = local_time->tm_mon;
617 tim.tm_year = local_time->tm_year;
619 tim.tm_hour = 0;
620 tim.tm_min = 0;
621 tim.tm_sec = 0;
622 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
624 p = columns[idx++];
626 /* We eat weekday name in case of extfs */
627 if (is_week (p, &tim))
628 p = columns[idx++];
630 /* Month name */
631 if (is_month (p, &tim)) {
632 /* And we expect, it followed by day number */
633 if (is_num (idx))
634 tim.tm_mday = (int) atol (columns[idx++]);
635 else
636 return 0; /* No day */
638 } else {
639 /* We expect:
640 3 fields max or we'll see oddities with certain file names.
641 So both year and time is not allowed.
642 Mon DD hh:mm[:ss]
643 Mon DD YYYY
644 But in case of extfs we allow these date formats:
645 MM-DD-YY hh:mm[:ss]
646 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
647 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
649 /* Special case with MM-DD-YY or MM-DD-YYYY */
650 if (is_dos_date (p)) {
651 p[2] = p[5] = '-';
653 if (sscanf (p, "%2d-%2d-%d", &d[0], &d[1], &d[2]) == 3) {
654 /* Months are zero based */
655 if (d[0] > 0)
656 d[0]--;
658 if (d[2] > 1900) {
659 d[2] -= 1900;
660 } else {
661 /* Y2K madness */
662 if (d[2] < 70)
663 d[2] += 100;
666 tim.tm_mon = d[0];
667 tim.tm_mday = d[1];
668 tim.tm_year = d[2];
669 got_year = 1;
670 } else
671 return 0; /* sscanf failed */
672 } else {
673 /* Locale's abbreviated month name followed by day number */
674 if (is_localized_month (p) && (is_num (idx++)))
675 l10n = 1;
676 else
677 return 0; /* unsupported format */
681 /* Here we expect to find time or year */
682 if (is_num (idx) && (is_time (columns[idx], &tim)
683 || (got_year = is_year (columns[idx], &tim))))
684 idx++;
685 else
686 return 0; /* Neither time nor date */
689 * If the date is less than 6 months in the past, it is shown without year
690 * other dates in the past or future are shown with year but without time
691 * This does not check for years before 1900 ... I don't know, how
692 * to represent them at all
694 if (!got_year && local_time->tm_mon < 6
695 && local_time->tm_mon < tim.tm_mon
696 && tim.tm_mon - local_time->tm_mon >= 6)
698 tim.tm_year--;
700 if (l10n || (*t = mktime (&tim)) < 0)
701 *t = 0;
702 return idx;
706 vfs_parse_ls_lga (const char *p, struct stat *s, char **filename,
707 char **linkname)
709 int idx, idx2, num_cols;
710 int i;
711 char *p_copy = NULL;
712 char *t = NULL;
713 const char *line = p;
714 size_t skipped;
716 if (strncmp (p, "total", 5) == 0)
717 return 0;
719 if (!vfs_parse_filetype (p, &skipped, &s->st_mode))
720 goto error;
721 p += skipped;
723 if (*p == ' ') /* Notwell 4 */
724 p++;
725 if (*p == '[') {
726 if (strlen (p) <= 8 || p[8] != ']')
727 goto error;
728 /* Should parse here the Notwell permissions :) */
729 if (S_ISDIR (s->st_mode))
730 s->st_mode |=
731 (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP
732 | S_IXOTH);
733 else
734 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
735 p += 9;
736 } else {
737 size_t skipped;
738 mode_t perms;
740 if (!vfs_parse_fileperms (p, &skipped, &perms))
741 goto error;
742 p += skipped;
743 s->st_mode |= perms;
746 p_copy = g_strdup (p);
747 num_cols = vfs_split_text (p_copy);
749 s->st_nlink = atol (columns[0]);
750 if (s->st_nlink <= 0)
751 goto error;
753 if (!is_num (1))
754 s->st_uid = vfs_finduid (columns[1]);
755 else
756 s->st_uid = (uid_t) atol (columns[1]);
758 /* Mhm, the ls -lg did not produce a group field */
759 for (idx = 3; idx <= 5; idx++)
760 if (is_month (columns[idx], NULL) || is_week (columns[idx], NULL)
761 || is_dos_date (columns[idx])
762 || is_localized_month (columns[idx]))
763 break;
765 if (idx == 6
766 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
767 goto error;
769 /* We don't have gid */
770 if (idx == 3
771 || (idx == 4 && (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode))))
772 idx2 = 2;
773 else {
774 /* We have gid field */
775 if (is_num (2))
776 s->st_gid = (gid_t) atol (columns[2]);
777 else
778 s->st_gid = vfs_findgid (columns[2]);
779 idx2 = 3;
782 /* This is device */
783 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)) {
784 int maj, min;
786 /* Corner case: there is no whitespace(s) between maj & min */
787 if (!is_num (idx2) && idx2 == 2) {
788 if (!is_num (++idx2) || sscanf (columns[idx2], " %d,%d", &min, &min) != 2)
789 goto error;
790 } else {
791 if (!is_num (idx2) || sscanf (columns[idx2], " %d,", &maj) != 1)
792 goto error;
794 if (!is_num (++idx2) || sscanf (columns[idx2], " %d", &min) != 1)
795 goto error;
797 #ifdef HAVE_STRUCT_STAT_ST_RDEV
798 s->st_rdev = makedev (maj, min);
799 #endif
800 s->st_size = 0;
802 } else {
803 /* Common file size */
804 if (!is_num (idx2))
805 goto error;
807 #ifdef HAVE_ATOLL
808 s->st_size = (off_t) atoll (columns[idx2]);
809 #else
810 s->st_size = (off_t) atof (columns[idx2]);
811 #endif
812 #ifdef HAVE_STRUCT_STAT_ST_RDEV
813 s->st_rdev = 0;
814 #endif
817 idx = vfs_parse_filedate (idx, &s->st_mtime);
818 if (!idx)
819 goto error;
820 /* Use resulting time value */
821 s->st_atime = s->st_ctime = s->st_mtime;
822 /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
823 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
824 s->st_blksize = 512;
825 #endif
826 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
827 s->st_blocks = (s->st_size + 511) / 512;
828 #endif
830 for (i = idx + 1, idx2 = 0; i < num_cols; i++)
831 if (strcmp (columns[i], "->") == 0) {
832 idx2 = i;
833 break;
836 if (((S_ISLNK (s->st_mode) || (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
837 &&idx2) {
839 if (filename) {
840 *filename =
841 g_strndup (p + column_ptr[idx],
842 column_ptr[idx2] - column_ptr[idx] - 1);
844 if (linkname) {
845 t = g_strdup (p + column_ptr[idx2 + 1]);
846 *linkname = t;
848 } else {
849 /* Extract the filename from the string copy, not from the columns
850 * this way we have a chance of entering hidden directories like ". ."
852 if (filename) {
854 * filename = g_strdup (columns [idx++]);
857 t = g_strdup (p + column_ptr[idx]);
858 *filename = t;
860 if (linkname)
861 *linkname = NULL;
864 if (t) {
865 int p = strlen (t);
866 if ((--p > 0) && (t[p] == '\r' || t[p] == '\n'))
867 t[p] = 0;
868 if ((--p > 0) && (t[p] == '\r' || t[p] == '\n'))
869 t[p] = 0;
872 g_free (p_copy);
873 return 1;
875 error:
877 static int errorcount = 0;
879 if (++errorcount < 5) {
880 message (1, _("Cannot parse:"), "%s",
881 (p_copy && *p_copy) ? p_copy : line);
882 } else if (errorcount == 5)
883 message (1, MSG_ERROR,
884 _("More parsing errors will be ignored."));
887 g_free (p_copy);
888 return 0;
891 void
892 vfs_die (const char *m)
894 message (1, _("Internal error:"), "%s", m);
895 exit (1);
898 char *
899 vfs_get_password (const char *msg)
901 return input_dialog (msg, _("Password:"), MC_HISTORY_VFS_PASSWORD, INPUT_PASSWORD);