ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-parse-ls.c
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-i18n.h"
32 #include <glib/gmem.h>
33 #include <glib/gmessages.h>
34 #include <glib/gstrfuncs.h>
35 #include <sys/types.h>
36 #include <grp.h>
37 #include <pwd.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44 #include <time.h>
45 #include <unistd.h>
46
47 #ifndef TUNMLEN 
48 #define TUNMLEN 256
49 #endif
50 #ifndef TGNMLEN
51 #define TGNMLEN 256
52 #endif
53
54 \f
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;
60
61 static int savegid = -993;
62 static char savegname[TGNMLEN];
63 static int my_gid = -993;
64
65 #define myuid   ( my_uid < 0? (my_uid = getuid ()): my_uid )
66 #define mygid   ( my_gid < 0? (my_gid = getgid ()): my_gid )
67
68 static int finduid (char *uname)
69 {
70         struct passwd *pw;
71         
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);
76                 if (pw) {
77                         saveuid = pw->pw_uid;
78                 } else {
79                         saveuid = myuid;
80                 }
81         }
82         return saveuid;
83 }
84
85 static int findgid (char *gname)
86 {
87         struct group *gr;
88         
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);
93                 if (gr) {
94                         savegid = gr->gr_gid;
95                 } else {
96                         savegid = mygid;
97                 }
98         }
99         return savegid;
100 }
101
102 \f
103 /* FIXME bugzilla.eazel.com 1188: This is ugly.  */
104 #define MAXCOLS 30
105
106 static int
107 vfs_split_text (char *p,
108                 char *columns[],
109                 int column_ptr[])
110 {
111         char *original = p;
112         int  numcols;
113
114         for (numcols = 0; *p && numcols < MAXCOLS; numcols++) {
115                 while (*p == ' ' || *p == '\r' || *p == '\n') {
116                         *p = 0;
117                         p++;
118                 }
119                 columns [numcols] = p;
120                 column_ptr [numcols] = p - original;
121                 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
122                         p++;
123         }
124         return numcols;
125 }
126
127 static int
128 is_num (const char *s)
129 {
130         if (!s || s[0] < '0' || s[0] > '9')
131                 return 0;
132         return 1;
133 }
134
135 static int
136 is_dos_date (char *str)
137 {
138         if (strlen (str) == 8 && str[2] == str[5] && strchr ("\\-/", (int)str[2]) != NULL)
139                 return 1;
140
141         return 0;
142 }
143
144 static int
145 is_week (char *str, struct tm *tim)
146 {
147         static char *week = "SunMonTueWedThuFriSat";
148         char *pos;
149
150         if ((pos = strstr (week, str)) != NULL) {
151                 if (tim != NULL)
152                         tim->tm_wday = (pos - week)/3;
153                 return 1;
154         }
155         return 0;    
156 }
157
158 static int
159 is_month (const char *str, struct tm *tim)
160 {
161         static char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
162         char *pos;
163     
164         if ((pos = strstr (month, str)) != NULL) {
165                 if (tim != NULL)
166                         tim->tm_mon = (pos - month)/3;
167                 return 1;
168         }
169         return 0;
170 }
171
172 static int
173 is_time (const char *str, struct tm *tim)
174 {
175         char *p, *p2;
176
177         if ((p = strchr (str, ':')) && (p2 = strrchr (str, ':'))) {
178                 if (p != p2) {
179                         if (sscanf (str, "%2d:%2d:%2d",
180                                     &tim->tm_hour, &tim->tm_min, &tim->tm_sec)
181                             != 3)
182                                 return 0;
183                 }
184                 else {
185                         if (sscanf (str, "%2d:%2d",
186                                     &tim->tm_hour, &tim->tm_min)
187                             != 2)
188                                 return 0;
189                 }
190         }
191         else 
192                 return 0;
193     
194         return 1;
195 }
196
197 static int is_year (const char *str, struct tm *tim)
198 {
199         long year;
200
201         if (strchr (str,':'))
202                 return 0;
203
204         if (strlen (str) != 4)
205                 return 0;
206
207         if (sscanf (str, "%ld", &year) != 1)
208                 return 0;
209
210         if (year < 1900 || year > 3000)
211                 return 0;
212
213         tim->tm_year = (int) (year - 1900);
214
215         return 1;
216 }
217
218 /*
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 :-(.
223  */
224
225 static int
226 vfs_parse_filetype (char c)
227 {
228         switch (c) {
229         case 'd':
230                 return S_IFDIR; 
231         case 'b':
232                 return S_IFBLK;
233         case 'c':
234                 return S_IFCHR;
235         case 'l':
236                 return S_IFLNK;
237         case 's':
238 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty
239                     close. */
240                 return S_IFSOCK;
241 #endif
242         case 'p':
243                 return S_IFIFO;
244         case 'm':
245         case 'n':               /* Don't know what these are :-) */
246         case '-':
247         case '?':
248                 return S_IFREG;
249         default:
250                 return -1;
251         }
252 }
253
254 static int
255 vfs_parse_filemode (const char *p)
256 {
257         /* converts rw-rw-rw- into 0666 */
258         int res = 0;
259
260         switch (*(p++)) {
261         case 'r':
262                 res |= 0400; break;
263         case '-':
264                 break;
265         default:
266                 return -1;
267         }
268
269         switch (*(p++)) {
270         case 'w':
271                 res |= 0200; break;
272         case '-':
273                 break;
274         default:
275                 return -1;
276         }
277
278         switch (*(p++)) {
279         case 'x':
280                 res |= 0100; break;
281         case 's':
282                 res |= 0100 | S_ISUID; break;
283         case 'S':
284                 res |= S_ISUID; break;
285         case '-':
286                 break;
287         default:
288                 return -1;
289         }
290
291         switch (*(p++)) {
292         case 'r':
293                 res |= 0040; break;
294         case '-':
295                 break;
296         default:
297                 return -1;
298         }
299
300         switch (*(p++)) {
301         case 'w':
302                 res |= 0020; break;
303         case '-':
304                 break;
305         default:
306                 return -1;
307         }
308
309         switch (*(p++)) {
310         case 'x':
311                 res |= 0010; break;
312         case 's':
313                 res |= 0010 | S_ISGID; break;
314         case 'l':
315         /* Solaris produces these */
316         case 'S':
317                 res |= S_ISGID; break;
318         case '-':
319                 break;
320         default:
321                 return -1;
322         }
323
324         switch (*(p++)) {
325         case 'r':
326                 res |= 0004; break;
327         case '-':
328                 break;
329         default:
330                 return -1;
331         }
332
333         switch (*(p++)) {
334         case 'w':
335                 res |= 0002; break;
336         case '-':
337                 break;
338         default:
339                 return -1;
340         }
341
342         switch (*(p++)) {
343         case 'x':
344                 res |= 0001; break;
345         case 't':
346                 res |= 0001 | S_ISVTX; break;
347         case 'T':
348                 res |= S_ISVTX; break;
349         case '-':
350                 break;
351         default:
352                 return -1;
353         }
354
355         return res;
356 }
357
358 static int
359 vfs_parse_filedate (int idx,
360                     char *columns[],
361                     time_t *t)
362 {       /* This thing parses from idx in columns[] array */
363
364         char *p;
365         struct tm tim;
366         int d[3];
367         int     got_year = 0;
368         int current_mon;
369         time_t now;
370     
371         /* Let's setup default time values */
372         now = time (NULL);
373         tim = *localtime (&now);
374         current_mon = tim.tm_mon;
375         tim.tm_hour = 0;
376         tim.tm_min  = 0;
377         tim.tm_sec  = 0;
378         tim.tm_isdst = -1; /* Let mktime () try to guess correct dst offset */
379     
380         p = columns [idx++];
381     
382         /* We eat weekday name in case of extfs */
383         if (is_week (p, &tim))
384                 p = columns [idx++];
385
386         /* Month name */
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++]);
391                 else
392                         return 0; /* No day */
393
394         } else {
395                 /* We usually expect:
396                    Mon DD hh:mm
397                    Mon DD  YYYY
398                    But in case of extfs we allow these date formats:
399                    Mon DD YYYY hh:mm
400                    Mon DD hh:mm YYYY
401                    Wek Mon DD hh:mm:ss YYYY
402                    MM-DD-YY hh:mm
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
405                    or second. */
406
407                 /* Here just this special case with MM-DD-YY */
408                 if (is_dos_date (p)) {
409                         p[2] = p[5] = '-';
410             
411                         if (sscanf (p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3) {
412                                 /*  We expect to get:
413                                     1. MM-DD-YY
414                                     2. DD-MM-YY
415                                     3. YY-MM-DD
416                                     4. YY-DD-MM  */
417                 
418                                 /* Hmm... maybe, next time :)*/
419                 
420                                 /* At last, MM-DD-YY */
421                                 d[0]--; /* Months are zerobased */
422                                 /* Y2K madness */
423                                 if (d[2] < 70)
424                                         d[2] += 100;
425
426                                 tim.tm_mon  = d[0];
427                                 tim.tm_mday = d[1];
428                                 tim.tm_year = d[2];
429                                 got_year = 1;
430                         } else
431                                 return 0; /* sscanf failed */
432                 } else
433                         return 0; /* unsupported format */
434         }
435
436         /* Here we expect to find time and/or year */
437     
438         if (is_num (columns[idx])) {
439                 if (is_time (columns[idx], &tim) || (got_year = is_year (columns[idx], &tim))) {
440                         idx++;
441
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 */
447         }
448         else 
449                 return 0; /* Nor time or date */
450
451         /*
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
456          */
457         if (!got_year &&
458             current_mon < 6 && current_mon < tim.tm_mon && 
459             tim.tm_mon - current_mon >= 6)
460
461                 tim.tm_year--;
462
463         if ((*t = mktime (&tim)) < 0)
464                 *t = 0;
465         return idx;
466 }
467
468 int
469 gnome_vfs_parse_ls_lga (const char *p,
470                         struct stat *s,
471                         char **filename,
472                         char **linkname)
473 {
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;
477         int i;
478         int nlink;
479         char *p_copy, *p_pristine;
480
481         if (strncmp (p, "total", 5) == 0)
482                 return 0;
483
484         p_copy = g_strdup (p);
485         if ((i = vfs_parse_filetype (*(p++))) == -1)
486                 goto error;
487
488         s->st_mode = i;
489         if (*p == ' ')  /* Notwell 4 */
490                 p++;
491         if (*p == '[') {
492                 if (strlen (p) <= 8 || p [8] != ']')
493                         goto error;
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);
498                 else
499                         s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
500                 p += 9;
501         } else {
502                 if ((i = vfs_parse_filemode (p)) == -1)
503                         goto error;
504                 s->st_mode |= i;
505                 p += 9;
506
507                 /* This is for an extra ACL attribute (HP-UX) */
508                 if (*p == '+')
509                         p++;
510         }
511
512         g_free (p_copy);
513         p_copy = g_strdup (p);
514         p_pristine = g_strdup (p);
515         num_cols = vfs_split_text (p_copy, columns, column_ptr);
516
517         nlink = atol (columns [0]);
518         if (nlink < 0)
519                 goto error;
520
521         s->st_nlink = nlink;
522
523         if (!is_num (columns[1]))
524                 s->st_uid = finduid (columns [1]);
525         else
526                 s->st_uid = (uid_t) atol (columns [1]);
527
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]))
533                         break;
534
535         if (idx == 6 || (idx == 5
536                          && !S_ISCHR (s->st_mode)
537                          && !S_ISBLK (s->st_mode)))
538                 goto error;
539
540         /* We don't have gid */ 
541         if (idx == 3 || (idx == 4 && (S_ISCHR (s->st_mode)
542                                       || S_ISBLK (s->st_mode))))
543                 idx2 = 2;
544         else { 
545                 /* We have gid field */
546                 if (is_num (columns[2]))
547                         s->st_gid = (gid_t) atol (columns [2]);
548                 else
549                         s->st_gid = findgid (columns [2]);
550                 idx2 = 3;
551         }
552
553         /* This is device */
554         if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)) {
555                 int maj, min;
556         
557                 if (!is_num (columns[idx2])
558                     || sscanf (columns [idx2], " %d,", &maj) != 1)
559                         goto error;
560         
561                 if (!is_num (columns[++idx2])
562                     || sscanf (columns [idx2], " %d", &min) != 1)
563                         goto error;
564         
565 #ifdef HAVE_ST_RDEV
566                 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
567 #endif
568                 s->st_size = 0;
569         
570         } else {
571                 /* Common file size */
572                 if (!is_num (columns[idx2]))
573                         goto error;
574         
575                 s->st_size = (gsize) atol (columns [idx2]);
576 #ifdef HAVE_ST_RDEV
577                 s->st_rdev = 0;
578 #endif
579         }
580
581         idx = vfs_parse_filedate (idx, columns, &s->st_mtime);
582         if (!idx)
583                 goto error;
584         /* Use resulting time value */
585         s->st_atime = s->st_ctime = s->st_mtime;
586         s->st_dev = 0;
587         s->st_ino = 0;
588 #ifdef HAVE_ST_BLKSIZE
589         s->st_blksize = 512;
590 #endif
591 #ifdef HAVE_ST_BLOCKS
592         s->st_blocks = (s->st_size + 511) / 512;
593 #endif
594
595         for (i = idx + 1, idx2 = 0; i < num_cols; i++ ) 
596                 if (strcmp (columns [i], "->") == 0) {
597                         idx2 = i;
598                         break;
599                 }
600     
601         if (((S_ISLNK (s->st_mode) 
602               || (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink?
603                                                                (in extfs) */
604             && idx2) {
605                 int p;
606                 char *s;
607             
608                 if (filename) {
609                         s = g_strndup (p_copy + column_ptr [idx],
610                                        column_ptr [idx2] - column_ptr [idx] - 1);
611                         *filename = s;
612                 }
613                 if (linkname) {
614                         s = g_strdup (p_copy + column_ptr [idx2+1]);
615                         p = strlen (s);
616                         if (s [p-1] == '\r' || s [p-1] == '\n')
617                                 s [p-1] = 0;
618                         if (s [p-2] == '\r' || s [p-2] == '\n')
619                                 s [p-2] = 0;
620                 
621                         *linkname = s;
622                 }
623         } else {
624                 /* Extract the filename from the string copy, not from the columns
625                  * this way we have a chance of entering hidden directories like ". ."
626                  */
627                 if (filename) {
628                         /* 
629                          *filename = g_strdup (columns [idx++]);
630                          */
631                         int p;
632                         char *s;
633
634                         s = g_strdup (p_pristine + column_ptr [idx]);
635                         p = strcspn (s, "\r\n");
636                         s[p] = '\0';
637
638                         *filename = s;
639                 }
640                 if (linkname)
641                         *linkname = NULL;
642         }
643         g_free (p_copy);
644         g_free (p_pristine);
645         return 1;
646
647  error:
648         {
649                 static int errorcount = 0;
650
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."));
655         }
656
657         if (p_copy != p)                /* Carefull! */
658                 g_free (p_copy);
659         return 0;
660 }