summaryrefslogtreecommitdiff
path: root/ls.c
diff options
context:
space:
mode:
Diffstat (limited to 'ls.c')
-rw-r--r--ls.c144
1 files changed, 102 insertions, 42 deletions
diff --git a/ls.c b/ls.c
index 3c4f928..91d6f08 100644
--- a/ls.c
+++ b/ls.c
@@ -1,7 +1,7 @@
/*
* UNG's Not GNU
*
- * Copyright (c) 2011-2019, Jakob Kaivo <jkk@ung.org>
+ * Copyright (c) 2011-2022, Jakob Kaivo <jkk@ung.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,7 @@
*/
#define _XOPEN_SOURCE 700
+#include <locale.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/stat.h>
@@ -89,6 +90,8 @@ static int columns = 80;
struct ls_entry {
char name[PATH_MAX];
+ struct stat st;
+
uintmax_t size;
ino_t inode;
time_t time;
@@ -280,7 +283,7 @@ static int ls_compare(struct ls_entry *e1, struct ls_entry *e2)
} else if (sort == DIRECTORY) {
return (reverse ? -1 : 1);
}
- return (reverse ? -1 : 1) * (strcmp(e1->name, e2->name));
+ return (reverse ? -1 : 1) * (strcoll(e1->name, e2->name));
}
static int ls_add(const char *path, int do_stat)
@@ -467,113 +470,162 @@ static void ls_print(struct ls_entry *c)
}
}
-int main(int argc, char **argv)
+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;
+ }
+ }
+
+ return strcoll(a, b);
+}
+
+int main(int argc, char *argv[])
+{
+ setlocale(LC_ALL, "");
+
struct stat st;
- char *files[ARG_MAX];
+ char *files[argc];
int c;
int i = 0, f = 0;
int lastfollow = 0;
char *cols = getenv("COLUMNS");
- if (cols != NULL)
+ if (cols != NULL) {
columns = atoi(cols);
+ }
+
+ if (!isatty(STDOUT_FILENO)) {
+ display = SINGLE;
+ }
while ((c = getopt(argc, argv, ":ACFRSacdgfiklmnoprstux1HL")) != -1) {
switch (c) {
- case 'A':
+ case 'A': /** all but . and .. **/
all = ALMOST;
break;
- case 'C':
+
+ case 'C': /** sort in columns **/
display = COLUMNS;
break;
- case 'F':
+
+ case 'F': /** append file type characters **/
format |= CHARACTER;
break;
- case 'H':
- if (links != NONE)
- return 1;
+
+ case 'H': /** follow symbolic links on command line **/
links = FOLLOW;
break;
- case 'L':
- if (links != NONE)
- return 1;
+
+ case 'L': /** follow all symbolic links **/
links = LLONG;
break;
- case 'R':
+
+ case 'R': /** run recursively **/
recurse = 1;
break;
- case 'S':
+
+ case 'S': /** sort by size **/
sort = SIZE;
break;
- case 'a':
+
+ case 'a': /** output all files **/
all = ALL;
break;
- case 'c':
+
+ case 'c': /** sort by change time **/
sort = CTIME;
break;
- case 'd':
+
+ case 'd': /** do not enter directories **/
listdirs = 1;
break;
- case 'f':
+
+ case 'f': /** do not sort **/
sort = DIRECTORY;
all = ALL;
display = ROWS;
format ^= BLOCKS;
recurse = 0;
break;
- case 'g':
+
+ case 'g': /** like -l, but without owner **/
display = LONG;
noowner = 1;
break;
- case 'i':
+
+ case 'i': /** include file serial numbers **/
format |= INODES;
break;
- case 'k':
+
+ case 'k': /** use a blocksize of 1024 **/
blocksize = 1024;
break;
- case 'l':
+
+ case 'l': /** output full details **/
display = LONG;
break;
- case 'm':
+
+ case 'm': /** stream output separated by commas **/
display = COMMA;
break;
- case 'n':
+
+ case 'n': /** like -l, but with numeric UID and GID **/
display = LONG;
numeric = 1;
break;
- case 'o':
+
+ case 'o': /** like -l, but without group **/
display = LONG;
nogroup = 1;
break;
- case 'p':
+
+ case 'p': /** append / after directory names **/
format |= DIRS;
break;
- case 'q':
+
+ case 'q': /** replace non-printable characters with ? **/
format |= QUOTE; // FIXME
break;
- case 'r':
+
+ case 'r': /** reverse sort order **/
reverse = 1;
break;
- case 's':
+
+ case 's': /** outpu total number of blocks **/
format |= BLOCKS; // FIXME
break;
- case 't':
+
+ case 't': /** sort by modified time **/
sort = MTIME;
break;
- case 'u':
+
+ case 'u': /** sort by access time **/
sort = ATIME;
break;
- case 'x':
+
+ case 'x': /** sort across rows first **/
display = ROWS;
break;
- case '1':
+
+ case '1': /** output one file per line **/
display = SINGLE;
break;
+
default:
return 1;
}
}
+ /* sort operands */
+ qsort(argv + optind, argc - optind, sizeof(*argv), ls_compare_operands);
+
if (optind >= argc) {
strcpy(dirlist[0].path, ".");
strcpy(dirlist[0].display, ".");
@@ -594,35 +646,43 @@ int main(int argc, char **argv)
optind++;
}
- if (links == FOLLOW)
+ if (links == FOLLOW) {
lastfollow = totaldirs;
+ }
- for (i = 0; i < f; i++)
+ for (i = 0; i < f; i++) {
ls_add(files[i], 0);
+ }
ls_print(root);
ls_reset();
- if (f > 0 && totaldirs > 0)
+ if (f > 0 && totaldirs > 0) {
putchar('\n');
+ }
for (i = 0; i < totaldirs; i++) {
int total = 0;
- if (i >= lastfollow && links == FOLLOW)
+ if (i >= lastfollow && links == FOLLOW) {
links = NONE;
+ }
total = ls_dir(dirlist[i].path);
- if (f > 0 || totaldirs > 1)
+ if (f > 0 || totaldirs > 1) {
printf("%s:\n", dirlist[i].display);
- if (display == LONG || format & BLOCKS)
+ }
+
+ if (display == LONG || format & BLOCKS) {
printf("total %u\n", total);
+ }
ls_print(root);
- if (totaldirs > i + 1)
+ if (totaldirs > i + 1) {
putchar('\n');
+ }
ls_reset();
}