ls.c revision 327e696808910f8e49370079f8d7f4db8cbe3820
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
25static int listpath(const char *name, int flags);
26
27static 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
41static 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
69static 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
79static 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
89static int listfile_size(const char *path, int flags)
90{
91    struct stat s;
92
93    if (lstat(path, &s) < 0)
94        return -1;
95
96    /* blocks are 512 bytes, we want output to be KB */
97    printf("%lld %s\n", s.st_blocks / 2, path);
98    return 0;
99}
100
101static int listfile_long(const char *path, int flags)
102{
103    struct stat s;
104    char date[32];
105    char mode[16];
106    char user[16];
107    char group[16];
108    const char *name;
109
110    /* name is anything after the final '/', or the whole path if none*/
111    name = strrchr(path, '/');
112    if(name == 0) {
113        name = path;
114    } else {
115        name++;
116    }
117
118    if(lstat(path, &s) < 0) {
119        return -1;
120    }
121
122    mode2str(s.st_mode, mode);
123    user2str(s.st_uid, user);
124    group2str(s.st_gid, group);
125
126    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
127    date[31] = 0;
128
129// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
130// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
131
132    switch(s.st_mode & S_IFMT) {
133    case S_IFBLK:
134    case S_IFCHR:
135        printf("%s %-8s %-8s %3d, %3d %s %s\n",
136               mode, user, group,
137               (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
138               date, name);
139        break;
140    case S_IFREG:
141        printf("%s %-8s %-8s %8d %s %s\n",
142               mode, user, group, (int) s.st_size, date, name);
143        break;
144    case S_IFLNK: {
145        char linkto[256];
146        int len;
147
148        len = readlink(path, linkto, 256);
149        if(len < 0) return -1;
150
151        if(len > 255) {
152            linkto[252] = '.';
153            linkto[253] = '.';
154            linkto[254] = '.';
155            linkto[255] = 0;
156        } else {
157            linkto[len] = 0;
158        }
159
160        printf("%s %-8s %-8s          %s %s -> %s\n",
161               mode, user, group, date, name, linkto);
162        break;
163    }
164    default:
165        printf("%s %-8s %-8s          %s %s\n",
166               mode, user, group, date, name);
167
168    }
169    return 0;
170}
171
172static int listfile(const char *dirname, const char *filename, int flags)
173{
174    if ((flags & (LIST_LONG | LIST_SIZE)) == 0) {
175        printf("%s\n", filename);
176        return 0;
177    }
178
179    char tmp[4096];
180    const char* pathname = filename;
181
182    if (dirname != NULL) {
183        snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
184        pathname = tmp;
185    } else {
186        pathname = filename;
187    }
188
189    if ((flags & LIST_LONG) != 0) {
190        return listfile_long(pathname, flags);
191    } else /*((flags & LIST_SIZE) != 0)*/ {
192        return listfile_size(pathname, flags);
193    }
194}
195
196static int listdir(const char *name, int flags)
197{
198    char tmp[4096];
199    DIR *d;
200    struct dirent *de;
201
202    d = opendir(name);
203    if(d == 0) {
204        fprintf(stderr, "opendir failed, %s\n", strerror(errno));
205        return -1;
206    }
207
208    while((de = readdir(d)) != 0){
209        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
210        if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
211
212        listfile(name, de->d_name, flags);
213    }
214
215    if (flags & LIST_RECURSIVE) {
216        rewinddir(d);
217
218        while ((de = readdir(d)) != 0) {
219            struct stat s;
220            int err;
221
222            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
223                continue;
224            if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
225                continue;
226
227            if (!strcmp(name, "/"))
228                snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
229            else
230                snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
231
232            /*
233             * If the name ends in a '/', use stat() so we treat it like a
234             * directory even if it's a symlink.
235             */
236            if (tmp[strlen(tmp)-1] == '/')
237                err = stat(tmp, &s);
238            else
239                err = lstat(tmp, &s);
240
241            if (err < 0) {
242                perror(tmp);
243                closedir(d);
244                return -1;
245            }
246
247            if (S_ISDIR(s.st_mode)) {
248                printf("\n%s:\n", tmp);
249                listdir(tmp, flags);
250            }
251        }
252    }
253
254    closedir(d);
255    return 0;
256}
257
258static int listpath(const char *name, int flags)
259{
260    struct stat s;
261    int err;
262
263    /*
264     * If the name ends in a '/', use stat() so we treat it like a
265     * directory even if it's a symlink.
266     */
267    if (name[strlen(name)-1] == '/')
268        err = stat(name, &s);
269    else
270        err = lstat(name, &s);
271
272    if (err < 0) {
273        perror(name);
274        return -1;
275    }
276
277    if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
278        if (flags & LIST_RECURSIVE)
279            printf("\n%s:\n", name);
280        return listdir(name, flags);
281    } else {
282        /* yeah this calls stat() again*/
283        return listfile(NULL, name, flags);
284    }
285}
286
287int ls_main(int argc, char **argv)
288{
289    int flags = 0;
290    int listed = 0;
291
292    if(argc > 1) {
293        int i;
294        int err = 0;
295
296        for (i = 1; i < argc; i++) {
297            if (!strcmp(argv[i], "-l")) {
298                flags |= LIST_LONG;
299            } else if (!strcmp(argv[i], "-s")) {
300                flags |= LIST_SIZE;
301            } else if (!strcmp(argv[i], "-a")) {
302                flags |= LIST_ALL;
303            } else if (!strcmp(argv[i], "-R")) {
304                flags |= LIST_RECURSIVE;
305            } else if (!strcmp(argv[i], "-d")) {
306                flags |= LIST_DIRECTORIES;
307            } else {
308                listed++;
309                if(listpath(argv[i], flags) != 0) {
310                    err = EXIT_FAILURE;
311                }
312            }
313        }
314
315        if (listed  > 0) return err;
316    }
317
318    // list working directory if no files or directories were specified
319    return listpath(".", flags);
320}
321