#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <stdarg.h>
#include <limits.h>
#include <time.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#define FILE_ALL 0001 /* -a */
#define FILE_LON 0002 /* -l */
#define FILE_TIM 0004 /* -t */
#define FILE_SIZ 0010 /* -s */
#define FILE_UND 0020 /* -h */
#define FILE_REV 0040 /* -r */
#define FILE_REC 0100 /* -r */
#define FILE_NUM 0200 /* -n */
#define FILE_INO 0400 /* -i */
#define GB (1024 * 1024 * 1024)
#define MB (1024 * 1024)
#define KB (1024)
#define RED "\033[31m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
#define MAGENTA "\033[35m"
#define CYAN "\033[36m"
#define END "\033[0m"
struct statf {
struct stat statbuf;
char buf[FILENAME_MAX + 1];
char path[PATH_MAX + 1];
};
int (*cmp)(const void *, const void *);
int filecmp(const void *, const void *);
int timecmp(const void *, const void *);
int sizecmp(const void *, const void *);
void get_fname_width(int ws_col, struct statf *statfp[], int fn);
void get_other_width(struct statf *statfp[], int fn);
void deal_argfile(int argc, char **argv);
void show_dir(char *pathname, int fn);
void show_single_dir(struct statf *statfp[], int fn);
int get_fn(char *pathname);
void show_align(int ws_col, struct statf *staf);
void show_attribute(struct statf *staf);
void fsize_trans(off_t size);
void err_exit(char *fmt, ...);
#endif /* _LS_H */
#include "ls.h"
static int option = 0;
static int fname_width;
static int fsize_width;
static int flink_width;
static int funame_width;
static int fgname_width;
static int fuid_width;
static int fgid_width;
static int finode_width;
int main(int argc, char **argv)
{
int c;
while (--argc > 0 && (*++argv)[0] == '-')
while ((c = *++argv[0]))
switch (c) {
case 'a':
option |= FILE_ALL;
break;
case 'l':
option |= FILE_LON;
break;
case 't':
option |= FILE_TIM;
break;
case 'S':
option |= FILE_SIZ;
break;
case 'h':
option |= FILE_UND;
break;
case 'r':
option |= FILE_REV;
break;
case 'R':
option |= FILE_REC;
break;
case 'n':
option |= FILE_NUM;
break;
case 'i':
option |= FILE_INO;
break;
default:
err_exit("usage: a.out [-ahilnRrSt] [directories or files...]");
break;
}
if (argc > 0)
deal_argfile(argc, argv);
else
show_dir(".", get_fn("."));
exit(0);
}
void deal_argfile(int argc, char **argv)
{
DIR *dp;
char *dirbuf[argc];
struct statf *statfp[argc];
int dn, fn, i;
dn = fn = 0;
while (argc-- > 0)
if ((dp = opendir(*argv)) == NULL) {
if ((statfp[fn] = malloc(sizeof(struct statf))) == NULL)
err_exit("%s", strerror(errno));
if (lstat(*argv, &statfp[fn]->statbuf) < 0)
err_exit("%s", strerror(errno));
strcpy(statfp[fn++]->buf, *argv++);
} else {
closedir(dp);
if ((dirbuf[dn++] = strdup(*argv++)) == NULL)
err_exit("%s", strerror(errno));
}
if (fn > 0) {
show_single_dir(statfp, fn);
if (dn > 0)
putchar('\n');
for (i = 0; i < fn; i++)
free(statfp[i]);
}
for (i = 0; i < dn; i++)
show_dir(dirbuf[i], get_fn(dirbuf[i]));
}
int get_fn(char *pathname)
{
DIR *dp;
struct dirent *dirp;
int fn;
fn = 0;
if ((dp = opendir(pathname)) == NULL)
err_exit("%s", strerror(errno));
printf(MAGENTA"%-s:\n"END, pathname);
while ((dirp = readdir(dp)) != NULL) {
if (!(option & FILE_ALL))
if (dirp->d_name[0] == '.')
continue;
fn++;
}
closedir(dp);
return fn;
}
void show_dir(char *pathname, int fn)
{
DIR *dp;
struct dirent *dirp;
struct statf *statfp[fn];
int i;
for (i = 0; i < fn; i++)
if ((statfp[i] = malloc(sizeof(struct statf))) == NULL)
err_exit("%s", strerror(errno));
if ((dp = opendir(pathname)) == NULL)
err_exit("%s", strerror(errno));
for (i = 0; (dirp = readdir(dp)) != NULL; i++) {
if (!(option & FILE_ALL))
if (dirp->d_name[0] == '.') {
i--;
continue;
}
strcpy(statfp[i]->buf, dirp->d_name);
strcpy(statfp[i]->path, pathname);
strcat(statfp[i]->path, "/");
strcat(statfp[i]->path, statfp[i]->buf);
if (lstat(statfp[i]->path, &statfp[i]->statbuf) < 0)
err_exit("%s", strerror(errno));
}
closedir(dp);
show_single_dir(statfp, fn);
if (option & FILE_REC)
for (i = 0; i < fn; i++)
if (strcmp(statfp[i]->buf, ".") != 0
&& strcmp(statfp[i]->buf, "..") != 0
&& S_ISDIR(statfp[i]->statbuf.st_mode))
show_dir(statfp[i]->path, get_fn(statfp[i]->path));
for (i = 0; i < fn; i++)
free(statfp[i]);
}
void show_single_dir(struct statf *statfp[], int fn)
{
struct winsize size;
int i;
if (isatty(1)) {
if (ioctl(1, TIOCGWINSZ, &size) < 0)
err_exit("%s", strerror(errno));
get_fname_width(size.ws_col, statfp, fn);
} else
fname_width = 0;
get_other_width(statfp, fn);
cmp = ((option & FILE_SIZ) ? sizecmp : ((option & FILE_TIM) ? timecmp : filecmp));
qsort(statfp, fn, sizeof(statfp[0]), cmp);
for (i = 0; i < fn; i++)
if (option & (FILE_LON | FILE_NUM))
show_attribute(statfp[i]);
else
show_align(size.ws_col, statfp[i]);
}
void get_fname_width(int ws_col, struct statf *statfp[], int fn)
{
fname_width = 0;
for (int i = 0; i < fn; i++)
if (strlen(statfp[i]->buf) > fname_width)
fname_width = strlen(statfp[i]->buf);
}
void get_other_width(struct statf *statfp[], int fn)
{
char tmp[100];
struct passwd *usr;
struct group *grp;
fsize_width = funame_width = fgname_width = fuid_width = fgid_width = finode_width = 0;
for (int i = 0; i < fn; i++) {
sprintf(tmp, "%lld", statfp[i]->statbuf.st_size);
if (strlen(tmp) > fsize_width)
fsize_width = strlen(tmp);
sprintf(tmp, "%d", statfp[i]->statbuf.st_nlink);
if (strlen(tmp) > flink_width)
flink_width = strlen(tmp);
sprintf(tmp, "%u", statfp[i]->statbuf.st_uid);
if (strlen(tmp) > fuid_width)
fuid_width = strlen(tmp);
sprintf(tmp, "%u", statfp[i]->statbuf.st_gid);
if (strlen(tmp) > fgid_width)
fgid_width = strlen(tmp);
sprintf(tmp, "%llu", statfp[i]->statbuf.st_ino);
if (strlen(tmp) > finode_width)
finode_width = strlen(tmp);
if ((usr = getpwuid(statfp[i]->statbuf.st_uid)) == NULL)
err_exit("%s", strerror(errno));
if (strlen(usr->pw_name) > funame_width)
funame_width = strlen(usr->pw_name);
if ((grp = getgrgid(statfp[i]->statbuf.st_gid)) == NULL)
err_exit("%s", strerror(errno));
if (strlen(grp->gr_name) > fgname_width)
fgname_width = strlen(grp->gr_name);
}
}
void show_align(int ws_col, struct statf *staf)
{
static int row;
if (fname_width) {
if (option & FILE_INO) {
if ((row += (fname_width + finode_width)) > ws_col) {
putchar('\n');
row = fname_width + finode_width + 1;
} else
row += 1;
} else {
if ((row += fname_width) > ws_col) {
putchar('\n');
row = fname_width + 1;
} else
row += 1;
}
}
if (option & FILE_INO)
printf("%*llu ", finode_width, staf->statbuf.st_ino);
if (S_ISREG(staf->statbuf.st_mode))
printf(CYAN"%-*s "END, fname_width, staf->buf);
else if (S_ISDIR(staf->statbuf.st_mode))
printf(MAGENTA"%-*s "END, fname_width, staf->buf);
else if (S_ISLNK(staf->statbuf.st_mode))
printf(YELLOW"%-*s "END, fname_width, staf->buf);
else if (S_ISCHR(staf->statbuf.st_mode))
printf(RED"%-*s "END, fname_width, staf->buf);
else if (S_ISBLK(staf->statbuf.st_mode))
printf(RED"%-*s "END, fname_width, staf->buf);
else if (S_ISFIFO(staf->statbuf.st_mode))
printf(CYAN"%-*s "END, fname_width, staf->buf);
else if (S_ISSOCK(staf->statbuf.st_mode))
printf(MAGENTA"%-*s "END, fname_width, staf->buf);
if(!fname_width)
putchar('\n');
}
void show_attribute(struct statf *staf)
{
struct passwd *usr;
struct group *grp;
char *timeptr;
if (option & FILE_INO)
printf("%*llu ", finode_width, staf->statbuf.st_ino);
if (S_ISREG(staf->statbuf.st_mode))
printf("-");
else if (S_ISDIR(staf->statbuf.st_mode))
printf("d");
else if (S_ISLNK(staf->statbuf.st_mode))
printf("l");
else if (S_ISCHR(staf->statbuf.st_mode))
printf("c");
else if (S_ISBLK(staf->statbuf.st_mode))
printf("b");
else if (S_ISFIFO(staf->statbuf.st_mode))
printf("f");
else if (S_ISSOCK(staf->statbuf.st_mode))
printf("s");
printf("%c", staf->statbuf.st_mode & S_IRUSR ? 'r' : '-');
printf("%c", staf->statbuf.st_mode & S_IWUSR ? 'w' : '-');
printf("%c", staf->statbuf.st_mode & S_IXUSR ? 'x' : '-');
printf("%c", staf->statbuf.st_mode & S_IRGRP ? 'r' : '-');
printf("%c", staf->statbuf.st_mode & S_IWGRP ? 'w' : '-');
printf("%c", staf->statbuf.st_mode & S_IXGRP ? 'x' : '-');
printf("%c", staf->statbuf.st_mode & S_IROTH ? 'r' : '-');
printf("%c", staf->statbuf.st_mode & S_IWOTH ? 'w' : '-');
printf("%c", staf->statbuf.st_mode & S_IXOTH ? 'x' : '-');
printf(" %*d ", flink_width, staf->statbuf.st_nlink);
if (option & FILE_NUM) {
printf("%*d ", fuid_width, staf->statbuf.st_uid);
printf("%*d ", fgid_width, staf->statbuf.st_gid);
} else {
if ((usr = getpwuid(staf->statbuf.st_uid)) == NULL)
err_exit("%s", strerror(errno));
if ((grp = getgrgid(staf->statbuf.st_gid)) == NULL)
err_exit("%s", strerror(errno));
printf("%-*s ", funame_width, usr->pw_name);
printf("%-*s ", fgname_width, grp->gr_name);
}
if (option & FILE_UND)
fsize_trans(staf->statbuf.st_size);
else
printf("%*lld ", fsize_width, staf->statbuf.st_size);
if ((timeptr = ctime(&staf->statbuf.st_mtime)) == NULL)
err_exit("%s", strerror(errno));
if (timeptr[strlen(timeptr) - 1] == '\n')
timeptr[strlen(timeptr) - 1] = '\0';
printf("%s ", timeptr);
if (S_ISREG(staf->statbuf.st_mode))
printf(CYAN"%-s"END, staf->buf);
else if (S_ISDIR(staf->statbuf.st_mode))
printf(MAGENTA"%-s"END, staf->buf);
else if (S_ISLNK(staf->statbuf.st_mode))
printf(YELLOW"%-s"END, staf->buf);
else if (S_ISCHR(staf->statbuf.st_mode))
printf(RED"%-s"END, staf->buf);
else if (S_ISBLK(staf->statbuf.st_mode))
printf(RED"%-s"END, staf->buf);
else if (S_ISFIFO(staf->statbuf.st_mode))
printf(CYAN"%-s"END, staf->buf);
else if (S_ISSOCK(staf->statbuf.st_mode))
printf(MAGENTA"%-s"END, staf->buf);
putchar('\n');
}
void fsize_trans(off_t size)
{
if (size / GB)
printf("%4.1lfG ", 1.0 * size / GB);
else if (size / MB)
printf("%4.1lfM ", 1.0 * size / MB);
else if (size / KB)
printf("%4.1lfK ", 1.0 * size / KB);
else
printf("%4lldB ", size);
}
int filecmp(const void *s, const void *t)
{
return strcmp((*(struct statf **)s)->buf
, (*(struct statf **)t)->buf)
* ((option & FILE_REV) ? -1 : 1);
}
int timecmp(const void *s, const void *t)
{
return ((*(struct statf **)s)->statbuf.st_mtime
- (*(struct statf **)t)->statbuf.st_mtime)
* ((option & FILE_REV) ? -1 : 1);
}
int sizecmp(const void *s, const void *t)
{
return ((*(struct statf **)s)->statbuf.st_size
- (*(struct statf **)t)->statbuf.st_size)
* ((option & FILE_REV) ? -1 : 1);
}
void err_exit(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}