-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
127 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |