source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-parse-ls.c @ 17128

Revision 17128, 13.2 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17127, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* gnome-vfs-parse-ls.c - Routines for parsing output from the `ls' command.
3
4   Copyright (C) 1999 Free Software Foundation
5
6   The Gnome Library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10
11   The Gnome Library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15
16   You should have received a copy of the GNU Library General Public
17   License along with the Gnome Library; see the file COPYING.LIB.  If not,
18   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.
20
21   Authors: 1995 Miguel de Icaza
22   1995 Jakub Jelinek
23   1998 Pavel Machek
24   1999 Cleanup by Ettore Perazzoli
25
26   finduid, findgid are from GNU tar.  */
27
28#include <config.h>
29#include "gnome-vfs-parse-ls.h"
30
31#include "gnome-vfs-private.h"
32#include "gnome-vfs.h"
33#include <glib.h>
34#include <grp.h>
35#include <pwd.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41#include <sys/types.h>
42#include <time.h>
43#include <unistd.h>
44
45#ifndef TUNMLEN
46#define TUNMLEN 256
47#endif
48#ifndef TGNMLEN
49#define TGNMLEN 256
50#endif
51
52
53/* FIXME bugzilla.eazel.com 1179:
54 * remove these globals.  */
55static gint saveuid = -993;
56static gchar saveuname[TUNMLEN];
57static gint my_uid = -993;
58
59static gint savegid = -993;
60static gchar savegname[TGNMLEN];
61static gint my_gid = -993;
62
63#define myuid   ( my_uid < 0? (my_uid = getuid ()): my_uid )
64#define mygid   ( my_gid < 0? (my_gid = getgid ()): my_gid )
65
66static gint finduid (gchar *uname)
67{
68        struct passwd *pw;
69       
70        if (uname[0] != saveuname[0]/* Quick test w/o proc call */
71            || 0 != strncmp (uname, saveuname, TUNMLEN)) {
72                strncpy (saveuname, uname, TUNMLEN);
73                pw = getpwnam (uname);
74                if (pw) {
75                        saveuid = pw->pw_uid;
76                } else {
77                        saveuid = myuid;
78                }
79        }
80        return saveuid;
81}
82
83static gint findgid (gchar *gname)
84{
85        struct group *gr;
86       
87        if (gname[0] != savegname[0]/* Quick test w/o proc call */
88            || 0 != strncmp (gname, savegname, TUNMLEN)) {
89                strncpy (savegname, gname, TUNMLEN);
90                gr = getgrnam (gname);
91                if (gr) {
92                        savegid = gr->gr_gid;
93                } else {
94                        savegid = mygid;
95                }
96        }
97        return savegid;
98}
99
100
101/* FIXME bugzilla.eazel.com 1188: This is ugly.  */
102#define MAXCOLS 30
103
104static gint
105vfs_split_text (gchar *p,
106                gchar *columns[],
107                gint column_ptr[])
108{
109        gchar *original = p;
110        gint  numcols;
111
112        for (numcols = 0; *p && numcols < MAXCOLS; numcols++) {
113                while (*p == ' ' || *p == '\r' || *p == '\n') {
114                        *p = 0;
115                        p++;
116                }
117                columns [numcols] = p;
118                column_ptr [numcols] = p - original;
119                while (*p && *p != ' ' && *p != '\r' && *p != '\n')
120                        p++;
121        }
122        return numcols;
123}
124
125static gint
126is_num (const gchar *s)
127{
128        if (!s || s[0] < '0' || s[0] > '9')
129                return 0;
130        return 1;
131}
132
133static gint
134is_dos_date (gchar *str)
135{
136        if (strlen (str) == 8 && str[2] == str[5] && strchr ("\\-/", (gint)str[2]) != NULL)
137                return 1;
138
139        return 0;
140}
141
142static gint
143is_week (gchar *str, struct tm *tim)
144{
145        static gchar *week = "SunMonTueWedThuFriSat";
146        gchar *pos;
147
148        if ((pos = strstr (week, str)) != NULL) {
149                if (tim != NULL)
150                        tim->tm_wday = (pos - week)/3;
151                return 1;
152        }
153        return 0;   
154}
155
156static gint
157is_month (const gchar *str, struct tm *tim)
158{
159        static gchar *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
160        gchar *pos;
161   
162        if ((pos = strstr (month, str)) != NULL) {
163                if (tim != NULL)
164                        tim->tm_mon = (pos - month)/3;
165                return 1;
166        }
167        return 0;
168}
169
170static gint
171is_time (const gchar *str, struct tm *tim)
172{
173        gchar *p, *p2;
174
175        if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
176                if (p != p2) {
177                        if (sscanf (str, "%2d:%2d:%2d",
178                                    &tim->tm_hour, &tim->tm_min, &tim->tm_sec)
179                            != 3)
180                                return 0;
181                }
182                else {
183                        if (sscanf (str, "%2d:%2d",
184                                    &tim->tm_hour, &tim->tm_min)
185                            != 2)
186                                return 0;
187                }
188        }
189        else
190                return 0;
191   
192        return 1;
193}
194
195static gint is_year (const gchar *str, struct tm *tim)
196{
197        glong year;
198
199        if (strchr (str,':'))
200                return 0;
201
202        if (strlen (str) != 4)
203                return 0;
204
205        if (sscanf (str, "%ld", &year) != 1)
206                return 0;
207
208        if (year < 1900 || year > 3000)
209                return 0;
210
211        tim->tm_year = (gint) (year - 1900);
212
213        return 1;
214}
215
216/*
217 * FIXME bugzilla.eazel.com 1182:
218 * this is broken. Consider following entry:
219 * -rwx------   1 root     root            1 Aug 31 10:04 2904 1234
220 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
221 */
222
223static gint
224vfs_parse_filetype (gchar c)
225{
226        switch (c) {
227        case 'd':
228                return S_IFDIR;
229        case 'b':
230                return S_IFBLK;
231        case 'c':
232                return S_IFCHR;
233        case 'l':
234                return S_IFLNK;
235        case 's':
236#ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty
237                    close. */
238                return S_IFSOCK;
239#endif
240        case 'p':
241                return S_IFIFO;
242        case 'm':
243        case 'n':               /* Don't know what these are :-) */
244        case '-':
245        case '?':
246                return S_IFREG;
247        default:
248                return -1;
249        }
250}
251
252static gint
253vfs_parse_filemode (const gchar *p)
254{
255        /* converts rw-rw-rw- into 0666 */
256        gint res = 0;
257
258        switch (*(p++)) {
259        case 'r':
260                res |= 0400; break;
261        case '-':
262                break;
263        default:
264                return -1;
265        }
266
267        switch (*(p++)) {
268        case 'w':
269                res |= 0200; break;
270        case '-':
271                break;
272        default:
273                return -1;
274        }
275
276        switch (*(p++)) {
277        case 'x':
278                res |= 0100; break;
279        case 's':
280                res |= 0100 | S_ISUID; break;
281        case 'S':
282                res |= S_ISUID; break;
283        case '-':
284                break;
285        default:
286                return -1;
287        }
288
289        switch (*(p++)) {
290        case 'r':
291                res |= 0040; break;
292        case '-':
293                break;
294        default:
295                return -1;
296        }
297
298        switch (*(p++)) {
299        case 'w':
300                res |= 0020; break;
301        case '-':
302                break;
303        default:
304                return -1;
305        }
306
307        switch (*(p++)) {
308        case 'x':
309                res |= 0010; break;
310        case 's':
311                res |= 0010 | S_ISGID; break;
312        case 'l':
313        /* Solaris produces these */
314        case 'S':
315                res |= S_ISGID; break;
316        case '-':
317                break;
318        default:
319                return -1;
320        }
321
322        switch (*(p++)) {
323        case 'r':
324                res |= 0004; break;
325        case '-':
326                break;
327        default:
328                return -1;
329        }
330
331        switch (*(p++)) {
332        case 'w':
333                res |= 0002; break;
334        case '-':
335                break;
336        default:
337                return -1;
338        }
339
340        switch (*(p++)) {
341        case 'x':
342                res |= 0001; break;
343        case 't':
344                res |= 0001 | S_ISVTX; break;
345        case 'T':
346                res |= S_ISVTX; break;
347        case '-':
348                break;
349        default:
350                return -1;
351        }
352
353        return res;
354}
355
356static gint
357vfs_parse_filedate (gint idx,
358                    gchar *columns[],
359                    time_t *t)
360{       /* This thing parses from idx in columns[] array */
361
362        gchar *p;
363        struct tm tim;
364        gint d[3];
365        gint    got_year = 0;
366        gint current_mon;
367        time_t now;
368   
369        /* Let's setup default time values */
370        now = time (NULL);
371        tim = *localtime (&now);
372        current_mon = tim.tm_mon;
373        tim.tm_hour = 0;
374        tim.tm_min  = 0;
375        tim.tm_sec  = 0;
376        tim.tm_isdst = -1; /* Let mktime () try to guess correct dst offset */
377   
378        p = columns [idx++];
379   
380        /* We eat weekday name in case of extfs */
381        if (is_week (p, &tim))
382                p = columns [idx++];
383
384        /* Month name */
385        if (is_month (p, &tim)) {
386                /* And we expect, it followed by day number */
387                if (is_num (columns[idx]))
388                        tim.tm_mday = (gint)atol (columns [idx++]);
389                else
390                        return 0; /* No day */
391
392        } else {
393                /* We usually expect:
394                   Mon DD hh:mm
395                   Mon DD  YYYY
396                   But in case of extfs we allow these date formats:
397                   Mon DD YYYY hh:mm
398                   Mon DD hh:mm YYYY
399                   Wek Mon DD hh:mm:ss YYYY
400                   MM-DD-YY hh:mm
401                   where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
402                   YYYY four digit year, hh, mm, ss two digit hour, minute
403                   or second. */
404
405                /* Here just this special case with MM-DD-YY */
406                if (is_dos_date (p)) {
407                        p[2] = p[5] = '-';
408           
409                        if (sscanf (p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3) {
410                                /*  We expect to get:
411                                    1. MM-DD-YY
412                                    2. DD-MM-YY
413                                    3. YY-MM-DD
414                                    4. YY-DD-MM  */
415               
416                                /* Hmm... maybe, next time :)*/
417               
418                                /* At last, MM-DD-YY */
419                                d[0]--; /* Months are zerobased */
420                                /* Y2K madness */
421                                if (d[2] < 70)
422                                        d[2] += 100;
423
424                                tim.tm_mon  = d[0];
425                                tim.tm_mday = d[1];
426                                tim.tm_year = d[2];
427                                got_year = 1;
428                        } else
429                                return 0; /* sscanf failed */
430                } else
431                        return 0; /* unsupported format */
432        }
433
434        /* Here we expect to find time and/or year */
435   
436        if (is_num (columns[idx])) {
437                if (is_time (columns[idx], &tim) || (got_year = is_year (columns[idx], &tim))) {
438                        idx++;
439
440                        /* This is a special case for ctime () or Mon DD YYYY hh:mm */
441                        if (is_num (columns[idx]) &&
442                           ((got_year = is_year (columns[idx], &tim)) || is_time (columns[idx], &tim)))
443                                idx++; /* time & year or reverse */
444                } /* only time or date */
445        }
446        else
447                return 0; /* Nor time or date */
448
449        /*
450         * If the date is less than 6 months in the past, it is shown without year
451         * other dates in the past or future are shown with year but without time
452         * This does not check for years before 1900 ... I don't know, how
453         * to represent them at all
454         */
455        if (!got_year &&
456            current_mon < 6 && current_mon < tim.tm_mon &&
457            tim.tm_mon - current_mon >= 6)
458
459                tim.tm_year--;
460
461        if ((*t = mktime (&tim)) < 0)
462                *t = 0;
463        return idx;
464}
465
466gint
467gnome_vfs_parse_ls_lga (const gchar *p,
468                        struct stat *s,
469                        gchar **filename,
470                        gchar **linkname)
471{
472        gchar *columns [MAXCOLS]; /* Points to the string in column n */
473        gint column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
474        gint idx, idx2, num_cols;
475        gint i;
476        gint nlink;
477        gchar *p_copy, *p_pristine;
478
479        if (strncmp (p, "total", 5) == 0)
480                return 0;
481
482        p_copy = g_strdup (p);
483        if ((i = vfs_parse_filetype (*(p++))) == -1)
484                goto error;
485
486        s->st_mode = i;
487        if (*p == ' ')  /* Notwell 4 */
488                p++;
489        if (*p == '[') {
490                if (strlen (p) <= 8 || p [8] != ']')
491                        goto error;
492                /* Should parse here the Notwell permissions :) */
493                if (S_ISDIR (s->st_mode))
494                        s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR
495                                       | S_IXUSR | S_IXGRP | S_IXOTH);
496                else
497                        s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
498                p += 9;
499        } else {
500                if ((i = vfs_parse_filemode (p)) == -1)
501                        goto error;
502                s->st_mode |= i;
503                p += 9;
504
505                /* This is for an extra ACL attribute (HP-UX) */
506                if (*p == '+')
507                        p++;
508        }
509
510        g_free (p_copy);
511        p_copy = g_strdup (p);
512        p_pristine = g_strdup (p);
513        num_cols = vfs_split_text (p_copy, columns, column_ptr);
514
515        nlink = atol (columns [0]);
516        if (nlink < 0)
517                goto error;
518
519        s->st_nlink = nlink;
520
521        if (!is_num (columns[1]))
522                s->st_uid = finduid (columns [1]);
523        else
524                s->st_uid = (uid_t) atol (columns [1]);
525
526        /* Mhm, the ls -lg did not produce a group field */
527        for (idx = 3; idx <= 5; idx++)
528                if (is_month (columns [idx], NULL)
529                    || is_week (columns [idx], NULL)
530                    || is_dos_date (columns[idx]))
531                        break;
532
533        if (idx == 6 || (idx == 5
534                         && !S_ISCHR (s->st_mode)
535                         && !S_ISBLK (s->st_mode)))
536                goto error;
537
538        /* We don't have gid */
539        if (idx == 3 || (idx == 4 && (S_ISCHR (s->st_mode)
540                                      || S_ISBLK (s->st_mode))))
541                idx2 = 2;
542        else {
543                /* We have gid field */
544                if (is_num (columns[2]))
545                        s->st_gid = (gid_t) atol (columns [2]);
546                else
547                        s->st_gid = findgid (columns [2]);
548                idx2 = 3;
549        }
550
551        /* This is device */
552        if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)) {
553                gint maj, min;
554       
555                if (!is_num (columns[idx2])
556                    || sscanf (columns [idx2], " %d,", &maj) != 1)
557                        goto error;
558       
559                if (!is_num (columns[++idx2])
560                    || sscanf (columns [idx2], " %d", &min) != 1)
561                        goto error;
562       
563#ifdef HAVE_ST_RDEV
564                s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
565#endif
566                s->st_size = 0;
567       
568        } else {
569                /* Common file size */
570                if (!is_num (columns[idx2]))
571                        goto error;
572       
573                s->st_size = (gsize) atol (columns [idx2]);
574#ifdef HAVE_ST_RDEV
575                s->st_rdev = 0;
576#endif
577        }
578
579        idx = vfs_parse_filedate (idx, columns, &s->st_mtime);
580        if (!idx)
581                goto error;
582        /* Use resulting time value */
583        s->st_atime = s->st_ctime = s->st_mtime;
584        s->st_dev = 0;
585        s->st_ino = 0;
586#ifdef HAVE_ST_BLKSIZE
587        s->st_blksize = 512;
588#endif
589#ifdef HAVE_ST_BLOCKS
590        s->st_blocks = (s->st_size + 511) / 512;
591#endif
592
593        for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
594                if (strcmp (columns [i], "->") == 0) {
595                        idx2 = i;
596                        break;
597                }
598   
599        if (((S_ISLNK (s->st_mode)
600              || (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink?
601                                                               (in extfs) */
602            && idx2) {
603                gint p;
604                gchar *s;
605           
606                if (filename) {
607                        s = g_strndup (p_copy + column_ptr [idx],
608                                       column_ptr [idx2] - column_ptr [idx] - 1);
609                        *filename = s;
610                }
611                if (linkname) {
612                        s = g_strdup (p_copy + column_ptr [idx2+1]);
613                        p = strlen (s);
614                        if (s [p-1] == '\r' || s [p-1] == '\n')
615                                s [p-1] = 0;
616                        if (s [p-2] == '\r' || s [p-2] == '\n')
617                                s [p-2] = 0;
618               
619                        *linkname = s;
620                }
621        } else {
622                /* Extract the filename from the string copy, not from the columns
623                 * this way we have a chance of entering hidden directories like ". ."
624                 */
625                if (filename) {
626                        /*
627                         *filename = g_strdup (columns [idx++]);
628                         */
629                        gint p;
630                        gchar *s;
631
632                        s = g_strdup (p_pristine + column_ptr [idx]);
633                        p = strcspn (s, "\r\n");
634                        s[p] = '\0';
635
636                        *filename = s;
637                }
638                if (linkname)
639                        *linkname = NULL;
640        }
641        g_free (p_copy);
642        g_free (p_pristine);
643        return 1;
644
645 error:
646        {
647                static gint errorcount = 0;
648
649                if (++errorcount < 5)
650                        g_warning (_("Could not parse: %s"), p_copy);
651                else if (errorcount == 5)
652                        g_warning (_("More parsing errors will be ignored."));
653        }
654
655        if (p_copy != p)                /* Carefull! */
656                g_free (p_copy);
657        return 0;
658}
Note: See TracBrowser for help on using the repository browser.