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.
4 Copyright (C) 1999 Free Software Foundation
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.
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.
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.
21 Authors: 1995 Miguel de Icaza
24 1999 Cleanup by Ettore Perazzoli
26 finduid, findgid are from GNU tar. */
29 #include "gnome-vfs-parse-ls.h"
31 #include "gnome-vfs-i18n.h"
32 #include <glib/gmem.h>
33 #include <glib/gmessages.h>
34 #include <glib/gstrfuncs.h>
35 #include <sys/types.h>
43 #include <sys/types.h>
55 /* FIXME bugzilla.eazel.com 1179:
56 * remove these globals. */
57 static int saveuid = -993;
58 static char saveuname[TUNMLEN];
59 static int my_uid = -993;
61 static int savegid = -993;
62 static char savegname[TGNMLEN];
63 static int my_gid = -993;
65 #define myuid ( my_uid < 0? (my_uid = getuid ()): my_uid )
66 #define mygid ( my_gid < 0? (my_gid = getgid ()): my_gid )
68 static int finduid (char *uname)
72 if (uname[0] != saveuname[0]/* Quick test w/o proc call */
73 || 0 != strncmp (uname, saveuname, TUNMLEN)) {
74 strncpy (saveuname, uname, TUNMLEN);
75 pw = getpwnam (uname);
85 static int findgid (char *gname)
89 if (gname[0] != savegname[0]/* Quick test w/o proc call */
90 || 0 != strncmp (gname, savegname, TUNMLEN)) {
91 strncpy (savegname, gname, TUNMLEN);
92 gr = getgrnam (gname);
103 /* FIXME bugzilla.eazel.com 1188: This is ugly. */
107 vfs_split_text (char *p,
114 for (numcols = 0; *p && numcols < MAXCOLS; numcols++) {
115 while (*p == ' ' || *p == '\r' || *p == '\n') {
119 columns [numcols] = p;
120 column_ptr [numcols] = p - original;
121 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
128 is_num (const char *s)
130 if (!s || s[0] < '0' || s[0] > '9')
136 is_dos_date (char *str)
138 if (strlen (str) == 8 && str[2] == str[5] && strchr ("\\-/", (int)str[2]) != NULL)
145 is_week (char *str, struct tm *tim)
147 static char *week = "SunMonTueWedThuFriSat";
150 if ((pos = strstr (week, str)) != NULL) {
152 tim->tm_wday = (pos - week)/3;
159 is_month (const char *str, struct tm *tim)
161 static char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
164 if ((pos = strstr (month, str)) != NULL) {
166 tim->tm_mon = (pos - month)/3;
173 is_time (const char *str, struct tm *tim)
177 if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
179 if (sscanf (str, "%2d:%2d:%2d",
180 &tim->tm_hour, &tim->tm_min, &tim->tm_sec)
185 if (sscanf (str, "%2d:%2d",
186 &tim->tm_hour, &tim->tm_min)
197 static int is_year (const char *str, struct tm *tim)
201 if (strchr (str,':'))
204 if (strlen (str) != 4)
207 if (sscanf (str, "%ld", &year) != 1)
210 if (year < 1900 || year > 3000)
213 tim->tm_year = (int) (year - 1900);
219 * FIXME bugzilla.eazel.com 1182:
220 * this is broken. Consider following entry:
221 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
222 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
226 vfs_parse_filetype (char c)
238 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty
245 case 'n': /* Don't know what these are :-) */
255 vfs_parse_filemode (const char *p)
257 /* converts rw-rw-rw- into 0666 */
282 res |= 0100 | S_ISUID; break;
284 res |= S_ISUID; break;
313 res |= 0010 | S_ISGID; break;
315 /* Solaris produces these */
317 res |= S_ISGID; break;
346 res |= 0001 | S_ISVTX; break;
348 res |= S_ISVTX; break;
359 vfs_parse_filedate (int idx,
362 { /* This thing parses from idx in columns[] array */
371 /* Let's setup default time values */
373 tim = *localtime (&now);
374 current_mon = tim.tm_mon;
378 tim.tm_isdst = -1; /* Let mktime () try to guess correct dst offset */
382 /* We eat weekday name in case of extfs */
383 if (is_week (p, &tim))
387 if (is_month (p, &tim)) {
388 /* And we expect, it followed by day number */
389 if (is_num (columns[idx]))
390 tim.tm_mday = (int)atol (columns [idx++]);
392 return 0; /* No day */
395 /* We usually expect:
398 But in case of extfs we allow these date formats:
401 Wek Mon DD hh:mm:ss YYYY
403 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
404 YYYY four digit year, hh, mm, ss two digit hour, minute
407 /* Here just this special case with MM-DD-YY */
408 if (is_dos_date (p)) {
411 if (sscanf (p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3) {
418 /* Hmm... maybe, next time :)*/
420 /* At last, MM-DD-YY */
421 d[0]--; /* Months are zerobased */
431 return 0; /* sscanf failed */
433 return 0; /* unsupported format */
436 /* Here we expect to find time and/or year */
438 if (is_num (columns[idx])) {
439 if (is_time (columns[idx], &tim) || (got_year = is_year (columns[idx], &tim))) {
442 /* This is a special case for ctime () or Mon DD YYYY hh:mm */
443 if (is_num (columns[idx]) &&
444 ((got_year = is_year (columns[idx], &tim)) || is_time (columns[idx], &tim)))
445 idx++; /* time & year or reverse */
446 } /* only time or date */
449 return 0; /* Nor time or date */
452 * If the date is less than 6 months in the past, it is shown without year
453 * other dates in the past or future are shown with year but without time
454 * This does not check for years before 1900 ... I don't know, how
455 * to represent them at all
458 current_mon < 6 && current_mon < tim.tm_mon &&
459 tim.tm_mon - current_mon >= 6)
463 if ((*t = mktime (&tim)) < 0)
469 gnome_vfs_parse_ls_lga (const char *p,
474 char *columns [MAXCOLS]; /* Points to the string in column n */
475 int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
476 int idx, idx2, num_cols;
479 char *p_copy, *p_pristine;
481 if (strncmp (p, "total", 5) == 0)
484 p_copy = g_strdup (p);
485 if ((i = vfs_parse_filetype (*(p++))) == -1)
489 if (*p == ' ') /* Notwell 4 */
492 if (strlen (p) <= 8 || p [8] != ']')
494 /* Should parse here the Notwell permissions :) */
495 if (S_ISDIR (s->st_mode))
496 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR
497 | S_IXUSR | S_IXGRP | S_IXOTH);
499 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
502 if ((i = vfs_parse_filemode (p)) == -1)
507 /* This is for an extra ACL attribute (HP-UX) */
513 p_copy = g_strdup (p);
514 p_pristine = g_strdup (p);
515 num_cols = vfs_split_text (p_copy, columns, column_ptr);
517 nlink = atol (columns [0]);
523 if (!is_num (columns[1]))
524 s->st_uid = finduid (columns [1]);
526 s->st_uid = (uid_t) atol (columns [1]);
528 /* Mhm, the ls -lg did not produce a group field */
529 for (idx = 3; idx <= 5; idx++)
530 if (is_month (columns [idx], NULL)
531 || is_week (columns [idx], NULL)
532 || is_dos_date (columns[idx]))
535 if (idx == 6 || (idx == 5
536 && !S_ISCHR (s->st_mode)
537 && !S_ISBLK (s->st_mode)))
540 /* We don't have gid */
541 if (idx == 3 || (idx == 4 && (S_ISCHR (s->st_mode)
542 || S_ISBLK (s->st_mode))))
545 /* We have gid field */
546 if (is_num (columns[2]))
547 s->st_gid = (gid_t) atol (columns [2]);
549 s->st_gid = findgid (columns [2]);
554 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)) {
557 if (!is_num (columns[idx2])
558 || sscanf (columns [idx2], " %d,", &maj) != 1)
561 if (!is_num (columns[++idx2])
562 || sscanf (columns [idx2], " %d", &min) != 1)
566 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
571 /* Common file size */
572 if (!is_num (columns[idx2]))
575 s->st_size = (gsize) atol (columns [idx2]);
581 idx = vfs_parse_filedate (idx, columns, &s->st_mtime);
584 /* Use resulting time value */
585 s->st_atime = s->st_ctime = s->st_mtime;
588 #ifdef HAVE_ST_BLKSIZE
591 #ifdef HAVE_ST_BLOCKS
592 s->st_blocks = (s->st_size + 511) / 512;
595 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
596 if (strcmp (columns [i], "->") == 0) {
601 if (((S_ISLNK (s->st_mode)
602 || (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink?
609 s = g_strndup (p_copy + column_ptr [idx],
610 column_ptr [idx2] - column_ptr [idx] - 1);
614 s = g_strdup (p_copy + column_ptr [idx2+1]);
616 if (s [p-1] == '\r' || s [p-1] == '\n')
618 if (s [p-2] == '\r' || s [p-2] == '\n')
624 /* Extract the filename from the string copy, not from the columns
625 * this way we have a chance of entering hidden directories like ". ."
629 *filename = g_strdup (columns [idx++]);
634 s = g_strdup (p_pristine + column_ptr [idx]);
635 p = strcspn (s, "\r\n");
649 static int errorcount = 0;
651 if (++errorcount < 5)
652 g_warning (_("Could not parse: %s"), p_copy);
653 else if (errorcount == 5)
654 g_warning (_("More parsing errors will be ignored."));
657 if (p_copy != p) /* Carefull! */