blob: b221074ae4e4a20d49b13ff5b428d277a0b23489 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001#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
Andy McFaddenb33d3412009-04-08 19:25:35 -070018#define LIST_LONG (1 << 0)
19#define LIST_ALL (1 << 1)
20#define LIST_RECURSIVE (1 << 2)
21#define LIST_DIRECTORIES (1 << 3)
Andy McFadden327e6962009-08-18 11:10:03 -070022#define LIST_SIZE (1 << 4)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080023
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
Andy McFadden327e6962009-08-18 11:10:03 -070089static 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)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800102{
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
Andy McFadden327e6962009-08-18 11:10:03 -0700172static 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
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800196static 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;
Andy McFadden327e6962009-08-18 11:10:03 -0700211
212 listfile(name, de->d_name, flags);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800213 }
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
Andy McFadden327e6962009-08-18 11:10:03 -0700227 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);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800231
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
Andy McFaddenb33d3412009-04-08 19:25:35 -0700277 if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800278 if (flags & LIST_RECURSIVE)
279 printf("\n%s:\n", name);
280 return listdir(name, flags);
281 } else {
Andy McFadden327e6962009-08-18 11:10:03 -0700282 /* yeah this calls stat() again*/
283 return listfile(NULL, name, flags);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800284 }
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++) {
Andy McFadden327e6962009-08-18 11:10:03 -0700297 if (!strcmp(argv[i], "-l")) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800298 flags |= LIST_LONG;
Andy McFadden327e6962009-08-18 11:10:03 -0700299 } else if (!strcmp(argv[i], "-s")) {
300 flags |= LIST_SIZE;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800301 } else if (!strcmp(argv[i], "-a")) {
302 flags |= LIST_ALL;
303 } else if (!strcmp(argv[i], "-R")) {
304 flags |= LIST_RECURSIVE;
Andy McFaddenb33d3412009-04-08 19:25:35 -0700305 } else if (!strcmp(argv[i], "-d")) {
306 flags |= LIST_DIRECTORIES;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800307 } 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}