summaryrefslogtreecommitdiff
path: root/ls.c
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ung.org>2022-04-11 14:46:34 -0400
committerJakob Kaivo <jkk@ung.org>2022-04-11 14:46:34 -0400
commitcf1dd56b1d86c028a199a9c2157d59c20ba07dc3 (patch)
tree6433415471a1c6d2db2e96482b483337642d156f /ls.c
parent659d3db1d5ac4660432eb945e0665e8e2fe3dc49 (diff)
rewrite to work more correctly
Diffstat (limited to 'ls.c')
-rw-r--r--ls.c609
1 files changed, 172 insertions, 437 deletions
diff --git a/ls.c b/ls.c
index 91d6f08..4a4f34f 100644
--- a/ls.c
+++ b/ls.c
@@ -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;
}