Skip to content

Commit

Permalink
userland(ls): add -l option
Browse files Browse the repository at this point in the history
  • Loading branch information
mosmeh committed Jun 22, 2024
1 parent 72b0463 commit 9fc48c2
Showing 1 changed file with 127 additions and 63 deletions.
190 changes: 127 additions & 63 deletions userland/ls.c
Original file line number Diff line number Diff line change
@@ -1,108 +1,172 @@
#include <dirent.h>
#include <errno.h>
#include <extra.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>

static int get_format(const struct dirent* dent, const char** out_format,
size_t* out_len) {
struct stat stat_buf;
if (stat(dent->d_name, &stat_buf) < 0) {
perror("stat");
return -1;
}
switch (stat_buf.st_mode & S_IFMT) {
static const char* get_format(const char* name, mode_t mode, size_t* out_len) {
switch (mode & S_IFMT) {
case S_IFDIR:
*out_format = "\x1b[01;34m%s\x1b[m/";
*out_len = strlen(dent->d_name) + 1;
break;
*out_len = strlen(name) + 1;
return "\x1b[01;34m%s\x1b[m/";
case S_IFCHR:
case S_IFBLK:
*out_format = "\x1b[01;33m%s\x1b[m";
*out_len = strlen(dent->d_name);
break;
*out_len = strlen(name);
return "\x1b[01;33m%s\x1b[m";
case S_IFIFO:
*out_format = "\x1b[33m%s\x1b[m|";
*out_len = strlen(dent->d_name) + 1;
break;
*out_len = strlen(name) + 1;
return "\x1b[33m%s\x1b[m|";
case S_IFLNK:
*out_format = "\x1b[01;36m%s\x1b[m@";
*out_len = strlen(dent->d_name) + 1;
break;
*out_len = strlen(name) + 1;
return "\x1b[01;36m%s\x1b[m@";
case S_IFSOCK:
*out_format = "\x1b[01;35m%s\x1b[m=";
*out_len = strlen(dent->d_name) + 1;
break;
*out_len = strlen(name) + 1;
return "\x1b[01;35m%s\x1b[m=";
case S_IFREG:
default:
if (stat_buf.st_mode & S_IXUSR) {
*out_format = "\x1b[01;32m%s\x1b[m*";
*out_len = strlen(dent->d_name) + 1;
if (mode & S_IXUSR) {
*out_len = strlen(name) + 1;
return "\x1b[01;32m%s\x1b[m*";
} else {
*out_format = "%s";
*out_len = strlen(dent->d_name);
*out_len = strlen(name);
return "%s";
}
break;
}
return 0;
}

static char get_file_type_char(mode_t mode) {
switch (mode & S_IFMT) {
case S_IFDIR:
return 'd';
case S_IFCHR:
return 'c';
case S_IFBLK:
return 'b';
case S_IFIFO:
return 'p';
case S_IFLNK:
return 'l';
case S_IFSOCK:
return 's';
case S_IFREG:
default:
return '-';
}
}

#define TAB_STOP 8

int main(int argc, char* argv[]) {
const char* path;
if (argc < 2) {
static char path_buf[1024];
getcwd(path_buf, 1024);
path = path_buf;
} else if (argc == 2) {
path = argv[1];
} else {
dprintf(STDERR_FILENO, "Usage: ls [DIRECTORY]\n");
return EXIT_FAILURE;
}
static int list_dir(const char* path, size_t terminal_width, bool long_format) {
int ret = -1;
DIR* dirp = NULL;

DIR* dirp = opendir(path);
dirp = opendir(path);
if (!dirp) {
perror("opendir");
return EXIT_FAILURE;
goto fail;
}
chdir(path);

size_t width = 0;
size_t path_len = strlen(path);
size_t x_pos = 0;
for (;;) {
errno = 0;
struct dirent* dent = readdir(dirp);
if (!dent) {
if (errno == 0)
break;
perror("readdir");
closedir(dirp);
return EXIT_FAILURE;
goto fail;
}

const char* format;
size_t len;
if (get_format(dent, &format, &len) < 0) {
closedir(dirp);
return EXIT_FAILURE;
size_t name_len = strlen(dent->d_name);
char* full_path = malloc(path_len + 1 + name_len + 1);
if (!full_path) {
perror("malloc");
goto fail;
}
strcpy(full_path, path);
full_path[path_len] = '/';
strcpy(full_path + path_len + 1, dent->d_name);

struct stat st;
ret = stat(full_path, &st);
free(full_path);
if (ret < 0) {
perror("stat");
goto fail;
}
size_t next_pos = round_up(width + len + 1, TAB_STOP);
if (next_pos >= 80) {
width = round_up(len + 1, TAB_STOP);

size_t len;
const char* format = get_format(dent->d_name, st.st_mode, &len);
if (long_format) {
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
printf("%c %3d,%6d ", get_file_type_char(st.st_mode),
major(st.st_rdev), minor(st.st_rdev));
} else {
printf("%c %10u ", get_file_type_char(st.st_mode), st.st_size);
}
printf(format, dent->d_name);
putchar('\n');
} else {
width = next_pos;
size_t next_pos = round_up(x_pos + len + 1, TAB_STOP);
if (next_pos >= terminal_width) {
x_pos = round_up(len + 1, TAB_STOP);
putchar('\n');
} else {
x_pos = next_pos;
}
printf(format, dent->d_name);
putchar('\t');
}
printf(format, dent->d_name);
putchar('\t');
}
if (width > 0)
if (x_pos > 0)
putchar('\n');

closedir(dirp);
return EXIT_SUCCESS;
ret = 0;
fail:
putchar('\n');
if (dirp)
closedir(dirp);
return ret;
}

int main(int argc, char* argv[]) {
bool long_format = false;
size_t num_dirs = 0;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-l"))
long_format = true;
else
++num_dirs;
}

size_t terminal_width = 80;
struct winsize winsize;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) >= 0)
terminal_width = winsize.ws_col;

if (num_dirs == 0) {
static char path_buf[PATH_MAX];
getcwd(path_buf, PATH_MAX);
if (list_dir(path_buf, terminal_width, long_format) < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}

int ret = EXIT_SUCCESS;
for (int i = 1; i < argc; i++) {
const char* arg = argv[i];
if (!strcmp(arg, "-l"))
continue;
if (num_dirs > 1)
printf("%s:\n", arg);
if (list_dir(arg, terminal_width, long_format) < 0)
ret = EXIT_FAILURE;
}
return ret;
}

0 comments on commit 9fc48c2

Please sign in to comment.