Unix Tools

Implementations of common Unix command-line utilities written in C.


unix-tools / ls.c

ls.c raw | download
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200809L
3#include <sys/stat.h>
4#include <dirent.h>
5#include <fcntl.h>
6#include <unistd.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <pwd.h>
14#include <grp.h>
15#include <time.h>
16
17struct entry {
18 char *name;
19 struct stat st;
20};
21
22static int dflag, lflag, nflag, pflag, rflag, sflag, tflag, uflag, Fflag;
23
24static int
25compar(const void *a, const void *b)
26{
27 const struct entry *left = a, *right = b;
28 int result;
29 if (tflag) {
30 time_t x = uflag ? left->st.st_atime : left->st.st_mtime;
31 time_t y = uflag ? right->st.st_atime : right->st.st_mtime;
32 result = x > y ? -1 : x < y ? 1 : strcmp(left->name, right->name);
33 } else result = strcmp(left->name, right->name);
34 return rflag ? -result : result;
35}
36
37static void
38format(const struct stat *st, const char *name, const char *prefix)
39{
40 if (sflag) printf("%ju ", (uintmax_t)st->st_size / 1024 + (st->st_size % 1024 != 0));
41 if (lflag) {
42 char mode[] = "----------", date[64] = "?";
43 const char *bits = "rwxrwxrwx";
44 struct passwd *user = getpwuid(st->st_uid);
45 struct group *group = getgrgid(st->st_gid);
46 time_t stamp = uflag ? st->st_atime : st->st_mtime;
47 struct tm *tm = localtime(&stamp);
48 int i;
49 if (S_ISDIR(st->st_mode)) mode[0] = 'd';
50 else if (S_ISLNK(st->st_mode)) mode[0] = 'l';
51 else if (S_ISFIFO(st->st_mode)) mode[0] = 'p';
52 else if (S_ISCHR(st->st_mode)) mode[0] = 'c';
53 else if (S_ISBLK(st->st_mode)) mode[0] = 'b';
54 else if (S_ISSOCK(st->st_mode)) mode[0] = 's';
55 for (i = 0; i < 9; ++i) if (st->st_mode & (0400 >> i)) mode[i+1] = bits[i];
56 if (st->st_mode & S_ISUID) mode[3] = mode[3] == 'x' ? 's' : 'S';
57 if (st->st_mode & S_ISGID) mode[6] = mode[6] == 'x' ? 's' : 'S';
58 if (st->st_mode & S_ISVTX) mode[9] = mode[9] == 'x' ? 't' : 'T';
59 if (tm) (void)strftime(date, sizeof(date), "%b %e %H:%M", tm);
60 printf("%s %ju ", mode, (uintmax_t)st->st_nlink);
61 if (user) printf("%s ", user->pw_name); else printf("%ju ", (uintmax_t)st->st_uid);
62 if (group) printf("%s ", group->gr_name); else printf("%ju ", (uintmax_t)st->st_gid);
63 printf("%jd %s ", (intmax_t)st->st_size, date);
64 }
65 if (prefix) printf("%s%s", prefix, prefix[strlen(prefix)-1] == '/' ? "" : "/");
66 printf("%s%s\n", name, Fflag ? (S_ISDIR(st->st_mode) ? "/" :
67 (st->st_mode & 0111) ? "*" : "") : "");
68}
69
70static int
71ls(const char *path, int prefix)
72{
73 struct stat st;
74 struct entry *entries = NULL;
75 struct dirent *entry;
76 DIR *dir;
77 size_t count = 0, i;
78 int status = 0;
79 if (lstat(path, &st) < 0) { perror(path); return 1; }
80 if (!S_ISDIR(st.st_mode) || dflag) { format(&st, path, NULL); return 0; }
81 dir = opendir(path);
82 if (dir == NULL) { perror(path); return 1; }
83 for (;;) {
84 struct entry *newentries;
85 errno = 0;
86 entry = readdir(dir);
87 if (entry == NULL) { if (errno) { perror(path); status = 1; } break; }
88 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
89 if (fstatat(dirfd(dir), entry->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
90 perror(entry->d_name); status = 1; continue;
91 }
92 if (count >= SIZE_MAX / sizeof(*entries)) { errno = ENOMEM; perror("ls"); status = 1; break; }
93 newentries = realloc(entries, (count + 1) * sizeof(*entries));
94 if (newentries == NULL) { perror("ls: realloc"); status = 1; break; }
95 entries = newentries;
96 entries[count].name = strdup(entry->d_name);
97 if (entries[count].name == NULL) { perror("ls: strdup"); status = 1; break; }
98 entries[count].st = st;
99 ++count;
100 }
101 if (!nflag && count > 1) qsort(entries, count, sizeof(*entries), compar);
102 for (i = 0; i < count; ++i) {
103 format(&entries[i].st, entries[i].name, prefix && !pflag ? path : NULL);
104 free(entries[i].name);
105 }
106 free(entries);
107 if (closedir(dir) < 0) { perror(path); status = 1; }
108 return status;
109}
110
111int
112main(int argc, char **argv)
113{
114 int option, status = 0;
115 while ((option = getopt(argc, argv, "dlnprstuF")) != -1) {
116 switch (option) {
117 case 'd': dflag = 1; break;
118 case 'l': lflag = 1; break;
119 case 'n': nflag = 1; break;
120 case 'p': pflag = 1; break;
121 case 'r': rflag = 1; break;
122 case 's': sflag = 1; break;
123 case 't': tflag = 1; break;
124 case 'u': uflag = 1; break;
125 case 'F': Fflag = 1; break;
126 default: fprintf(stderr, "usage: %s [-dlnprstuF] [file...]\n", argv[0]); return 1;
127 }
128 }
129 if (optind == argc) status = ls(".", 0);
130 for (; optind < argc; ++optind) status |= ls(argv[optind], 1);
131 if (fflush(stdout) == EOF || ferror(stdout)) { perror("ls: stdout"); status = 1; }
132 return status;
133}