diff options
author | Jakob Kaivo <jkk@ung.org> | 2022-04-11 14:46:34 -0400 |
---|---|---|
committer | Jakob Kaivo <jkk@ung.org> | 2022-04-11 14:46:34 -0400 |
commit | cf1dd56b1d86c028a199a9c2157d59c20ba07dc3 (patch) | |
tree | 6433415471a1c6d2db2e96482b483337642d156f /ls.c | |
parent | 659d3db1d5ac4660432eb945e0665e8e2fe3dc49 (diff) |
rewrite to work more correctly
Diffstat (limited to 'ls.c')
-rw-r--r-- | ls.c | 609 |
1 files changed, 172 insertions, 437 deletions
@@ -23,6 +23,8 @@ */ #define _XOPEN_SOURCE 700 +#include <errno.h> +#include <limits.h> #include <locale.h> #include <stdio.h> #include <stdint.h> @@ -39,8 +41,12 @@ #include <limits.h> #include <libgen.h> -#ifndef ARG_MAX -#define ARG_MAX 512 +#ifndef PATH_MAX +#ifdef _XOPEN_PATH_MAX +#define PATH_MAX _XOPEN_PATH_MAX +#else +#define PATH_MAX _POSIX_PATH_MAX +#endif #endif #define NONE 0 @@ -56,14 +62,6 @@ static enum { ATIME, } sort = ALPHA; -static enum { - COLUMNS, - ROWS, - COMMA, - LONG, - SINGLE, -} display = COLUMNS; - #define CHARACTER 1 << 0 #define INODES 1 << 1 #define DIRS 1 << 2 @@ -85,426 +83,190 @@ static int blocksize = DEFAULT_BLOCK_SIZE; static int numeric = 0; static int nogroup = 0; static int reverse = 0; -static int totaldirs = 0; +//static int totaldirs = 0; static int columns = 80; -struct ls_entry { - char name[PATH_MAX]; +struct file_info { + char *path; struct stat st; - - uintmax_t size; - ino_t inode; - time_t time; - uid_t uid; - gid_t gid; - char owner[PATH_MAX]; - char group[PATH_MAX]; - nlink_t links; - blkcnt_t blocks; - mode_t mode; - char link[PATH_MAX]; - char type; - int isexec; - struct ls_entry *left; - struct ls_entry *right; - struct ls_entry *up; -}; - -static struct ls_entry *root = NULL; -static int biggest = 1; -static nlink_t maxlinks = 1; -static int longestowner = 1; -static int longestgroup = 1; -static int longestname = 1; -static int sizelen = 1; -static int linklen = 1; - -struct directory { - char display[PATH_MAX]; - char path[PATH_MAX]; }; -static struct directory dirlist[ARG_MAX]; - -int nftw_ls(const char *p, const struct stat *sb, int flag, struct FTW *f) -{ - (void)sb; - - if (flag == FTW_D) { - strcat(dirlist[totaldirs].path, p); - // FIXME: this is showing the wrong names - if (f->level > 0) - f->base--; - while (f->level >= 0) { - f->base--; - if (p[f->base] == '/') - f->level--; - } - strcat(dirlist[totaldirs].display, &p[f->base]); - totaldirs++; - } - return 0; -} - -static int ls_stat(const char *p, struct stat *st) -{ - // FIXME fracking -L and -H - if (links == NONE) - return lstat(p, st); - return stat(p, st); -} +static void (*ls_print)(size_t n, struct file_info[static n]); -void ls_reset(void) +static char * ls_filename(struct file_info *fi) { - root = NULL; - biggest = 1; - maxlinks = 1; - longestowner = 1; - longestgroup = 1; - longestname = 1; - sizelen = 1; - linklen = 1; + static char name[PATH_MAX]; + /* TODO: decorate with /@|* if needed */ + snprintf(name, sizeof(name), "%s", fi->path); + return name; } -static int ls_other(struct ls_entry *current, int cpos) +static int ls_compare_files(const void *op_a, const void *op_b) { - char umode[4], gmode[4], omode[4]; - char time[BUFSIZ]; - - if (current == NULL) - return 0; - - if (current->left != NULL) { - cpos = ls_other(current->left, cpos); - free(current->left); - current->left = NULL; + const struct file_info *a = op_a; + const struct file_info *b = op_b; + + int ret = 0; + switch (sort) { + case SIZE: + ret = a->st.st_size - b->st.st_size; + break; + + case CTIME: + ret = a->st.st_ctime - b->st.st_ctime; + break; + + case MTIME: + ret = a->st.st_mtime - b->st.st_mtime; + break; + + case ATIME: + ret = a->st.st_atime - b->st.st_atime; + break; + + case DIRECTORY: + ret = 1; + break; + + case ALPHA: + /* handled below */ + break; } - if (format & INODES) { - cpos += printf("%u ", (unsigned int)current->inode); + if (ret == 0) { + ret = strcoll(a->path, b->path); } - if (display == LONG) { - while (biggest /= 10) - sizelen++; - while (maxlinks /= 10) - linklen++; - - umode[0] = (S_IRUSR & current->mode) ? 'r' : '-'; - umode[1] = (S_IWUSR & current->mode) ? 'w' : '-'; - if (S_ISUID & current->mode) - umode[2] = (S_IXUSR & current->mode) ? 's' : 'S'; - else - umode[2] = (S_IXUSR & current->mode) ? 'x' : '-'; - umode[3] = '\0'; - gmode[0] = (S_IRGRP & current->mode) ? 'r' : '-'; - gmode[1] = (S_IWGRP & current->mode) ? 'w' : '-'; - if (S_ISGID & current->mode) - gmode[2] = (S_IXGRP & current->mode) ? 's' : 'S'; - else - gmode[2] = (S_IXGRP & current->mode) ? 'x' : '-'; - gmode[3] = '\0'; - omode[0] = (S_IROTH & current->mode) ? 'r' : '-'; - omode[1] = (S_IWOTH & current->mode) ? 'w' : '-'; - if (S_ISVTX & current->mode) - omode[2] = (S_IXOTH & current->mode) ? 't' : 'T'; - else - omode[2] = (S_IXOTH & current->mode) ? 'x' : '-'; - omode[3] = '\0'; - strftime(time, BUFSIZ, "%b %e %H:%M", - localtime(&(current->time))); - // over six months user "%b %e %Y" - - printf("%c%s%s%s ", current->type, umode, gmode, omode); - printf("%*u ", linklen, (unsigned int)current->links); - - if (!noowner) { - if (numeric) - printf("%*u ", longestowner, current->uid); - else - printf("%-*s ", longestowner, current->owner); - } - - if (!nogroup) { - if (numeric) - printf("%*u ", longestowner, current->gid); - else - printf("%-*s ", longestgroup, current->group); - } - - printf("%*ju ", sizelen, (uintmax_t)current->size); - printf("%s %s", time, current->name); + return (reverse ? -1 : 1) * ret; +} - if (links != LLONG && current->type == 'l') - printf(" -> %s", current->link); +/* +static int ls_dir(char *dir) +{ + DIR *d = opendir(dir); + struct dirent *de; + int blocks = 0; + char filename[PATH_MAX]; - putchar('\n'); - } else if (display == COMMA) { - if (cpos + strlen(current->name) + 2 > (unsigned)columns) { - putchar('\n'); - cpos = 0; - } - cpos += printf("%s, ", current->name); - } else if (display == SINGLE) { - printf("%s\n", current->name); - } else { // if (display == ROWS) { - cpos += printf("%-*s ", longestname, current->name); - if (cpos + longestname + 1 > columns) { - putchar('\n'); - cpos = 0; + while ((de = readdir(d)) != NULL) { + if (de->d_name[0] != '.' || (de->d_name[0] == '.' && all == ALL) + || (strcmp(".", de->d_name) && strcmp("..", de->d_name) + && all == ALMOST)) { + strcpy(filename, dir); + strcat(filename, "/"); + strcat(filename, de->d_name); + //blocks += ls_add(filename, 1); } } - - if (current->right != NULL) { - cpos = ls_other(current->right, cpos); - free(current->right); - current->right = NULL; - } - - return cpos; + closedir(d); + return blocks * DEFAULT_BLOCK_SIZE / blocksize; } +*/ -static int ls_columns(struct ls_entry *current, int cpos) +static size_t ls_find_widest(size_t n, struct file_info files[static n]) { - //int cols = columns / (longestname + 1); - //int row = 0; - // FIXME: this isn't right at all - - return ls_other(current, cpos); + size_t ret = 0; + for (size_t i = 0; i < n; i++) { + size_t len = strlen(ls_filename(files + i)); + if (len > ret) { + ret = len; + } + } + return ret + 1; } -static int ls_compare(struct ls_entry *e1, struct ls_entry *e2) +static void ls_print_single(size_t n, struct file_info files[static n]) { - if (sort == SIZE && e1->size != e2->size) { - return (reverse ? -1 : 1) * (e2->size - e1->size); - } else if ((sort == CTIME || sort == MTIME || sort == ATIME) - && e1->time != e2->time) { - return (reverse ? -1 : 1) * (e2->time - e1->time); - } else if (sort == DIRECTORY) { - return (reverse ? -1 : 1); + for (size_t i = 0; i < n; i++) { + printf("%s\n", ls_filename(files + i)); } - return (reverse ? -1 : 1) * (strcoll(e1->name, e2->name)); } -static int ls_add(const char *path, int do_stat) +static void ls_print_serial(size_t n, struct file_info files[static n]) { - struct stat st; - char dname[PATH_MAX]; - char lname[PATH_MAX]; - struct ls_entry *working = malloc(sizeof(struct ls_entry)); - struct ls_entry *current; - working->left = NULL; - working->right = NULL; - working->blocks = 0; - - strcpy(dname, basename((char *)path)); - memset(working->name, 0, PATH_MAX); - memset(working->link, 0, PATH_MAX); - memset(working->owner, 0, PATH_MAX); - memset(working->group, 0, PATH_MAX); - - if (do_stat == 1 || sort != NONE || display == LONG || recurse || - (format & CHARACTER || format & INODES || format & DIRS - || format & BLOCKS)) { - ls_stat(path, &st); - - if (S_ISDIR(st.st_mode)) - working->type = 'd'; - else if (S_ISLNK(st.st_mode)) - working->type = 'l'; - else if (S_ISBLK(st.st_mode)) - working->type = 'b'; - else if (S_ISCHR(st.st_mode)) - working->type = 'c'; - else if (S_ISFIFO(st.st_mode)) - working->type = 'p'; - else - working->type = '-'; - - working->size = st.st_size; - working->inode = st.st_ino; - working->links = st.st_nlink; - working->uid = st.st_uid; - working->gid = st.st_gid; - working->blocks = st.st_blocks; - working->mode = st.st_mode; - - if (sort == CTIME) - working->time = st.st_ctime; - else if (sort == ATIME) - working->time = st.st_atime; - else - working->time = st.st_mtime; - - if (working->size > (uintmax_t)biggest) - biggest = working->size; - if (working->links > maxlinks) - maxlinks = working->links; - - if (!numeric) { - struct passwd pw = *getpwuid(working->uid); - struct group gr = *getgrgid(working->gid); - strcpy(working->owner, pw.pw_name); - strcpy(working->group, gr.gr_name); - if (strlen(working->owner) > (size_t)longestowner) - longestowner = strlen(working->owner); - if (strlen(working->group) > (size_t)longestgroup) - longestgroup = strlen(working->group); - } else { - int l, n = 0; - for (l = working->uid; l > 0; l /= 10) - n++; - if (n > longestowner) - longestowner = n; - for (l = working->gid; l > 0; l /= 10) - n++; - if (n > longestgroup) - longestgroup = n; - } - - if (working->type == '-') - working->isexec = (access(path, X_OK) == 0 ? 1 : 0); - - if (working->type == 'd' - && (format & DIRS || format & CHARACTER)) - strcat(dname, "/"); - else if (format & CHARACTER) { - if (working->type == 'l') - strcat(dname, "@"); - else if (working->type == 'p') - strcat(dname, "|"); - else if (working->isexec) - strcat(dname, "*"); - } - - if (working->type == 'l') { - // FIXME: maybe do a chdir() here so relative symlinks resolve proerly - int n = 0; - memset(lname, 0, PATH_MAX); - n = readlink(path, lname, PATH_MAX); - stat(lname, &st); - if (S_ISDIR(st.st_mode) - && (format & DIRS || format & CHARACTER)) { - lname[n] = '/'; - } else if (format & CHARACTER) { - if (S_ISLNK(st.st_mode)) - lname[n] = '@'; - else if (S_ISFIFO(st.st_mode)) - lname[n] = '|'; - else if (access(lname, X_OK) == 0) - lname[n] = '*'; - } - strcpy(working->link, lname); - } - } - - if (recurse && working->type == 'd') { - char fullpath[PATH_MAX]; - getcwd(fullpath, PATH_MAX); - strcat(fullpath, "/"); - strcat(fullpath, path); - nftw(fullpath, nftw_ls, 0, (links != NONE ? 0 : FTW_PHYS)); - } - - strcpy(working->name, dname); - - if (strlen(dname) > (size_t)longestname) { - longestname = strlen(dname); + if (n == 0) { + return; } - if (root == NULL) { - root = working; - } else { - current = root; - INSERT: - if (ls_compare(current, working) > 0) { - if (current->left == NULL) { - working->up = current; - current->left = working; - } else { - current = current->left; - goto INSERT; - } - } else { - if (current->right == NULL) { - working->up = current; - current->right = working; - } else { - current = current->right; - goto INSERT; - } - } + /* TODO: lines */ + for (size_t i = 0; i < n - 1; i++) { + printf("%s, ", ls_filename(files + i)); } - - return working->blocks; + printf("%s\n", ls_filename(files + n - 1)); } -static int ls_dir(char *dir) +static void ls_print_columns(size_t n, struct file_info files[static n]) { - DIR *d = opendir(dir); - struct dirent *de; - int blocks = 0; - char filename[PATH_MAX]; - - while ((de = readdir(d)) != NULL) { - if (de->d_name[0] != '.' || (de->d_name[0] == '.' && all == ALL) - || (strcmp(".", de->d_name) && strcmp("..", de->d_name) - && all == ALMOST)) { - strcpy(filename, dir); - strcat(filename, "/"); - strcat(filename, de->d_name); - blocks += ls_add(filename, 1); + /* TODO: partial columns */ + + size_t widest = ls_find_widest(n, files); + size_t ncolumns = columns / widest; + size_t nrows = n / ncolumns; + char format[32] = ""; + snprintf(format, sizeof(format), "%%-%zds", widest); + for (size_t i = 0; i < nrows; i++) { + for (size_t j = 0; j < ncolumns; j++) { + printf(format, ls_filename(files + i + (nrows * j))); } + printf("\n"); } - closedir(d); - return blocks * DEFAULT_BLOCK_SIZE / blocksize; } -static void ls_print(struct ls_entry *c) +static void ls_print_rows(size_t n, struct file_info files[static n]) { - if (display == COLUMNS) { - if (ls_columns(c, 0) != 0) - putchar('\n'); - } else if (ls_other(c, 0) != 0 && display != LONG && display != SINGLE) { - putchar('\n'); + size_t widest = ls_find_widest(n, files); + size_t ncolumns = columns / widest; + char format[32] = ""; + snprintf(format, sizeof(format), "%%-%zds", widest); + for (size_t i = 0; i < n; i++) { + printf(format, ls_filename(files + i)); + if (i % ncolumns == ncolumns - 1) { + printf("\n"); + } } } static int ls_compare_operands(const void *op_a, const void *op_b) { - const char *a = *(char**)op_a; - const char *b = *(char**)op_b; - struct stat sta, stb; - if (stat(a, &sta) == 0 && stat(b, &stb) == 0) { - if (S_ISDIR(sta.st_mode) && !S_ISDIR(stb.st_mode)) { - return 1; - } - if (S_ISDIR(stb.st_mode) && !S_ISDIR(sta.st_mode)) { - return -1; - } + const struct file_info *a = op_a; + const struct file_info *b = op_b; + + if (a->path == NULL && b->path == NULL) { + return 0; } - return strcoll(a, b); + if (a->path == NULL) { + return 1; + } + + if (b->path == NULL) { + return -1; + } + + if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) { + return 1; + } + + if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) { + return -1; + } + + return strcoll(a->path, b->path); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); - struct stat st; - char *files[argc]; - int c; - int i = 0, f = 0; - int lastfollow = 0; char *cols = getenv("COLUMNS"); if (cols != NULL) { columns = atoi(cols); } - if (!isatty(STDOUT_FILENO)) { - display = SINGLE; - } + /* TODO: default to ls_print_columns */ + ls_print = isatty(STDOUT_FILENO) ? ls_print_columns : ls_print_single; + int c; while ((c = getopt(argc, argv, ":ACFRSacdgfiklmnoprstux1HL")) != -1) { switch (c) { case 'A': /** all but . and .. **/ @@ -512,7 +274,7 @@ int main(int argc, char *argv[]) break; case 'C': /** sort in columns **/ - display = COLUMNS; + ls_print = ls_print_columns; break; case 'F': /** append file type characters **/ @@ -550,13 +312,13 @@ int main(int argc, char *argv[]) case 'f': /** do not sort **/ sort = DIRECTORY; all = ALL; - display = ROWS; + //display = ROWS; format ^= BLOCKS; recurse = 0; break; case 'g': /** like -l, but without owner **/ - display = LONG; + //display = LONG; noowner = 1; break; @@ -569,20 +331,20 @@ int main(int argc, char *argv[]) break; case 'l': /** output full details **/ - display = LONG; + //display = LONG; break; case 'm': /** stream output separated by commas **/ - display = COMMA; + ls_print = ls_print_serial; break; case 'n': /** like -l, but with numeric UID and GID **/ - display = LONG; + //display = LONG; numeric = 1; break; case 'o': /** like -l, but without group **/ - display = LONG; + //display = LONG; nogroup = 1; break; @@ -611,11 +373,11 @@ int main(int argc, char *argv[]) break; case 'x': /** sort across rows first **/ - display = ROWS; + ls_print = ls_print_rows; break; case '1': /** output one file per line **/ - display = SINGLE; + ls_print = ls_print_single; break; default: @@ -624,68 +386,41 @@ int main(int argc, char *argv[]) } /* sort operands */ - qsort(argv + optind, argc - optind, sizeof(*argv), ls_compare_operands); - - if (optind >= argc) { - strcpy(dirlist[0].path, "."); - strcpy(dirlist[0].display, "."); - totaldirs = 1; - } - - while (optind < argc) { - if (ls_stat(argv[optind], &st) != 0) { - perror(argv[optind]); - } else if (!listdirs && S_ISDIR(st.st_mode)) { - strcpy(dirlist[totaldirs].display, argv[optind]); - strcpy(dirlist[totaldirs].path, argv[optind]); - totaldirs++; - } else { - files[f] = argv[optind]; - f++; + /* TODO: deal with -L/-H */ + argv += optind; + argc -= optind; + struct file_info operands[argc + 1]; + int ret = 0; + for (int i = 0; i < argc; i++) { + operands[i].path = argv[i]; + if (stat(argv[i], &operands[i].st) == -1) { + operands[i].path = NULL; + fprintf(stderr, "ls: %s: %s\n", argv[i], strerror(errno)); + ret = 1; } - optind++; } - - if (links == FOLLOW) { - lastfollow = totaldirs; + operands[argc].path = NULL; + qsort(operands, argc, sizeof(operands[0]), ls_compare_operands); + + /* list files */ + size_t nfiles = 0; + struct file_info *op = operands; + while (op->path != NULL && !S_ISDIR(op->st.st_mode)) { + op++; + nfiles++; } - for (i = 0; i < f; i++) { - ls_add(files[i], 0); - } + qsort(operands, nfiles, sizeof(operands[0]), ls_compare_files); + ls_print(nfiles, operands); - ls_print(root); - ls_reset(); + /* list directories */ - if (f > 0 && totaldirs > 0) { - putchar('\n'); - } - - for (i = 0; i < totaldirs; i++) { - int total = 0; - - if (i >= lastfollow && links == FOLLOW) { - links = NONE; - } - - total = ls_dir(dirlist[i].path); - - if (f > 0 || totaldirs > 1) { - printf("%s:\n", dirlist[i].display); - } + /* TODO: now always necessary */ + printf("\n"); - if (display == LONG || format & BLOCKS) { - printf("total %u\n", total); - } - - ls_print(root); - - if (totaldirs > i + 1) { - putchar('\n'); - } - - ls_reset(); + while (op->path != NULL) { + printf("%s: (directory)\n", op->path); + op++; } - - return 0; + return ret; } |