blob: ec5606f55d2ce1597ed7b2bf29b7800a6c404ad2 [file] [log] [blame]
Rob Landley3d8685b2012-02-13 21:26:27 -06001/* vi: set sw=4 ts=4:
2 *
3 * ls.c - list files
4 *
5 * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
6 *
Rob Landleyda5aa3a2012-02-13 21:27:50 -06007 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
Rob Landley3d8685b2012-02-13 21:26:27 -06008
Andre Renaude8a9df42012-03-03 18:17:49 -06009USE_LS(NEWTOY(ls, "AnRlF1a", TOYFLAG_BIN))
Rob Landley3d8685b2012-02-13 21:26:27 -060010
11config LS
12 bool "ls"
Rob Landleyb0dcd662012-02-26 22:04:37 -060013 default n
Rob Landley3d8685b2012-02-13 21:26:27 -060014 help
Andre Renaude8a9df42012-03-03 18:17:49 -060015 usage: ls [-lFaA1] [directory...]
Rob Landley3d8685b2012-02-13 21:26:27 -060016 list files
17
Rob Landley3d8685b2012-02-13 21:26:27 -060018 -1 list one file per line
Andre Renaude8a9df42012-03-03 18:17:49 -060019 -a list all files
20 -A list all files except . and ..
21 -F append a character as a file type indicator
Rob Landleyda5aa3a2012-02-13 21:27:50 -060022 -l show full details for each file
Rob Landley3d8685b2012-02-13 21:26:27 -060023*/
24
25#include "toys.h"
26
27#define FLAG_a 1
28#define FLAG_1 2
29#define FLAG_F 4
Rob Landleyda5aa3a2012-02-13 21:27:50 -060030#define FLAG_l 8
Rob Landley4797bc22012-02-18 15:19:00 -060031#define FLAG_R 16
Andre Renaud94d673d2012-02-21 20:48:52 -060032#define FLAG_n 32
Andre Renaude8a9df42012-03-03 18:17:49 -060033#define FLAG_A 64
Rob Landley3d8685b2012-02-13 21:26:27 -060034
35static int dir_filter(const struct dirent *d)
36{
Andre Renaude8a9df42012-03-03 18:17:49 -060037 /* Skip over all '.*' entries, unless -a is given */
38 if (!(toys.optflags & FLAG_a)) {
39 /* -A means show everything except the . & .. entries */
40 if (toys.optflags & FLAG_A) {
41 if (strcmp(d->d_name, ".") == 0 ||
42 strcmp(d->d_name, "..") == 0)
43 return 0;
44 } else if (d->d_name[0] == '.')
Rob Landley3d8685b2012-02-13 21:26:27 -060045 return 0;
Andre Renaude8a9df42012-03-03 18:17:49 -060046 }
Rob Landley3d8685b2012-02-13 21:26:27 -060047 return 1;
48}
49
50static void do_ls(int fd, char *name)
51{
52 struct dirent **entries;
53 int nentries;
54 int i;
55 int maxwidth = -1;
Rob Landleyda5aa3a2012-02-13 21:27:50 -060056 int ncolumns = 1;
Rob Landley4797bc22012-02-18 15:19:00 -060057 struct dirent file_dirent;
58 struct dirent *file_direntp;
Rob Landley3d8685b2012-02-13 21:26:27 -060059
Rob Landleyda5aa3a2012-02-13 21:27:50 -060060 if (!name || strcmp(name, "-") == 0)
Rob Landley3d8685b2012-02-13 21:26:27 -060061 name = ".";
62
Rob Landley4797bc22012-02-18 15:19:00 -060063 if (toys.optflags & FLAG_R)
64 xprintf("\n%s:\n", name);
65
Rob Landleyda5aa3a2012-02-13 21:27:50 -060066 /* Get all the files in this directory */
Rob Landley3d8685b2012-02-13 21:26:27 -060067 nentries = scandir(name, &entries, dir_filter, alphasort);
Rob Landley4797bc22012-02-18 15:19:00 -060068 if (nentries < 0) {
69 /* We've just selected a single file, so create a single-length list */
70 /* FIXME: This means that ls *.x results in a whole bunch of single
71 * listings, not one combined listing.
72 */
73 if (errno == ENOTDIR) {
74 nentries = 1;
75 strcpy(file_dirent.d_name, name);
76 file_direntp = &file_dirent;
77 entries = &file_direntp;
78 } else
79 perror_exit("ls: cannot access %s'", name);
80 }
Rob Landley3d8685b2012-02-13 21:26:27 -060081
82
83 /* Determine the widest entry so we can flow them properly */
84 if (!(toys.optflags & FLAG_1)) {
85 int columns;
86 char *columns_str;
87
88 for (i = 0; i < nentries; i++) {
89 struct dirent *ent = entries[i];
90 int width;
91
92 width = strlen(ent->d_name);
93 if (width > maxwidth)
94 maxwidth = width;
95 }
96 /* We always want at least a single space for each entry */
97 maxwidth++;
98 if (toys.optflags & FLAG_F)
99 maxwidth++;
100
101 columns_str = getenv("COLUMNS");
102 columns = columns_str ? atoi(columns_str) : 80;
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600103 ncolumns = maxwidth ? columns / maxwidth : 1;
Rob Landley3d8685b2012-02-13 21:26:27 -0600104 }
105
106 for (i = 0; i < nentries; i++) {
107 struct dirent *ent = entries[i];
108 int len = strlen(ent->d_name);
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600109 struct stat st;
110 int stat_valid = 0;
Rob Landley3d8685b2012-02-13 21:26:27 -0600111
Rob Landley4797bc22012-02-18 15:19:00 -0600112 sprintf(toybuf, "%s/%s", name, ent->d_name);
113
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600114 /* Provide the ls -l long output */
115 if (toys.optflags & FLAG_l) {
116 char type;
117 char timestamp[64];
118 struct tm mtime;
119
Rob Landley4797bc22012-02-18 15:19:00 -0600120 if (lstat(toybuf, &st))
121 perror_exit("Can't stat %s", toybuf);
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600122 stat_valid = 1;
123 if (S_ISDIR(st.st_mode))
124 type = 'd';
125 else if (S_ISCHR(st.st_mode))
126 type = 'c';
127 else if (S_ISBLK(st.st_mode))
128 type = 'b';
129 else if (S_ISLNK(st.st_mode))
130 type = 'l';
131 else
132 type = '-';
133
134 xprintf("%c%c%c%c%c%c%c%c%c%c ", type,
135 (st.st_mode & S_IRUSR) ? 'r' : '-',
136 (st.st_mode & S_IWUSR) ? 'w' : '-',
137 (st.st_mode & S_IXUSR) ? 'x' : '-',
138 (st.st_mode & S_IRGRP) ? 'r' : '-',
139 (st.st_mode & S_IWGRP) ? 'w' : '-',
140 (st.st_mode & S_IXGRP) ? 'x' : '-',
141 (st.st_mode & S_IROTH) ? 'r' : '-',
142 (st.st_mode & S_IWOTH) ? 'w' : '-',
143 (st.st_mode & S_IXOTH) ? 'x' : '-');
144
145 xprintf("%2d ", st.st_nlink);
Andre Renaud94d673d2012-02-21 20:48:52 -0600146 if (toys.optflags & FLAG_n) {
147 xprintf("%4d ", st.st_uid);
148 xprintf("%4d ", st.st_gid);
149 } else {
150 struct passwd *pwd = getpwuid(st.st_uid);
151 struct group *grp = getgrgid(st.st_gid);
152 if (!pwd)
153 xprintf("%4d ", st.st_uid);
154 else
155 xprintf("%-10s ", pwd->pw_name);
156 if (!grp)
157 xprintf("%4d ", st.st_gid);
158 else
159 xprintf("%-10s ", grp->gr_name);
160 }
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600161 if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
162 xprintf("%3d, %3d ", major(st.st_rdev), minor(st.st_rdev));
163 else
Andre Renaud94d673d2012-02-21 20:48:52 -0600164 xprintf("%12lld ", st.st_size);
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600165
166 localtime_r(&st.st_mtime, &mtime);
167
168 strftime(timestamp, sizeof(timestamp), "%b %e %H:%M", &mtime);
169 xprintf("%s ", timestamp);
170 }
171
172 xprintf("%s", ent->d_name);
173
174 /* Append the file-type indicator character */
175 if (toys.optflags & FLAG_F) {
176 if (!stat_valid) {
Rob Landley4797bc22012-02-18 15:19:00 -0600177 if (lstat(toybuf, &st))
178 perror_exit("Can't stat %s", toybuf);
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600179 stat_valid = 1;
Rob Landley3d8685b2012-02-13 21:26:27 -0600180 }
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600181 if (S_ISDIR(st.st_mode)) {
182 xprintf("/");
183 len++;
184 } else if (S_ISREG(st.st_mode) &&
Rob Landley3d8685b2012-02-13 21:26:27 -0600185 (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600186 xprintf("*");
187 len++;
188 } else if (S_ISLNK(st.st_mode)) {
189 xprintf("@");
Rob Landley3d8685b2012-02-13 21:26:27 -0600190 len++;
191 }
192 }
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600193 if (toys.optflags & FLAG_1) {
194 xprintf("\n");
195 } else {
Rob Landley3d8685b2012-02-13 21:26:27 -0600196 if (i % ncolumns == ncolumns - 1)
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600197 xprintf("\n");
Rob Landley3d8685b2012-02-13 21:26:27 -0600198 else
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600199 xprintf("%*s", maxwidth - len, "");
Rob Landley3d8685b2012-02-13 21:26:27 -0600200 }
201 }
202 /* Make sure we put at a trailing new line in */
203 if (!(toys.optflags & FLAG_1) && (i % ncolumns))
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600204 xprintf("\n");
Rob Landley4797bc22012-02-18 15:19:00 -0600205
206 if (toys.optflags & FLAG_R) {
207 for (i = 0; i < nentries; i++) {
208 struct dirent *ent = entries[i];
209 struct stat st;
210 char dirname[PATH_MAX];
211
212 sprintf(dirname, "%s/%s", name, ent->d_name);
213 if (lstat(dirname, &st))
214 perror_exit("Can't stat %s", dirname);
215 if (S_ISDIR(st.st_mode))
216 do_ls(0, dirname);
217 }
218 }
Rob Landley3d8685b2012-02-13 21:26:27 -0600219}
220
221void ls_main(void)
222{
Andre Renaud94d673d2012-02-21 20:48:52 -0600223 /* If the output is not a TTY, then just do one-file per line
224 * This makes ls easier to use with other command line tools (grep/awk etc...)
225 */
226 if (!isatty(fileno(stdout)))
227 toys.optflags |= FLAG_1;
Rob Landleyda5aa3a2012-02-13 21:27:50 -0600228 /* Long output must be one-file per line */
229 if (toys.optflags & FLAG_l)
230 toys.optflags |= FLAG_1;
Rob Landley3d8685b2012-02-13 21:26:27 -0600231 loopfiles(toys.optargs, do_ls);
232}