1 /*
2    Routines for parsing output from the 'ls' command.
3 
4    Copyright (C) 1988-2021
5    Free Software Foundation, Inc.
6 
7    Copyright (C) 1995, 1996 Miguel de Icaza
8 
9    Written by:
10    Miguel de Icaza, 1995, 1996
11    Slava Zanko <slavazanko@gmail.com>, 2011
12 
13    This file is part of the Midnight Commander.
14 
15    The Midnight Commander is free software: you can redistribute it
16    and/or modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation, either version 3 of the License,
18    or (at your option) any later version.
19 
20    The Midnight Commander is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23    GNU General Public License for more details.
24 
25    You should have received a copy of the GNU General Public License
26    along with this program.  If not, see <http://www.gnu.org/licenses/>.
27  */
28 
29 /**
30  * \file
31  * \brief Source: Utilities for VFS modules
32  * \author Miguel de Icaza
33  * \date 1995, 1996
34  */
35 
36 #include <config.h>
37 
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 
42 #include "lib/global.h"
43 #include "lib/unixcompat.h"     /* makedev */
44 #include "lib/widget.h"         /* message() */
45 
46 #include "utilvfs.h"
47 
48 /*** global variables ****************************************************************************/
49 
50 /*** file scope macro definitions ****************************************************************/
51 
52 /* Parsing code is used by ftpfs, fish and extfs */
53 #define MAXCOLS         30
54 
55 /*** file scope type declarations ****************************************************************/
56 
57 /*** file scope variables ************************************************************************/
58 
59 static char *columns[MAXCOLS];  /* Points to the string in column n */
60 static int column_ptr[MAXCOLS]; /* Index from 0 to the starting positions of the columns */
61 static size_t vfs_parce_ls_final_num_spaces = 0;
62 
63 /* --------------------------------------------------------------------------------------------- */
64 /*** file scope functions ************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
66 
67 static gboolean
is_num(int idx)68 is_num (int idx)
69 {
70     char *column = columns[idx];
71 
72     return (column != NULL && isdigit (column[0]));
73 }
74 
75 /* --------------------------------------------------------------------------------------------- */
76 /* Return TRUE for MM-DD-YY and MM-DD-YYYY */
77 
78 static gboolean
is_dos_date(const char * str)79 is_dos_date (const char *str)
80 {
81     size_t len;
82 
83     if (str == NULL)
84         return FALSE;
85 
86     len = strlen (str);
87     if (len != 8 && len != 10)
88         return FALSE;
89 
90     if (str[2] != str[5])
91         return FALSE;
92 
93     return (strchr ("\\-/", (int) str[2]) != NULL);
94 }
95 
96 /* --------------------------------------------------------------------------------------------- */
97 
98 static gboolean
is_week(const char * str,struct tm * tim)99 is_week (const char *str, struct tm *tim)
100 {
101     static const char *week = "SunMonTueWedThuFriSat";
102     const char *pos;
103 
104     if (str == NULL)
105         return FALSE;
106 
107     pos = strstr (week, str);
108     if (pos == NULL)
109         return FALSE;
110 
111     if (tim != NULL)
112         tim->tm_wday = (pos - week) / 3;
113 
114     return TRUE;
115 }
116 
117 /* --------------------------------------------------------------------------------------------- */
118 /**
119  * Check for possible locale's abbreviated month name (Jan..Dec).
120  * Any 3 bytes long string without digit, control and punctuation characters.
121  * isalpha() is locale specific, so it cannot be used if current
122  * locale is "C" and ftp server use Cyrillic.
123  * NB: It is assumed there are no whitespaces in month.
124  */
125 static gboolean
is_localized_month(const char * month)126 is_localized_month (const char *month)
127 {
128     int i;
129 
130     if (month == NULL)
131         return FALSE;
132 
133     for (i = 0;
134          i < 3 && *month != '\0' && !isdigit ((unsigned char) *month)
135          && !iscntrl ((unsigned char) *month) && !ispunct ((unsigned char) *month); i++, month++)
136         ;
137 
138     return (i == 3 && *month == '\0');
139 }
140 
141 /* --------------------------------------------------------------------------------------------- */
142 
143 static gboolean
is_time(const char * str,struct tm * tim)144 is_time (const char *str, struct tm *tim)
145 {
146     const char *p, *p2;
147 
148     if (str == NULL)
149         return FALSE;
150 
151     p = strchr (str, ':');
152     if (p == NULL)
153         return FALSE;
154 
155     p2 = strrchr (str, ':');
156     if (p2 == NULL)
157         return FALSE;
158 
159     if (p != p2)
160     {
161         if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
162             return FALSE;
163     }
164     else
165     {
166         if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
167             return FALSE;
168     }
169 
170     return TRUE;
171 }
172 
173 /* --------------------------------------------------------------------------------------------- */
174 
175 static gboolean
is_year(char * str,struct tm * tim)176 is_year (char *str, struct tm *tim)
177 {
178     long year;
179 
180     if (str == NULL)
181         return FALSE;
182 
183     if (strchr (str, ':') != NULL)
184         return FALSE;
185 
186     if (strlen (str) != 4)
187         return FALSE;
188 
189     /* cppcheck-suppress invalidscanf */
190     if (sscanf (str, "%ld", &year) != 1)
191         return FALSE;
192 
193     if (year < 1900 || year > 3000)
194         return FALSE;
195 
196     tim->tm_year = (int) (year - 1900);
197 
198     return TRUE;
199 }
200 
201 /* --------------------------------------------------------------------------------------------- */
202 /*** public functions ****************************************************************************/
203 /* --------------------------------------------------------------------------------------------- */
204 
205 gboolean
vfs_parse_filetype(const char * s,size_t * ret_skipped,mode_t * ret_type)206 vfs_parse_filetype (const char *s, size_t * ret_skipped, mode_t * ret_type)
207 {
208     mode_t type;
209 
210     switch (*s)
211     {
212     case 'd':
213         type = S_IFDIR;
214         break;
215     case 'b':
216         type = S_IFBLK;
217         break;
218     case 'c':
219         type = S_IFCHR;
220         break;
221     case 'l':
222         type = S_IFLNK;
223         break;
224 #ifdef S_IFSOCK
225     case 's':
226         type = S_IFSOCK;
227         break;
228 #else
229     case 's':
230         type = S_IFIFO;
231         break;
232 #endif
233 #ifdef S_IFDOOR                 /* Solaris door */
234     case 'D':
235         type = S_IFDOOR;
236         break;
237 #else
238     case 'D':
239         type = S_IFIFO;
240         break;
241 #endif
242     case 'p':
243         type = S_IFIFO;
244         break;
245 #ifdef S_IFNAM                  /* Special named files */
246     case 'n':
247         type = S_IFNAM;
248         break;
249 #else
250     case 'n':
251         type = S_IFREG;
252         break;
253 #endif
254     case 'm':                  /* Don't know what these are :-) */
255     case '-':
256     case '?':
257         type = S_IFREG;
258         break;
259     default:
260         return FALSE;
261     }
262 
263     *ret_type = type;
264 
265     if (ret_skipped != NULL)
266         *ret_skipped = 1;
267     return TRUE;
268 }
269 
270 /* --------------------------------------------------------------------------------------------- */
271 
272 gboolean
vfs_parse_fileperms(const char * s,size_t * ret_skipped,mode_t * ret_perms)273 vfs_parse_fileperms (const char *s, size_t * ret_skipped, mode_t * ret_perms)
274 {
275     const char *p = s;
276     mode_t perms = 0;
277 
278     switch (*p++)
279     {
280     case '-':
281         break;
282     case 'r':
283         perms |= S_IRUSR;
284         break;
285     default:
286         return FALSE;
287     }
288 
289     switch (*p++)
290     {
291     case '-':
292         break;
293     case 'w':
294         perms |= S_IWUSR;
295         break;
296     default:
297         return FALSE;
298     }
299 
300     switch (*p++)
301     {
302     case '-':
303         break;
304     case 'S':
305         perms |= S_ISUID;
306         break;
307     case 's':
308         perms |= S_IXUSR | S_ISUID;
309         break;
310     case 'x':
311         perms |= S_IXUSR;
312         break;
313     default:
314         return FALSE;
315     }
316 
317     switch (*p++)
318     {
319     case '-':
320         break;
321     case 'r':
322         perms |= S_IRGRP;
323         break;
324     default:
325         return FALSE;
326     }
327 
328     switch (*p++)
329     {
330     case '-':
331         break;
332     case 'w':
333         perms |= S_IWGRP;
334         break;
335     default:
336         return FALSE;
337     }
338 
339     switch (*p++)
340     {
341     case '-':
342         break;
343     case 'S':
344         perms |= S_ISGID;
345         break;
346     case 'l':
347         perms |= S_ISGID;
348         break;                  /* found on Solaris */
349     case 's':
350         perms |= S_IXGRP | S_ISGID;
351         break;
352     case 'x':
353         perms |= S_IXGRP;
354         break;
355     default:
356         return FALSE;
357     }
358 
359     switch (*p++)
360     {
361     case '-':
362         break;
363     case 'r':
364         perms |= S_IROTH;
365         break;
366     default:
367         return FALSE;
368     }
369 
370     switch (*p++)
371     {
372     case '-':
373         break;
374     case 'w':
375         perms |= S_IWOTH;
376         break;
377     default:
378         return FALSE;
379     }
380 
381     switch (*p++)
382     {
383     case '-':
384         break;
385     case 'T':
386         perms |= S_ISVTX;
387         break;
388     case 't':
389         perms |= S_IXOTH | S_ISVTX;
390         break;
391     case 'x':
392         perms |= S_IXOTH;
393         break;
394     default:
395         return FALSE;
396     }
397 
398     if (*p == '+')
399         /* ACLs on Solaris, HP-UX and others */
400         p++;
401 
402     if (ret_skipped != NULL)
403         *ret_skipped = p - s;
404     *ret_perms = perms;
405 
406     return TRUE;
407 }
408 
409 /* --------------------------------------------------------------------------------------------- */
410 
411 gboolean
vfs_parse_filemode(const char * s,size_t * ret_skipped,mode_t * ret_mode)412 vfs_parse_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode)
413 {
414     const char *p = s;
415     mode_t type, perms;
416     size_t skipped;
417 
418     if (!vfs_parse_filetype (p, &skipped, &type))
419         return FALSE;
420 
421     p += skipped;
422     if (!vfs_parse_fileperms (p, &skipped, &perms))
423         return FALSE;
424 
425     p += skipped;
426     *ret_skipped = p - s;
427     *ret_mode = type | perms;
428 
429     return TRUE;
430 }
431 
432 /* --------------------------------------------------------------------------------------------- */
433 
434 gboolean
vfs_parse_raw_filemode(const char * s,size_t * ret_skipped,mode_t * ret_mode)435 vfs_parse_raw_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode)
436 {
437     const char *p = s;
438     mode_t remote_type = 0, local_type, perms = 0;
439 
440     /* isoctal */
441     for (; *p >= '0' && *p <= '7'; p++)
442     {
443         perms *= 010;
444         perms += (*p - '0');
445     }
446 
447     if (*p++ != ' ')
448         return FALSE;
449 
450     for (; *p >= '0' && *p <= '7'; p++)
451     {
452         remote_type *= 010;
453         remote_type += (*p - '0');
454     }
455 
456     if (*p++ != ' ')
457         return FALSE;
458 
459     /* generated with:
460        $ perl -e 'use Fcntl ":mode";
461        my @modes = (S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFREG);
462        foreach $t (@modes) { printf ("%o\n", $t); };'
463        TODO: S_IFDOOR, S_IFIFO, S_IFSOCK (if supported by os)
464        (see vfs_parse_filetype)
465      */
466 
467     switch (remote_type)
468     {
469     case 020000:
470         local_type = S_IFCHR;
471         break;
472     case 040000:
473         local_type = S_IFDIR;
474         break;
475     case 060000:
476         local_type = S_IFBLK;
477         break;
478     case 0120000:
479         local_type = S_IFLNK;
480         break;
481     case 0100000:
482     default:                   /* don't know what is it */
483         local_type = S_IFREG;
484         break;
485     }
486 
487     *ret_skipped = p - s;
488     *ret_mode = local_type | perms;
489 
490     return TRUE;
491 }
492 
493 /* --------------------------------------------------------------------------------------------- */
494 
495 gboolean
vfs_parse_month(const char * str,struct tm * tim)496 vfs_parse_month (const char *str, struct tm * tim)
497 {
498     static const char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
499     const char *pos;
500 
501     if (str == NULL)
502         return FALSE;
503 
504     pos = strstr (month, str);
505     if (pos == NULL)
506         return FALSE;
507 
508     if (tim != NULL)
509         tim->tm_mon = (pos - month) / 3;
510 
511     return TRUE;
512 }
513 
514 /* --------------------------------------------------------------------------------------------- */
515 /** This function parses from idx in the columns[] array */
516 
517 int
vfs_parse_filedate(int idx,time_t * t)518 vfs_parse_filedate (int idx, time_t * t)
519 {
520     char *p;
521     struct tm tim;
522     int d[3];
523     gboolean got_year = FALSE;
524     gboolean l10n = FALSE;      /* Locale's abbreviated month name */
525     time_t current_time;
526     struct tm *local_time;
527 
528     /* Let's setup default time values */
529     current_time = time (NULL);
530     local_time = localtime (&current_time);
531     tim.tm_mday = local_time->tm_mday;
532     tim.tm_mon = local_time->tm_mon;
533     tim.tm_year = local_time->tm_year;
534 
535     tim.tm_hour = 0;
536     tim.tm_min = 0;
537     tim.tm_sec = 0;
538     tim.tm_isdst = -1;          /* Let mktime() try to guess correct dst offset */
539 
540     p = columns[idx++];
541 
542     /* We eat weekday name in case of extfs */
543     if (is_week (p, &tim))
544         p = columns[idx++];
545 
546     /*
547        ALLOWED DATE FORMATS
548 
549        We expect 3 fields max or we'll see oddities with certain file names.
550 
551        Formats that contain either year or time (the default 'ls' formats):
552 
553        * Mon DD hh:mm[:ss]
554        * Mon DD YYYY
555 
556        Formats that contain both year and time, to make it easier to write
557        extfs scripts:
558 
559        * MM-DD-YYYY hh:mm[:ss]
560        * MM-DD-YY hh:mm[:ss]
561 
562        ('/' and '\' can be used instead of '-'.)
563 
564        where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
565        YYYY four digit year, hh, mm, ss two digit hour, minute or second.
566 
567        (As for the "3 fields max" restriction: this prevents, for example, a
568        file name "13:48" from being considered part of a "Sep 19 2016" date
569        string preceding it.)
570      */
571 
572     /* Month name */
573     if (vfs_parse_month (p, &tim))
574     {
575         /* And we expect, it followed by day number */
576         if (!is_num (idx))
577             return 0;           /* No day */
578 
579         tim.tm_mday = (int) atol (columns[idx++]);
580 
581     }
582     else if (is_dos_date (p))
583     {
584         /* Case with MM-DD-YY or MM-DD-YYYY */
585         p[2] = p[5] = '-';
586 
587         /* cppcheck-suppress invalidscanf */
588         if (sscanf (p, "%2d-%2d-%d", &d[0], &d[1], &d[2]) != 3)
589             return 0;           /* sscanf failed */
590 
591         /* Months are zero based */
592         if (d[0] > 0)
593             d[0]--;
594 
595         if (d[2] > 1900)
596             d[2] -= 1900;
597         else if (d[2] < 70)
598             /* Y2K madness */
599             d[2] += 100;
600 
601         tim.tm_mon = d[0];
602         tim.tm_mday = d[1];
603         tim.tm_year = d[2];
604         got_year = TRUE;
605     }
606     else if (is_localized_month (p) && is_num (idx++))
607         /* Locale's abbreviated month name followed by day number */
608         l10n = TRUE;
609     else
610         return 0;               /* unsupported format */
611 
612     /* Here we expect to find time or year */
613     if (!is_num (idx)
614         || !(is_time (columns[idx], &tim) || (got_year = is_year (columns[idx], &tim))))
615         return 0;               /* Neither time nor date */
616 
617     idx++;
618 
619     /*
620      * If the date is less than 6 months in the past, it is shown without year
621      * other dates in the past or future are shown with year but without time
622      * This does not check for years before 1900 ... I don't know, how
623      * to represent them at all
624      */
625     if (!got_year && local_time->tm_mon < 6 && local_time->tm_mon < tim.tm_mon
626         && tim.tm_mon - local_time->tm_mon >= 6)
627         tim.tm_year--;
628 
629     *t = mktime (&tim);
630     if (l10n || (*t < 0))
631         *t = 0;
632 
633     return idx;
634 }
635 
636 /* --------------------------------------------------------------------------------------------- */
637 
638 int
vfs_split_text(char * p)639 vfs_split_text (char *p)
640 {
641     char *original = p;
642     int numcols;
643 
644     memset (columns, 0, sizeof (columns));
645 
646     for (numcols = 0; *p != '\0' && numcols < MAXCOLS; numcols++)
647     {
648         for (; *p == ' ' || *p == '\r' || *p == '\n'; p++)
649             *p = '\0';
650 
651         columns[numcols] = p;
652         column_ptr[numcols] = p - original;
653 
654         for (; *p != '\0' && *p != ' ' && *p != '\r' && *p != '\n'; p++)
655             ;
656     }
657 
658     return numcols;
659 }
660 
661 /* --------------------------------------------------------------------------------------------- */
662 
663 void
vfs_parse_ls_lga_init(void)664 vfs_parse_ls_lga_init (void)
665 {
666     vfs_parce_ls_final_num_spaces = 1;
667 }
668 
669 /* --------------------------------------------------------------------------------------------- */
670 
671 size_t
vfs_parse_ls_lga_get_final_spaces(void)672 vfs_parse_ls_lga_get_final_spaces (void)
673 {
674     return vfs_parce_ls_final_num_spaces;
675 }
676 
677 /* --------------------------------------------------------------------------------------------- */
678 
679 gboolean
vfs_parse_ls_lga(const char * p,struct stat * s,char ** filename,char ** linkname,size_t * num_spaces)680 vfs_parse_ls_lga (const char *p, struct stat * s, char **filename, char **linkname,
681                   size_t * num_spaces)
682 {
683     int idx, idx2, num_cols;
684     int i;
685     char *p_copy = NULL;
686     char *t = NULL;
687     const char *line = p;
688     size_t skipped;
689 
690     if (strncmp (p, "total", 5) == 0)
691         return FALSE;
692 
693     if (!vfs_parse_filetype (p, &skipped, &s->st_mode))
694         goto error;
695 
696     p += skipped;
697     if (*p == ' ')              /* Notwell 4 */
698         p++;
699     if (*p == '[')
700     {
701         if (strlen (p) <= 8 || p[8] != ']')
702             goto error;
703 
704         /* Should parse here the Notwell permissions :) */
705         if (S_ISDIR (s->st_mode))
706             s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
707         else
708             s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
709         p += 9;
710     }
711     else
712     {
713         size_t lc_skipped;
714         mode_t perms;
715 
716         if (!vfs_parse_fileperms (p, &lc_skipped, &perms))
717             goto error;
718 
719         p += lc_skipped;
720         s->st_mode |= perms;
721     }
722 
723     p_copy = g_strdup (p);
724     num_cols = vfs_split_text (p_copy);
725 
726     s->st_nlink = atol (columns[0]);
727     if (s->st_nlink <= 0)
728         goto error;
729 
730     if (!is_num (1))
731         s->st_uid = vfs_finduid (columns[1]);
732     else
733         s->st_uid = (uid_t) atol (columns[1]);
734 
735     /* Mhm, the ls -lg did not produce a group field */
736     for (idx = 3; idx <= 5; idx++)
737         if (vfs_parse_month (columns[idx], NULL) || is_week (columns[idx], NULL)
738             || is_dos_date (columns[idx]) || is_localized_month (columns[idx]))
739             break;
740 
741     if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
742         goto error;
743 
744     /* We don't have gid */
745     if (idx == 3 || (idx == 4 && (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode))))
746         idx2 = 2;
747     else
748     {
749         /* We have gid field */
750         if (is_num (2))
751             s->st_gid = (gid_t) atol (columns[2]);
752         else
753             s->st_gid = vfs_findgid (columns[2]);
754         idx2 = 3;
755     }
756 
757     /* This is device */
758     if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode))
759     {
760         int maj, min;
761 
762         /* Corner case: there is no whitespace(s) between maj & min */
763         if (!is_num (idx2) && idx2 == 2)
764         {
765             /* cppcheck-suppress invalidscanf */
766             if (!is_num (++idx2) || sscanf (columns[idx2], " %d,%d", &maj, &min) != 2)
767                 goto error;
768         }
769         else
770         {
771             /* cppcheck-suppress invalidscanf */
772             if (!is_num (idx2) || sscanf (columns[idx2], " %d,", &maj) != 1)
773                 goto error;
774 
775             /* cppcheck-suppress invalidscanf */
776             if (!is_num (++idx2) || sscanf (columns[idx2], " %d", &min) != 1)
777                 goto error;
778         }
779 #ifdef HAVE_STRUCT_STAT_ST_RDEV
780         s->st_rdev = makedev (maj, min);
781 #endif
782         s->st_size = 0;
783 
784     }
785     else
786     {
787         /* Common file size */
788         if (!is_num (idx2))
789             goto error;
790 
791         s->st_size = (off_t) g_ascii_strtoll (columns[idx2], NULL, 10);
792 #ifdef HAVE_STRUCT_STAT_ST_RDEV
793         s->st_rdev = 0;
794 #endif
795     }
796 
797     idx = vfs_parse_filedate (idx, &s->st_mtime);
798     if (idx == 0)
799         goto error;
800 
801     /* Use resulting time value */
802     s->st_atime = s->st_ctime = s->st_mtime;
803 #ifdef HAVE_STRUCT_STAT_ST_MTIM
804     s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
805 #endif
806 
807     /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
808 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
809     s->st_blksize = 512;
810 #endif
811     vfs_adjust_stat (s);
812 
813     if (num_spaces != NULL)
814     {
815         *num_spaces = column_ptr[idx] - column_ptr[idx - 1] - strlen (columns[idx - 1]);
816         if (DIR_IS_DOTDOT (columns[idx]))
817             vfs_parce_ls_final_num_spaces = *num_spaces;
818     }
819 
820     for (i = idx + 1, idx2 = 0; i < num_cols; i++)
821         if (strcmp (columns[i], "->") == 0)
822         {
823             idx2 = i;
824             break;
825         }
826 
827     if (((S_ISLNK (s->st_mode) || (num_cols == idx + 3 && s->st_nlink > 1)))    /* Maybe a hardlink? (in extfs) */
828         && idx2 != 0)
829     {
830         if (filename != NULL)
831             *filename = g_strndup (p + column_ptr[idx], column_ptr[idx2] - column_ptr[idx] - 1);
832 
833         if (linkname != NULL)
834         {
835             t = g_strdup (p + column_ptr[idx2 + 1]);
836             *linkname = t;
837         }
838     }
839     else
840     {
841         /* Extract the filename from the string copy, not from the columns
842          * this way we have a chance of entering hidden directories like ". ."
843          */
844         if (filename != NULL)
845         {
846             /* filename = g_strdup (columns [idx++]); */
847             t = g_strdup (p + column_ptr[idx]);
848             *filename = t;
849         }
850 
851         if (linkname != NULL)
852             *linkname = NULL;
853     }
854 
855     if (t != NULL)
856     {
857         size_t p2;
858 
859         p2 = strlen (t);
860         if (--p2 > 0 && (t[p2] == '\r' || t[p2] == '\n'))
861             t[p2] = '\0';
862         if (--p2 > 0 && (t[p2] == '\r' || t[p2] == '\n'))
863             t[p2] = '\0';
864     }
865 
866     g_free (p_copy);
867     return TRUE;
868 
869   error:
870     {
871         static int errorcount = 0;
872 
873         if (++errorcount < 5)
874             message (D_ERROR, _("Cannot parse:"), "%s",
875                      (p_copy != NULL && *p_copy != '\0') ? p_copy : line);
876         else if (errorcount == 5)
877             message (D_ERROR, MSG_ERROR, _("More parsing errors will be ignored."));
878     }
879 
880     g_free (p_copy);
881     return FALSE;
882 }
883 
884 /* --------------------------------------------------------------------------------------------- */
885