| #include <stdio.h> |
| #include <stdlib.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/sysmacros.h> |
| |
| #define STAT_ISSET(mode, mask) (((mode) & mask) == mask) |
| |
| static size_t max_linksiz = 128; |
| static int max_nlinks = 1; |
| static int max_size = 1; |
| static int max_uid = 1; |
| static int max_gid = 1; |
| static int max_min = 1; |
| static int max_maj = 1; |
| |
| static void do_preformat(const struct stat *st) |
| { |
| int bytes; |
| |
| bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_nlink); |
| if (bytes > max_nlinks) |
| max_nlinks = bytes; |
| |
| bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_uid); |
| if (bytes > max_uid) |
| max_uid = bytes; |
| |
| bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_gid); |
| if (bytes > max_gid) |
| max_gid = bytes; |
| |
| if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { |
| bytes = snprintf(NULL, 0, "%u", major(st->st_rdev)); |
| if (bytes > max_maj) |
| max_maj = bytes; |
| |
| bytes = snprintf(NULL, 0, "%u", minor(st->st_rdev)); |
| if (bytes > max_min) |
| max_min = bytes; |
| |
| max_size = max_maj + max_min + 1; |
| } else { |
| bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_size); |
| if (bytes > max_size) |
| max_size = bytes; |
| } |
| return; |
| } |
| |
| static void do_stat(const struct stat *st, const char *path) |
| { |
| char *fmt, *link_name; |
| int rc; |
| |
| switch (st->st_mode & S_IFMT) { |
| case S_IFBLK: |
| putchar('b'); |
| break; |
| case S_IFCHR: |
| putchar('c'); |
| break; |
| case S_IFDIR: |
| putchar('d'); |
| break; |
| case S_IFIFO: |
| putchar('p'); |
| break; |
| case S_IFLNK: |
| putchar('l'); |
| break; |
| case S_IFSOCK: |
| putchar('s'); |
| break; |
| case S_IFREG: |
| putchar('-'); |
| break; |
| default: |
| putchar('?'); |
| break; |
| } |
| putchar(STAT_ISSET(st->st_mode, S_IRUSR) ? 'r' : '-'); |
| putchar(STAT_ISSET(st->st_mode, S_IWUSR) ? 'w' : '-'); |
| |
| !STAT_ISSET(st->st_mode, S_ISUID) ? |
| putchar(STAT_ISSET(st->st_mode, S_IXUSR) ? 'x' : '-') : |
| putchar('S'); |
| |
| putchar(STAT_ISSET(st->st_mode, S_IRGRP) ? 'r' : '-'); |
| putchar(STAT_ISSET(st->st_mode, S_IWGRP) ? 'w' : '-'); |
| |
| !STAT_ISSET(st->st_mode, S_ISGID) ? |
| putchar(STAT_ISSET(st->st_mode, S_IXGRP) ? 'x' : '-') : |
| putchar('S'); |
| |
| putchar(STAT_ISSET(st->st_mode, S_IROTH) ? 'r' : '-'); |
| putchar(STAT_ISSET(st->st_mode, S_IWOTH) ? 'w' : '-'); |
| |
| !STAT_ISSET(st->st_mode, S_ISVTX) ? |
| putchar(STAT_ISSET(st->st_mode, S_IXOTH) ? 'x' : '-') : |
| putchar(S_ISDIR(st->st_mode) ? 't' : 'T'); |
| |
| if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { |
| rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%du,%%%du %%s", |
| max_nlinks, max_uid, max_gid, max_maj, max_min); |
| if (rc == -1) { |
| perror("asprintf"); |
| exit(1); |
| } |
| fprintf(stdout, fmt, |
| (uintmax_t) st->st_nlink, |
| (uintmax_t) st->st_uid, |
| (uintmax_t) st->st_gid, |
| major(st->st_rdev), |
| minor(st->st_rdev), |
| path); |
| } else { |
| rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%dju %%s", |
| max_nlinks, max_uid, max_gid, max_size); |
| if (rc == -1) { |
| perror("asprintf"); |
| exit(1); |
| } |
| fprintf(stdout, fmt, |
| (uintmax_t) st->st_nlink, |
| (uintmax_t) st->st_uid, |
| (uintmax_t) st->st_gid, |
| (uintmax_t) st->st_size, |
| path); |
| } |
| free(fmt); |
| |
| if (S_ISLNK(st->st_mode)) { |
| link_name = malloc(max_linksiz); |
| if (link_name == NULL) { |
| perror("malloc"); |
| exit(1); |
| } |
| rc = readlink(path, link_name, max_linksiz); |
| if (rc == -1) { |
| free(link_name); |
| perror("readlink"); |
| exit(1); |
| } |
| link_name[rc] = '\0'; |
| fprintf(stdout, " -> %s", link_name); |
| free(link_name); |
| } |
| |
| putchar('\n'); |
| return; |
| } |
| |
| static void do_dir(const char *path, int preformat) |
| { |
| DIR *dir; |
| struct dirent *dent; |
| struct stat st; |
| |
| if (chdir(path) == -1) { |
| perror(path); |
| exit(1); |
| } |
| |
| dir = opendir(path); |
| if (dir == NULL) { |
| perror(path); |
| exit(1); |
| } |
| |
| while ((dent = readdir(dir)) != NULL) { |
| if (lstat(dent->d_name, &st)) { |
| perror(dent->d_name); |
| exit(1); |
| } |
| (preformat) ? |
| do_preformat(&st) : |
| do_stat(&st, dent->d_name); |
| } |
| |
| closedir(dir); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int i; |
| struct stat st; |
| |
| if (argc == 1) { |
| do_dir(".", 1); |
| do_dir(".", 0); |
| return 0; |
| } |
| |
| for (i = 1; i < argc; i++) { |
| if (argv[i][0] == '-' && argv[i][1] == 'h') { |
| fprintf(stdout, "Usage: ls [-h] [FILE ...]\n"); |
| return 0; |
| } |
| |
| if (lstat(argv[i], &st)) { |
| perror(argv[i]); |
| exit(1); |
| } |
| |
| S_ISDIR(st.st_mode) ? |
| do_dir(argv[i], 1) : |
| do_preformat(&st); |
| } |
| |
| for (i = 1; i < argc; i++) { |
| if (lstat(argv[i], &st)) { |
| perror(argv[i]); |
| exit(1); |
| } |
| |
| S_ISDIR(st.st_mode) ? |
| do_dir(argv[i], 0) : |
| do_stat(&st, argv[i]); |
| } |
| |
| return 0; |
| } |