Home | History | Annotate | Download | only in toolbox
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <sys/types.h>
      5 #include <dirent.h>
      6 #include <errno.h>
      7 
      8 #include <sys/stat.h>
      9 #include <unistd.h>
     10 #include <time.h>
     11 
     12 #include <pwd.h>
     13 #include <grp.h>
     14 
     15 #include <linux/kdev_t.h>
     16 
     17 // bits for flags argument
     18 #define LIST_LONG           (1 << 0)
     19 #define LIST_ALL            (1 << 1)
     20 #define LIST_RECURSIVE      (1 << 2)
     21 #define LIST_DIRECTORIES    (1 << 3)
     22 #define LIST_SIZE           (1 << 4)
     23 
     24 // fwd
     25 static int listpath(const char *name, int flags);
     26 
     27 static char mode2kind(unsigned mode)
     28 {
     29     switch(mode & S_IFMT){
     30     case S_IFSOCK: return 's';
     31     case S_IFLNK: return 'l';
     32     case S_IFREG: return '-';
     33     case S_IFDIR: return 'd';
     34     case S_IFBLK: return 'b';
     35     case S_IFCHR: return 'c';
     36     case S_IFIFO: return 'p';
     37     default: return '?';
     38     }
     39 }
     40 
     41 static void mode2str(unsigned mode, char *out)
     42 {
     43     *out++ = mode2kind(mode);
     44 
     45     *out++ = (mode & 0400) ? 'r' : '-';
     46     *out++ = (mode & 0200) ? 'w' : '-';
     47     if(mode & 04000) {
     48         *out++ = (mode & 0100) ? 's' : 'S';
     49     } else {
     50         *out++ = (mode & 0100) ? 'x' : '-';
     51     }
     52     *out++ = (mode & 040) ? 'r' : '-';
     53     *out++ = (mode & 020) ? 'w' : '-';
     54     if(mode & 02000) {
     55         *out++ = (mode & 010) ? 's' : 'S';
     56     } else {
     57         *out++ = (mode & 010) ? 'x' : '-';
     58     }
     59     *out++ = (mode & 04) ? 'r' : '-';
     60     *out++ = (mode & 02) ? 'w' : '-';
     61     if(mode & 01000) {
     62         *out++ = (mode & 01) ? 't' : 'T';
     63     } else {
     64         *out++ = (mode & 01) ? 'x' : '-';
     65     }
     66     *out = 0;
     67 }
     68 
     69 static void user2str(unsigned uid, char *out)
     70 {
     71     struct passwd *pw = getpwuid(uid);
     72     if(pw) {
     73         strcpy(out, pw->pw_name);
     74     } else {
     75         sprintf(out, "%d", uid);
     76     }
     77 }
     78 
     79 static void group2str(unsigned gid, char *out)
     80 {
     81     struct group *gr = getgrgid(gid);
     82     if(gr) {
     83         strcpy(out, gr->gr_name);
     84     } else {
     85         sprintf(out, "%d", gid);
     86     }
     87 }
     88 
     89 static int show_total_size(const char *dirname, DIR *d, int flags)
     90 {
     91     struct dirent *de;
     92     char tmp[1024];
     93     struct stat s;
     94     int sum = 0;
     95 
     96     /* run through the directory and sum up the file block sizes */
     97     while ((de = readdir(d)) != 0) {
     98         if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
     99             continue;
    100         if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
    101             continue;
    102 
    103         if (strcmp(dirname, "/") == 0)
    104             snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
    105         else
    106             snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
    107 
    108         if (lstat(tmp, &s) < 0) {
    109             fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
    110             rewinddir(d);
    111             return -1;
    112         }
    113 
    114         sum += s.st_blocks / 2;
    115     }
    116 
    117     printf("total %d\n", sum);
    118     rewinddir(d);
    119     return 0;
    120 }
    121 
    122 static int listfile_size(const char *path, const char *filename, int flags)
    123 {
    124     struct stat s;
    125 
    126     if (lstat(path, &s) < 0) {
    127         fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
    128         return -1;
    129     }
    130 
    131     /* blocks are 512 bytes, we want output to be KB */
    132     printf("%lld %s\n", s.st_blocks / 2, filename);
    133     return 0;
    134 }
    135 
    136 static int listfile_long(const char *path, int flags)
    137 {
    138     struct stat s;
    139     char date[32];
    140     char mode[16];
    141     char user[16];
    142     char group[16];
    143     const char *name;
    144 
    145     /* name is anything after the final '/', or the whole path if none*/
    146     name = strrchr(path, '/');
    147     if(name == 0) {
    148         name = path;
    149     } else {
    150         name++;
    151     }
    152 
    153     if(lstat(path, &s) < 0) {
    154         return -1;
    155     }
    156 
    157     mode2str(s.st_mode, mode);
    158     user2str(s.st_uid, user);
    159     group2str(s.st_gid, group);
    160 
    161     strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
    162     date[31] = 0;
    163 
    164 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
    165 // MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
    166 
    167     switch(s.st_mode & S_IFMT) {
    168     case S_IFBLK:
    169     case S_IFCHR:
    170         printf("%s %-8s %-8s %3d, %3d %s %s\n",
    171                mode, user, group,
    172                (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
    173                date, name);
    174         break;
    175     case S_IFREG:
    176         printf("%s %-8s %-8s %8d %s %s\n",
    177                mode, user, group, (int) s.st_size, date, name);
    178         break;
    179     case S_IFLNK: {
    180         char linkto[256];
    181         int len;
    182 
    183         len = readlink(path, linkto, 256);
    184         if(len < 0) return -1;
    185 
    186         if(len > 255) {
    187             linkto[252] = '.';
    188             linkto[253] = '.';
    189             linkto[254] = '.';
    190             linkto[255] = 0;
    191         } else {
    192             linkto[len] = 0;
    193         }
    194 
    195         printf("%s %-8s %-8s          %s %s -> %s\n",
    196                mode, user, group, date, name, linkto);
    197         break;
    198     }
    199     default:
    200         printf("%s %-8s %-8s          %s %s\n",
    201                mode, user, group, date, name);
    202 
    203     }
    204     return 0;
    205 }
    206 
    207 static int listfile(const char *dirname, const char *filename, int flags)
    208 {
    209     if ((flags & (LIST_LONG | LIST_SIZE)) == 0) {
    210         printf("%s\n", filename);
    211         return 0;
    212     }
    213 
    214     char tmp[4096];
    215     const char* pathname = filename;
    216 
    217     if (dirname != NULL) {
    218         snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
    219         pathname = tmp;
    220     } else {
    221         pathname = filename;
    222     }
    223 
    224     if ((flags & LIST_LONG) != 0) {
    225         return listfile_long(pathname, flags);
    226     } else /*((flags & LIST_SIZE) != 0)*/ {
    227         return listfile_size(pathname, filename, flags);
    228     }
    229 }
    230 
    231 static int listdir(const char *name, int flags)
    232 {
    233     char tmp[4096];
    234     DIR *d;
    235     struct dirent *de;
    236 
    237     d = opendir(name);
    238     if(d == 0) {
    239         fprintf(stderr, "opendir failed, %s\n", strerror(errno));
    240         return -1;
    241     }
    242 
    243     if ((flags & LIST_SIZE) != 0) {
    244         show_total_size(name, d, flags);
    245     }
    246 
    247     while((de = readdir(d)) != 0){
    248         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
    249         if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
    250 
    251         listfile(name, de->d_name, flags);
    252     }
    253 
    254     if (flags & LIST_RECURSIVE) {
    255         rewinddir(d);
    256 
    257         while ((de = readdir(d)) != 0) {
    258             struct stat s;
    259             int err;
    260 
    261             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
    262                 continue;
    263             if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
    264                 continue;
    265 
    266             if (!strcmp(name, "/"))
    267                 snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
    268             else
    269                 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
    270 
    271             /*
    272              * If the name ends in a '/', use stat() so we treat it like a
    273              * directory even if it's a symlink.
    274              */
    275             if (tmp[strlen(tmp)-1] == '/')
    276                 err = stat(tmp, &s);
    277             else
    278                 err = lstat(tmp, &s);
    279 
    280             if (err < 0) {
    281                 perror(tmp);
    282                 closedir(d);
    283                 return -1;
    284             }
    285 
    286             if (S_ISDIR(s.st_mode)) {
    287                 printf("\n%s:\n", tmp);
    288                 listdir(tmp, flags);
    289             }
    290         }
    291     }
    292 
    293     closedir(d);
    294     return 0;
    295 }
    296 
    297 static int listpath(const char *name, int flags)
    298 {
    299     struct stat s;
    300     int err;
    301 
    302     /*
    303      * If the name ends in a '/', use stat() so we treat it like a
    304      * directory even if it's a symlink.
    305      */
    306     if (name[strlen(name)-1] == '/')
    307         err = stat(name, &s);
    308     else
    309         err = lstat(name, &s);
    310 
    311     if (err < 0) {
    312         perror(name);
    313         return -1;
    314     }
    315 
    316     if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
    317         if (flags & LIST_RECURSIVE)
    318             printf("\n%s:\n", name);
    319         return listdir(name, flags);
    320     } else {
    321         /* yeah this calls stat() again*/
    322         return listfile(NULL, name, flags);
    323     }
    324 }
    325 
    326 int ls_main(int argc, char **argv)
    327 {
    328     int flags = 0;
    329     int listed = 0;
    330 
    331     if(argc > 1) {
    332         int i;
    333         int err = 0;
    334 
    335         for (i = 1; i < argc; i++) {
    336             if (!strcmp(argv[i], "-l")) {
    337                 flags |= LIST_LONG;
    338             } else if (!strcmp(argv[i], "-s")) {
    339                 flags |= LIST_SIZE;
    340             } else if (!strcmp(argv[i], "-a")) {
    341                 flags |= LIST_ALL;
    342             } else if (!strcmp(argv[i], "-R")) {
    343                 flags |= LIST_RECURSIVE;
    344             } else if (!strcmp(argv[i], "-d")) {
    345                 flags |= LIST_DIRECTORIES;
    346             } else {
    347                 listed++;
    348                 if(listpath(argv[i], flags) != 0) {
    349                     err = EXIT_FAILURE;
    350                 }
    351             }
    352         }
    353 
    354         if (listed  > 0) return err;
    355     }
    356 
    357     // list working directory if no files or directories were specified
    358     return listpath(".", flags);
    359 }
    360