2012-01-26 19:12:10 -06:00
|
|
|
/*
|
|
|
|
* ls
|
2012-07-06 21:08:28 -07:00
|
|
|
*
|
|
|
|
* Lists files in a directory, with nice color
|
|
|
|
* output like any modern ls should have.
|
2012-01-26 19:12:10 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <syscall.h>
|
|
|
|
#include <unistd.h>
|
2012-09-16 20:37:22 -07:00
|
|
|
#include <dirent.h>
|
2013-03-25 22:24:26 -07:00
|
|
|
#include <termios.h>
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
#include "lib/list.h"
|
|
|
|
|
2012-01-26 19:12:10 -06:00
|
|
|
#define MIN_COL_SPACING 2
|
|
|
|
|
|
|
|
#define EXE_COLOR "1;32"
|
|
|
|
#define DIR_COLOR "1;34"
|
|
|
|
#define REG_COLOR "0"
|
|
|
|
#define MEDIA_COLOR ""
|
|
|
|
#define SYM_COLOR ""
|
|
|
|
#define BROKEN_COLOR "1;"
|
2013-03-17 16:32:10 -07:00
|
|
|
#define DEVICE_COLOR "1;33;40"
|
2014-06-05 22:19:55 -07:00
|
|
|
#define SETUID_COLOR "37;41"
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2014-05-27 21:38:30 -07:00
|
|
|
#define DEFAULT_TERM_WIDTH 0
|
|
|
|
#define DEFAULT_TERM_HEIGHT 0
|
2012-03-07 19:35:15 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:59:55 -08:00
|
|
|
#define LINE_LEN 4096
|
|
|
|
|
2014-06-05 21:34:10 -07:00
|
|
|
static int human_readable = 0;
|
|
|
|
static int stdout_is_tty = 1;
|
2014-06-05 22:19:55 -07:00
|
|
|
static int this_year = 0;
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
int entcmp(const void * c1, const void * c2) {
|
|
|
|
struct dirent * d1 = *(struct dirent **)c1;
|
|
|
|
struct dirent * d2 = *(struct dirent **)c2;
|
|
|
|
return strcmp(d1->d_name, d2->d_name);
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
void print_entry(const char * filename, const char * srcpath, int colwidth) {
|
|
|
|
/* Figure out full relpath */
|
|
|
|
char * relpath = malloc(strlen(srcpath) + strlen(filename) + 2);
|
|
|
|
sprintf(relpath, "%s/%s", srcpath, filename);
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
/* Classify file */
|
|
|
|
struct stat statbuf;
|
|
|
|
stat(relpath, &statbuf);
|
|
|
|
free(relpath);
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
const char * ansi_color_str;
|
|
|
|
if (S_ISDIR(statbuf.st_mode)) {
|
2013-03-17 16:32:10 -07:00
|
|
|
/* Directory */
|
2012-12-09 16:03:22 -08:00
|
|
|
ansi_color_str = DIR_COLOR;
|
|
|
|
} else if (statbuf.st_mode & 0111) {
|
2013-03-17 16:32:10 -07:00
|
|
|
/* Executable */
|
2012-12-09 16:03:22 -08:00
|
|
|
ansi_color_str = EXE_COLOR;
|
2013-03-17 16:32:10 -07:00
|
|
|
} else if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
|
|
|
|
/* Device file */
|
|
|
|
ansi_color_str = DEVICE_COLOR;
|
2012-01-26 19:12:10 -06:00
|
|
|
} else {
|
2013-03-17 16:32:10 -07:00
|
|
|
/* Regular file? */
|
2012-12-09 16:03:22 -08:00
|
|
|
ansi_color_str = REG_COLOR;
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
/* Print the file name */
|
2014-06-05 21:34:10 -07:00
|
|
|
if (stdout_is_tty) {
|
|
|
|
printf("\033[%sm%s\033[0m", ansi_color_str, filename);
|
|
|
|
} else {
|
|
|
|
printf("%s", filename);
|
|
|
|
}
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
/* Pad the rest of the column */
|
|
|
|
for (int rem = colwidth - strlen(filename); rem > 0; rem--) {
|
|
|
|
printf(" ");
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
int print_username(char * _out, int uid) {
|
2012-12-09 16:59:55 -08:00
|
|
|
FILE * passwd = fopen("/etc/passwd", "r");
|
|
|
|
char line[LINE_LEN];
|
2014-06-05 22:19:55 -07:00
|
|
|
int out;
|
2012-12-09 16:59:55 -08:00
|
|
|
|
|
|
|
while (fgets(line, LINE_LEN, passwd) != NULL) {
|
|
|
|
|
|
|
|
line[strlen(line)-1] = '\0';
|
|
|
|
|
|
|
|
char *p, *tokens[10], *last;
|
|
|
|
int i = 0;
|
2013-03-17 16:32:10 -07:00
|
|
|
for ((p = strtok_r(line, ":", &last)); p; (p = strtok_r(NULL, ":", &last)), i++) {
|
2012-12-09 16:59:55 -08:00
|
|
|
if (i < 511) tokens[i] = p;
|
|
|
|
}
|
|
|
|
tokens[i] = NULL;
|
|
|
|
|
|
|
|
if (atoi(tokens[2]) == uid) {
|
2014-06-05 22:19:55 -07:00
|
|
|
out = sprintf(_out, "%s", tokens[0]);
|
2012-12-09 16:59:55 -08:00
|
|
|
fclose(passwd);
|
2014-06-05 22:19:55 -07:00
|
|
|
return out;
|
2012-12-09 16:59:55 -08:00
|
|
|
}
|
|
|
|
}
|
2014-06-05 22:19:55 -07:00
|
|
|
out = sprintf(_out, "%d", uid);
|
2012-12-09 16:59:55 -08:00
|
|
|
fclose(passwd);
|
2014-06-05 22:19:55 -07:00
|
|
|
return out;
|
2012-12-09 16:59:55 -08:00
|
|
|
}
|
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
int print_human_readable_size(char * _out, size_t s) {
|
2014-06-05 21:34:10 -07:00
|
|
|
if (s >= 1<<20) {
|
|
|
|
size_t t = s / (1 << 20);
|
2014-06-05 22:19:55 -07:00
|
|
|
return sprintf(_out, "%d.%1dM", t, (s - t * (1 << 20)) / ((1 << 20) / 10));
|
2014-06-05 21:34:10 -07:00
|
|
|
} else if (s >= 1<<10) {
|
|
|
|
size_t t = s / (1 << 10);
|
2014-06-05 22:19:55 -07:00
|
|
|
return sprintf(_out, "%d.%1dK", t, (s - t * (1 << 10)) / ((1 << 10) / 10));
|
2014-06-05 21:34:10 -07:00
|
|
|
} else {
|
2014-06-05 22:19:55 -07:00
|
|
|
return sprintf(_out, "%d", s);
|
2014-06-05 21:34:10 -07:00
|
|
|
}
|
2014-06-05 22:19:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void update_column_widths(int * widths, const char * filename, const char * srcpath) {
|
|
|
|
char * relpath = malloc(strlen(srcpath) + strlen(filename) + 2);
|
|
|
|
sprintf(relpath, "%s/%s", srcpath, filename);
|
|
|
|
|
|
|
|
/* Classify file */
|
|
|
|
struct stat statbuf;
|
|
|
|
stat(relpath, &statbuf);
|
|
|
|
free(relpath);
|
|
|
|
|
|
|
|
char tmp[256];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/* Links */
|
|
|
|
n = sprintf(tmp, "%d", statbuf.st_nlink);
|
|
|
|
if (n > widths[0]) widths[0] = n;
|
2014-06-05 21:34:10 -07:00
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
/* User */
|
|
|
|
n = print_username(tmp, statbuf.st_uid);
|
|
|
|
if (n > widths[1]) widths[1] = n;
|
|
|
|
|
|
|
|
/* Group */
|
|
|
|
n = print_username(tmp, statbuf.st_gid);
|
|
|
|
if (n > widths[2]) widths[2] = n;
|
|
|
|
|
|
|
|
/* File size */
|
|
|
|
if (human_readable) {
|
|
|
|
n = print_human_readable_size(tmp, statbuf.st_size);
|
|
|
|
} else {
|
|
|
|
n = sprintf(tmp, "%d", statbuf.st_size);
|
|
|
|
}
|
|
|
|
if (n > widths[3]) widths[3] = n;
|
2014-06-05 21:34:10 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
void print_entry_long(int * widths, const char * filename, const char * srcpath) {
|
2012-01-26 19:12:10 -06:00
|
|
|
/* Figure out full relpath */
|
|
|
|
char * relpath = malloc(strlen(srcpath) + strlen(filename) + 2);
|
|
|
|
sprintf(relpath, "%s/%s", srcpath, filename);
|
|
|
|
|
|
|
|
/* Classify file */
|
|
|
|
struct stat statbuf;
|
|
|
|
stat(relpath, &statbuf);
|
|
|
|
free(relpath);
|
|
|
|
|
|
|
|
const char * ansi_color_str;
|
|
|
|
if (S_ISDIR(statbuf.st_mode)) {
|
|
|
|
// Directory
|
|
|
|
ansi_color_str = DIR_COLOR;
|
2014-06-05 22:19:55 -07:00
|
|
|
} else if (statbuf.st_mode & S_ISUID) {
|
|
|
|
ansi_color_str = SETUID_COLOR;
|
2012-01-26 19:12:10 -06:00
|
|
|
} else if (statbuf.st_mode & 0111) {
|
|
|
|
// Executable
|
|
|
|
ansi_color_str = EXE_COLOR;
|
2014-04-01 23:14:58 -07:00
|
|
|
} else if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
|
|
|
|
/* Device file */
|
|
|
|
ansi_color_str = DEVICE_COLOR;
|
2012-01-26 19:12:10 -06:00
|
|
|
} else {
|
|
|
|
// Something else
|
|
|
|
ansi_color_str = REG_COLOR;
|
|
|
|
}
|
|
|
|
|
2012-12-09 16:59:55 -08:00
|
|
|
/* file permissions */
|
|
|
|
if (S_ISLNK(statbuf.st_mode)) {
|
|
|
|
printf("l");
|
2014-05-25 21:59:51 -07:00
|
|
|
} else if (statbuf.st_mode & S_ISUID) {
|
|
|
|
printf("s");
|
2012-12-09 16:59:55 -08:00
|
|
|
} else {
|
|
|
|
printf( (S_ISDIR(statbuf.st_mode)) ? "d" : "-");
|
|
|
|
}
|
2012-12-09 16:03:22 -08:00
|
|
|
printf( (statbuf.st_mode & S_IRUSR) ? "r" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IWUSR) ? "w" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IXUSR) ? "x" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IRGRP) ? "r" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IWGRP) ? "w" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IXGRP) ? "x" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IROTH) ? "r" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IWOTH) ? "w" : "-");
|
|
|
|
printf( (statbuf.st_mode & S_IXOTH) ? "x" : "-");
|
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
printf( " %*d ", widths[0], statbuf.st_nlink); /* number of links, not supported */
|
2012-12-09 16:59:55 -08:00
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
char tmp[100];
|
|
|
|
print_username(tmp, statbuf.st_uid);
|
|
|
|
printf("%-*s ", widths[1], tmp);
|
|
|
|
print_username(tmp, statbuf.st_gid);
|
|
|
|
printf("%-*s ", widths[2], tmp);
|
2012-12-09 16:59:55 -08:00
|
|
|
|
2014-06-05 21:34:10 -07:00
|
|
|
if (human_readable) {
|
2014-06-05 22:19:55 -07:00
|
|
|
print_human_readable_size(tmp, statbuf.st_size);
|
|
|
|
printf("%*s ", widths[3], tmp);
|
2014-06-05 21:34:10 -07:00
|
|
|
} else {
|
2014-06-05 22:19:55 -07:00
|
|
|
printf("%*d ", widths[3], statbuf.st_size);
|
2014-06-05 21:34:10 -07:00
|
|
|
}
|
2012-12-09 16:59:55 -08:00
|
|
|
|
|
|
|
char time_buf[80];
|
2014-06-05 22:19:55 -07:00
|
|
|
struct tm * timeinfo = localtime(&statbuf.st_mtime);
|
|
|
|
if (timeinfo->tm_year == this_year) {
|
|
|
|
strftime(time_buf, 80, "%b %d %H:%M", timeinfo);
|
|
|
|
} else {
|
|
|
|
strftime(time_buf, 80, "%b %d %Y", timeinfo);
|
|
|
|
}
|
2012-12-09 16:59:55 -08:00
|
|
|
printf("%s ", time_buf);
|
2012-01-26 19:12:10 -06:00
|
|
|
|
|
|
|
/* Print the file name */
|
2014-06-05 22:19:55 -07:00
|
|
|
if (stdout_is_tty) {
|
|
|
|
printf("\033[%sm%s\033[0m", ansi_color_str, filename);
|
|
|
|
} else {
|
|
|
|
printf("%s", filename);
|
|
|
|
}
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2014-06-05 22:19:55 -07:00
|
|
|
printf("\n");
|
2012-12-09 16:03:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void show_usage(int argc, char * argv[]) {
|
|
|
|
printf(
|
|
|
|
"ls - list files\n"
|
|
|
|
"\n"
|
|
|
|
"usage: %s [-lha] [path]\n"
|
|
|
|
"\n"
|
|
|
|
" -a \033[3mlist all files (including . files)\033[0m\n"
|
|
|
|
" -l \033[3muse a long listing format\033[0m\n"
|
2014-06-05 21:34:10 -07:00
|
|
|
" -h \033[3mhuman-readable file sizes\033[0m\n"
|
2012-12-09 16:03:22 -08:00
|
|
|
" -? \033[3mshow this help text\033[0m\n"
|
|
|
|
"\n", argv[0]);
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char * argv[]) {
|
|
|
|
|
|
|
|
/* Parse arguments */
|
|
|
|
char * p = ".";
|
|
|
|
int explicit_path_set = 0;
|
|
|
|
int show_hidden = 0;
|
2012-12-09 16:03:22 -08:00
|
|
|
int long_mode = 0;
|
2012-01-26 19:12:10 -06:00
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
int index, c;
|
2014-06-05 21:34:10 -07:00
|
|
|
while ((c = getopt(argc, argv, "ahl?")) != -1) {
|
2012-01-26 19:12:10 -06:00
|
|
|
switch (c) {
|
|
|
|
case 'a':
|
|
|
|
show_hidden = 1;
|
|
|
|
break;
|
2014-06-05 21:34:10 -07:00
|
|
|
case 'h':
|
|
|
|
human_readable = 1;
|
|
|
|
break;
|
2012-12-09 16:03:22 -08:00
|
|
|
case 'l':
|
|
|
|
long_mode = 1;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
show_usage(argc, argv);
|
|
|
|
return 0;
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind < argc) {
|
|
|
|
p = argv[optind];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-05 21:34:10 -07:00
|
|
|
stdout_is_tty = isatty(STDOUT_FILENO);
|
|
|
|
|
2012-01-26 19:12:10 -06:00
|
|
|
/* Open the directory */
|
|
|
|
DIR * dirp = opendir(p);
|
|
|
|
if (dirp == NULL) {
|
|
|
|
printf("no such directory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the entries in the directory */
|
|
|
|
list_t * ents_list = list_create();
|
|
|
|
|
|
|
|
struct dirent * ent = readdir(dirp);
|
|
|
|
while (ent != NULL) {
|
2012-09-16 20:37:22 -07:00
|
|
|
if (show_hidden || (ent->d_name[0] != '.')) {
|
2012-01-26 19:12:10 -06:00
|
|
|
struct dirent * entcpy = malloc(sizeof(struct dirent));
|
|
|
|
memcpy(entcpy, ent, sizeof(struct dirent));
|
|
|
|
list_insert(ents_list, (void *)entcpy);
|
|
|
|
}
|
|
|
|
|
|
|
|
ent = readdir(dirp);
|
|
|
|
}
|
|
|
|
closedir(dirp);
|
|
|
|
|
|
|
|
/* Now, copy those entries into an array (for sorting) */
|
|
|
|
struct dirent ** ents_array = malloc(sizeof(struct dirent *) * ents_list->length);
|
|
|
|
int index = 0;
|
|
|
|
node_t * node;
|
|
|
|
foreach(node, ents_list) {
|
|
|
|
ents_array[index++] = (struct dirent *)node->value;
|
|
|
|
}
|
|
|
|
list_free(ents_list);
|
|
|
|
int numents = index;
|
|
|
|
|
|
|
|
qsort(ents_array, numents, sizeof(struct dirent *), entcmp);
|
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
if (long_mode) {
|
2014-06-05 22:19:55 -07:00
|
|
|
int widths[4] = {0,0,0,0};
|
|
|
|
struct tm * timeinfo;
|
|
|
|
struct timeval now;
|
|
|
|
gettimeofday(&now, NULL); //time(NULL);
|
|
|
|
timeinfo = localtime((time_t *)&now.tv_sec);
|
|
|
|
this_year = timeinfo->tm_year;
|
|
|
|
for (int i = 0; i < numents; i++) {
|
|
|
|
update_column_widths(widths, ents_array[i]->d_name, p);
|
|
|
|
}
|
2012-12-09 16:03:22 -08:00
|
|
|
for (int i = 0; i < numents; i++) {
|
2014-06-05 22:19:55 -07:00
|
|
|
print_entry_long(widths, ents_array[i]->d_name, p);
|
2012-12-09 16:03:22 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Determine the gridding dimensions */
|
|
|
|
int ent_max_len = 0;
|
|
|
|
for (int i = 0; i < numents; i++) {
|
|
|
|
ent_max_len = MAX(ent_max_len, strlen(ents_array[i]->d_name));
|
|
|
|
}
|
2012-09-05 22:47:52 -07:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
int term_width = DEFAULT_TERM_WIDTH;
|
|
|
|
int term_height = DEFAULT_TERM_HEIGHT;
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2014-06-05 21:34:10 -07:00
|
|
|
if (stdout_is_tty) {
|
2014-05-27 21:38:30 -07:00
|
|
|
struct winsize w;
|
|
|
|
ioctl(1, TIOCGWINSZ, &w);
|
|
|
|
term_width = w.ws_col;
|
|
|
|
term_height = w.ws_row;
|
|
|
|
term_width -= 1; /* And this just helps clean up our math */
|
|
|
|
}
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
int col_ext = ent_max_len + MIN_COL_SPACING;
|
|
|
|
int cols = ((term_width - ent_max_len) / col_ext) + 1;
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
/* Print the entries */
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
for (int i = 0; i < numents;) {
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
/* Columns */
|
2012-09-16 20:37:22 -07:00
|
|
|
print_entry(ents_array[i++]->d_name, p, ent_max_len);
|
2012-01-26 19:12:10 -06:00
|
|
|
|
2012-12-09 16:03:22 -08:00
|
|
|
for (int j = 0; (i < numents) && (j < (cols-1)); j++) {
|
|
|
|
printf(" ");
|
|
|
|
print_entry(ents_array[i++]->d_name, p, ent_max_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
}
|
2012-01-26 19:12:10 -06:00
|
|
|
}
|
2012-12-09 16:03:22 -08:00
|
|
|
|
2012-01-26 19:12:10 -06:00
|
|
|
free(ents_array);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vim: tabstop=4
|
|
|
|
* vim: shiftwidth=4
|
|
|
|
* vim: noexpandtab
|
|
|
|
*/
|