Back to index

avfs  1.0.1
parsels.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 1998  Miklos Szeredi <miklos@szeredi.hu>
00004     
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 
00008     PARSELS module
00009 
00010     This module is partly based on the 'vfs.c' module of 
00011     Midnight Commander VFS, by Miguel de Icaza, Jakub Jelinek and 
00012     Pavel Machek.
00013 */
00014 
00015 #include "parsels.h"
00016 #include "ugid.h"
00017 
00018 #include <string.h>
00019 #include <stdlib.h>
00020 #include <stdio.h>
00021 #include <unistd.h>
00022 
00023 #include "config.h"
00024 
00025 #define MAXCOLS             30
00026 
00027 struct columns {
00028     int idx;
00029     int num;
00030 
00031     char *cols[MAXCOLS];
00032     int   cptr[MAXCOLS];
00033 };
00034 
00035 #define CURR_COL(col) (col->cols[col->idx])
00036 #define INC_COL(col)  (col->cols[col->idx++])
00037 
00038 
00039 struct lscache {
00040     struct ugidcache *ugid;
00041     struct avtm currtim;
00042 };
00043 
00044 static void free_lscache(struct lscache *cache)
00045 {
00046     av_unref_obj(cache->ugid);
00047 }
00048 
00049 struct lscache *av_new_lscache()
00050 {
00051     struct lscache *cache;
00052 
00053     AV_NEW_OBJ(cache, free_lscache);
00054 
00055     cache->ugid = av_new_ugidcache();
00056     av_localtime(time(NULL), &cache->currtim);
00057 
00058     return cache;
00059 }
00060 
00061 static void split_text(char *p, struct columns *col)
00062 {
00063     char *original = p;
00064     int  numcols;
00065     for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
00066         while (*p == ' ' || *p == '\r' || *p == '\n'){
00067             *p = 0;
00068             p++;
00069         }
00070         col->cols [numcols] = p;
00071         col->cptr [numcols] = p - original;
00072         while (*p && *p != ' ' && *p != '\r' && *p != '\n')
00073             p++;
00074     }
00075     col->idx = 0;
00076     col->num = numcols;
00077 }
00078 
00079 static int is_num (struct columns *col)
00080 {
00081     if (!CURR_COL(col) || CURR_COL(col)[0] < '0' || CURR_COL(col)[0] > '9')
00082         return 0;
00083     return 1;
00084 }
00085 
00086 static int is_dos_date(const char *str)
00087 {
00088     int len;
00089 
00090     if (!str)
00091        return 0;
00092 
00093     len = strlen (str);
00094     if (len != 8 && len != 10)
00095        return 0;
00096 
00097     if (str[2] != str[5])
00098        return 0;
00099 
00100     if (!strchr ("\\-/", (int) str[2]))
00101        return 0;
00102 
00103     return 1;
00104 }
00105 
00106 static int is_iso_date(const char *str)
00107 {
00108     int len;
00109 
00110     if (!str)
00111        return 0;
00112 
00113     len = strlen (str);
00114     if (len != 10)
00115        return 0;
00116 
00117     if (str[4] != str[7])
00118        return 0;
00119 
00120     if (!strchr ("\\-/", (int) str[4]))
00121        return 0;
00122 
00123     return 1;
00124 }
00125 
00126 
00127 static int is_week(const char *str)
00128 {
00129     if(strstr("SunMonTueWedThuFriSat", str) != NULL) return 1;
00130 
00131     return 0;
00132 }
00133 
00134 static int is_month(const char *str, struct avtm *tim)
00135 {
00136     static const char *const month = "JanFebMarAprMayJunJulAugSepOctNovDec";
00137     char *pos;
00138     
00139     if((pos=strstr(month, str)) != NULL){
00140         if(tim != NULL)
00141             tim->mon = (pos - month)/3;
00142         return 1;
00143     }
00144     return 0;
00145 }
00146 
00147 static int is_time(const char *str, struct avtm *tim)
00148 {
00149     char *p, *p2;
00150   
00151     if ((p=strchr(str, ':')) && (p2=strrchr(str, ':'))) {
00152         if (p != p2) {
00153             if (sscanf (str, "%2d:%2d:%2d", &tim->hour, &tim->min, &tim->sec) != 3)
00154                 return 0;
00155         }
00156         else {
00157             if (sscanf (str, "%2d:%2d", &tim->hour, &tim->min) != 2)
00158                 return 0;
00159         }
00160     }
00161     else return 0;
00162   
00163     return 1;
00164 }
00165 
00166 static int is_year(const char *str, struct avtm *tim)
00167 {
00168     long year;
00169   
00170     if (strchr(str,':'))
00171         return (0);
00172   
00173     if (strlen(str)!=4)
00174         return (0);
00175   
00176     if (sscanf(str, "%ld", &year) != 1)
00177         return (0);
00178   
00179     if (year < 1900 || year > 3000)
00180         return (0);
00181   
00182     tim->year = (int) (year - 1900);
00183   
00184     return (1);
00185 }
00186 
00187 /*
00188  * FIXME: this is broken. Consider following entry:
00189  * -rwx------   1 root     root            1 Aug 31 10:04 2904 1234
00190  * where "2904 1234" is filename. Well, this code decodes it as year :-(.
00191  */
00192 
00193 static int parse_filetype (char c)
00194 {
00195     switch (c){
00196     case 'd': return AV_IFDIR; 
00197     case 'b': return AV_IFBLK;
00198     case 'c': return AV_IFCHR;
00199     case 'l': return AV_IFLNK;
00200     case 's': return AV_IFSOCK;
00201     case 'p': return AV_IFIFO;
00202     case 'm': 
00203     case 'n':        /* Don't know what these are :-) */
00204     case '-': 
00205     case '?': return AV_IFREG;
00206   
00207     default: return -1;
00208     }
00209 }
00210 
00211 /* converts rw-rw-rw- into 0666 */
00212 static int parse_filemode (const char *p)
00213 {      
00214     int res = 0;
00215 
00216     switch (*(p++)){
00217     case 'r': res |= 0400; break;
00218     case '-': break;
00219     default: return -1;
00220     }
00221 
00222     switch (*(p++)){
00223     case 'w': res |= 0200; break;
00224     case '-': break;
00225     default: return -1;
00226     }
00227 
00228     switch (*(p++)){
00229     case 'x': res |= 0100; break;
00230     case 's': res |= 0100 | AV_ISUID; break;
00231     case 'S': res |= AV_ISUID; break;
00232     case '-': break;
00233     default: return -1;
00234     }
00235 
00236     switch (*(p++)){
00237     case 'r': res |= 0040; break;
00238     case '-': break;
00239     default: return -1;
00240     }
00241 
00242     switch (*(p++)){
00243     case 'w': res |= 0020; break;
00244     case '-': break;
00245     default: return -1;
00246     }
00247   
00248     switch (*(p++)){
00249     case 'x': res |= 0010; break;
00250     case 's': res |= 0010 | AV_ISGID; break;
00251     case 'S': res |= AV_ISGID; break;
00252     case '-': break;
00253     default: return -1;
00254     }
00255 
00256     switch (*(p++)){
00257     case 'r': res |= 0004; break;
00258     case '-': break;
00259     default: return -1;
00260     }
00261 
00262     switch (*(p++)){
00263     case 'w': res |= 0002; break;
00264     case '-': break;
00265     default: return -1;
00266     }
00267 
00268     switch (*(p++)){
00269     case 'x': res |= 0001; break;
00270     case 't': res |= 0001 | AV_ISVTX; break;
00271     case 'T': res |= AV_ISVTX; break;
00272     case '-': break;
00273     default: return -1;
00274     }
00275   
00276     return res;
00277 }
00278 
00279 static avtime_t parse_filedate(struct columns *col, struct avtm *currtim)
00280 {      
00281     char *p;
00282     struct avtm tim;
00283     int d[3];
00284     int got_year = 0;
00285     avtime_t t;
00286 
00287     /* Let's setup default time values */
00288     tim.year = currtim->year;
00289     tim.mon  = currtim->mon;
00290     tim.day  = currtim->day;
00291     tim.hour = 0;
00292     tim.min  = 0;
00293     tim.sec  = 0;
00294     
00295     p = INC_COL(col);
00296     
00297     /* We eat weekday name in case of extfs */
00298     if(is_week(p)) p = INC_COL(col);
00299   
00300     /* Month name */
00301     if(is_month(p, &tim)){
00302         /* And we expect, it followed by day number */
00303         if (is_num (col))
00304             tim.day = (int) atol (INC_COL(col));
00305         else
00306             return -1; /* No day */
00307     
00308     } else {
00309         /* We usually expect:
00310            Mon DD hh:mm
00311            Mon DD  YYYY
00312            But in case of extfs we allow these date formats:
00313            Mon DD YYYY hh:mm
00314            Mon DD hh:mm YYYY
00315            Wek Mon DD hh:mm:ss YYYY
00316            MM-DD-YY hh:mm
00317            YYYY-MM-DD hh:mm (ISO 8601)
00318            where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
00319            YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
00320     
00321         /* Here just this special case with MM-DD-YY or MM-DD-YYYY */
00322         if (is_dos_date(p)){
00323             p[2] = p[5] = '-';
00324       
00325             if(sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
00326                 /*  We expect to get:
00327                     1. MM-DD-YY
00328                     2. DD-MM-YY
00329                     3. YY-MM-DD
00330                     4. YY-DD-MM  */
00331        
00332                 /* Hmm... maybe, next time :)*/
00333        
00334                 /* At last, MM-DD-YY or MM-DD-YYYY*/
00335              
00336                /* Months are zero based */
00337                if (d[0] > 0)
00338                   d[0]--;
00339 
00340               if (d[2] > 1900) {
00341                   d[2] -= 1900;
00342               } else {
00343                   /* Y2K madness */
00344                   if (d[2] < 70)
00345                      d[2] += 100;
00346               }
00347               
00348                 tim.mon   = d[0];
00349                 tim.day   = d[1];
00350                 tim.year = d[2];
00351                 got_year = 1;
00352             } else
00353                 return -1; /* sscanf failed */
00354         } else if (is_iso_date(p)) {
00355             if(sscanf(p, "%4d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
00356                 if(d[0] < 1900) return -1;
00357                 d[0] -= 1900;
00358                 
00359                 if(d[1] < 1 || d[1] > 12) return -1;
00360                 d[1]--; /* Months are zero based */
00361 
00362                 if(d[2] < 1 || d[2] > 31) return -1;
00363                 
00364                 tim.mon   = d[1];
00365                 tim.day   = d[2];
00366                 tim.year = d[0];
00367                 got_year = 1;
00368             } else
00369                 return -1; /* sscanf failed */
00370         } else
00371             return -1; /* unsupported format */
00372     }
00373   
00374     /* Here we expect to find time and/or year */
00375   
00376     if (is_num (col)) {
00377         if(is_time(CURR_COL(col), &tim) || 
00378            (got_year = is_year(CURR_COL(col), &tim))) {
00379             col->idx++;
00380 
00381             /* This is a special case for ctime() or Mon DD YYYY hh:mm */
00382             if(is_num (col)) {
00383                 if(got_year) {
00384                     if(is_time(CURR_COL(col), &tim))
00385                         col->idx++; /* year & time */
00386                 }
00387                 else if((got_year = is_year(CURR_COL(col), &tim)))
00388                     col->idx++; /* time & year */
00389             }
00390         } /* only time or date */
00391     }
00392     else 
00393         return -1; /* Nor time or date */
00394 
00395     /*
00396      * If the date is less than 6 months in the past, it is shown without year
00397      * other dates in the past or future are shown with year but without time
00398      * This does not check for years before 1900 ... I don't know, how
00399      * to represent them at all
00400      */
00401     if (!got_year &&
00402         currtim->mon < 6 && currtim->mon < tim.mon && 
00403         tim.mon - currtim->mon >= 6) tim.year--;
00404   
00405     t = av_mktime(&tim);
00406     if(t < 0) t = 0;
00407 
00408     return t;
00409 }
00410 
00411 int av_parse_ls(struct lscache *cache, const char *line,
00412                   struct avstat *stbuf, char **filename, char **linkname)
00413 {
00414     struct columns colstruct;
00415     struct columns *col = &colstruct;
00416     int i;
00417     int saveidx, lnkidx;
00418     char *p_copy = NULL;
00419     const char *lineorig = line;
00420 
00421     *linkname = NULL;
00422     *filename = NULL;
00423     
00424     if (strncmp (line, "total", 5) == 0)
00425         return 0;
00426 
00427     if ((i = parse_filetype(*(line++))) == -1)
00428         goto error;
00429   
00430     stbuf->mode = i;
00431     if (*line == ' ')       /* Notwell 4 */
00432         line++;
00433     if (*line == '['){
00434         if (strlen (line) <= 8 || line [8] != ']')
00435             goto error;
00436         /* Should parse here the Notwell permissions :) */
00437         if (AV_ISDIR (stbuf->mode))
00438             stbuf->mode |= 755;
00439         else
00440             stbuf->mode |= 644;
00441         line += 9;
00442     } else {
00443         if ((i = parse_filemode(line)) == -1)
00444             goto error;
00445         stbuf->mode |= i;
00446         line += 9;
00447     
00448         /* This is for an extra ACL attribute (HP-UX) */
00449         if (*line == '+')
00450             line++;
00451     }
00452 
00453     p_copy = av_strdup(line);
00454 
00455     split_text (p_copy, col);
00456 
00457   
00458     stbuf->nlink = atol (INC_COL(col));
00459     if (stbuf->nlink <= 0)
00460         goto error;
00461   
00462     if (!is_num (col))
00463         stbuf->uid = av_finduid (cache->ugid, CURR_COL(col), -1);
00464     else
00465         stbuf->uid = (uid_t) atol (CURR_COL(col));
00466   
00467     /* Mhm, the ls -lg did not produce a group field */
00468     for (col->idx = 3; col->idx <= 5; col->idx++) 
00469         if (is_month(CURR_COL(col), NULL) || 
00470             is_week(CURR_COL(col)) || 
00471             is_dos_date(CURR_COL(col)) ||
00472             is_iso_date(CURR_COL(col)))
00473             break;
00474 
00475     saveidx = col->idx;
00476     col->idx = 2;
00477 
00478   
00479     if (saveidx == 6 || 
00480         (saveidx == 5 && !AV_ISCHR(stbuf->mode) && !AV_ISBLK(stbuf->mode)))
00481         goto error;
00482 
00483     if (!(saveidx == 3 || 
00484           (saveidx == 4 && (AV_ISCHR(stbuf->mode) || AV_ISBLK (stbuf->mode))))) {
00485         /* We have gid field */
00486 
00487         if (is_num (col))
00488             stbuf->gid = (gid_t) atol (INC_COL(col));
00489         else
00490             stbuf->gid = av_findgid (cache->ugid, INC_COL(col), -1);
00491     }
00492   
00493     /* This is device */
00494     if (AV_ISCHR (stbuf->mode) || AV_ISBLK (stbuf->mode)){
00495         int maj, min;
00496        
00497         if (!is_num (col) || sscanf(INC_COL(col), " %d,", &maj) != 1)
00498             goto error;
00499     
00500         if (!is_num (col) || sscanf(INC_COL(col), " %d", &min) != 1)
00501             goto error;
00502        
00503         stbuf->rdev = av_mkdev(maj, min);
00504         stbuf->size = 0;
00505     
00506     } else {
00507         /* Common file size */
00508         if (!is_num (col))
00509             goto error;
00510     
00511 #ifdef HAVE_ATOLL
00512         stbuf->size = (avoff_t) atoll (INC_COL(col));
00513 #else
00514         stbuf->size = (avoff_t) atol (INC_COL(col));
00515 #endif
00516         stbuf->rdev = 0;
00517     }
00518   
00519     col->idx = saveidx;
00520   
00521     stbuf->mtime.nsec = 0;
00522     stbuf->mtime.sec = parse_filedate(col, &cache->currtim);
00523     if (stbuf->mtime.sec == -1)
00524         goto error;
00525 
00526     /* Use resulting time value */
00527     stbuf->atime = stbuf->ctime = stbuf->mtime;
00528     stbuf->dev = 0;
00529     stbuf->ino = 0;
00530     stbuf->blksize = 512;
00531     stbuf->blocks = AV_DIV(stbuf->size, 512);
00532 
00533     saveidx = col->idx;
00534     lnkidx = 0;
00535 
00536     for (col->idx ++; col->idx < col->num; col->idx++) 
00537         if (strcmp (CURR_COL(col), "->") == 0) {
00538             lnkidx = col->idx;
00539             break;
00540         }
00541   
00542     col->idx = saveidx;
00543 
00544     if (((AV_ISLNK (stbuf->mode) || 
00545           /* Maybe a hardlink? (in extfs) */
00546           (col->num == saveidx + 3 && stbuf->nlink > 1))) 
00547         && lnkidx){
00548         int p;
00549         int len;
00550         char *s;
00551   
00552         len = col->cptr[lnkidx] - col->cptr[col->idx] - 1;
00553         s = av_malloc(len + 1);
00554         strncpy(s, line + col->cptr[col->idx], len);
00555         s[len] = '\0';
00556         *filename = s;
00557     
00558         s = av_strdup (line + col->cptr[lnkidx + 1]);
00559         p = strlen (s);
00560         if (s [p-1] == '\r' || s [p-1] == '\n')
00561             s [p-1] = 0;
00562         if (s [p-2] == '\r' || s [p-2] == '\n')
00563             s [p-2] = 0;
00564     
00565         *linkname = s;
00566 
00567     } else {
00568         int p;
00569         char *s;
00570     
00571         s = av_strdup (line + col->cptr[col->idx]);
00572         p = strlen (s);
00573     
00574         if (p >= 1 && (s [p-1] == '\r' || s [p-1] == '\n'))
00575             s [p-1] = 0;
00576         if (p >= 2 && (s [p-2] == '\r' || s [p-2] == '\n'))
00577             s [p-2] = 0;
00578     
00579         *filename = s;
00580     }
00581     av_free (p_copy);
00582     return 1;
00583   
00584   error:
00585     av_free(p_copy);
00586     av_log(AVLOG_WARNING, "Could not parse %s", lineorig);
00587 
00588     return 0;
00589 }