• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mode2kind(unsigned mode)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 
mode2str(unsigned mode,char * out)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 
user2str(unsigned uid,char * out)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 
group2str(unsigned gid,char * out)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 
listfile_size(const char * path,int flags)89 static 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 
listfile_long(const char * path,int flags)101 static 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 
listfile(const char * dirname,const char * filename,int flags)172 static 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 
listdir(const char * name,int flags)196 static 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 
listpath(const char * name,int flags)258 static 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 
ls_main(int argc,char ** argv)287 int 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