From 6458a0f9e0da30a738153dd4041d52d0a10435f1 Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Tue, 16 May 2023 14:00:22 -0400 Subject: [PATCH] update ls --- command/ls.c | 633 +++++++++++++++++++++++++++++++--------------- compile_flags.txt | 1 - 2 files changed, 427 insertions(+), 207 deletions(-) diff --git a/command/ls.c b/command/ls.c index 517264b..788f2ff 100644 --- a/command/ls.c +++ b/command/ls.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include +/* quirky colors */ #define FILE_COLOR ANSCII BLACK COLOR #define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR #define DIR_COLOR_EXEC ANSCII BACKGROUND GREEN NEXT NORMAL BLACK COLOR @@ -22,36 +24,49 @@ #define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR #define SOCK_COLOR ANSCII BOLD NEXT NORMAL MAGENTA COLOR +/** + * Describes when to do something + */ enum When { - YES, - NO, - AUTO + YES, /* always */ + NO, /* never */ + AUTO /* when not tty */ }; +/** + * Flags that are to be used with ls + */ static struct { - bool hidden; - bool hide_dot; - bool more_info; - bool one_column; - bool recurse; - enum When colored; + bool hidden; /* -a */ + bool hide_dot; /* -A */ + bool more_info; /* -l */ + bool one_column; /* -1 */ + bool recurse; /* -R */ + bool octets; /* -o */ + enum When colored; /* --color=never/no/yes/always/auto */ } flags; +/** + * Keeps track of the files data + */ struct FileInfo { - struct passwd* usr; - struct group* grp; - char* name; - char date[13]; - char mode[11]; - char size[5]; - int links; - int bytes; - bool set_uid; - bool set_gid; - bool exec; - unsigned char type; + struct passwd* usr; /* the files owner user */ + struct group* grp; /* the files owner group */ + char* name; /* the file name */ + char date[13]; /* the files romanized date */ + char mode[11]; /* the files mode romanized or octets */ + char size[5]; /* the human readable file size */ + int links; /* the amount of inode links */ + int bytes; /* the amount of bytes in the file*/ + bool set_uid; /* if the file is set uid */ + bool set_gid; /* if the file is set gid */ + bool exec; /* if the binary is executable by the user running ls */ + unsigned char type; /* the type of the file */ }; +/** + * Keeps track of the biggest data to make the output data look pretty + */ struct FileListInfo { int max_link; int max_usr; @@ -62,6 +77,11 @@ struct FileListInfo { int total_size; }; +/** + * Gets a directory and fails if it cannot be opened + * @param path the path to the directory + * @returns the directory if found or NULL if not + */ static DIR* get_directory(char* path) { DIR* d = opendir(path); if (d == NULL) { @@ -74,6 +94,94 @@ static DIR* get_directory(char* path) { return d; } +/** + * Takes in a file and parses its mode to be humand readable + * @param info the file infomation to store the mode into + * @param mode the octet form of the mode + * @param type the type of the file + */ +static void parse_file_mode(struct FileInfo* info, mode_t mode, int type) { + switch (type) { + case DT_BLK: + info->mode[0] = 'b'; /* block device */ + break; + case DT_CHR: + info->mode[0] = 'c'; /* something i dunno */ + break; + case DT_DIR: + info->mode[0] = 'd'; /* directory */ + break; + case DT_FIFO: + info->mode[0] = 'f'; /* fifo */ + break; + case DT_LNK: + info->mode[0] = 'l'; /* symlink */ + break; + case DT_SOCK: + info->mode[0] = 's'; /* socket */ + break; + case DT_UNKNOWN: + info->mode[0] = 'u'; /* unkown (eye emoji) */ + break; + case DT_WHT: + info->mode[0] = 'w'; /* tf, wheat? idk */ + break; + default: + info->mode[0] = '-'; /* regular file */ + break; + } + + /* parse info for the user bits */ + info->mode[1] = (mode & S_IRUSR) ? 'r' : '-'; + info->mode[2] = (mode & S_IWUSR) ? 'w' : '-'; + if (mode & S_IXUSR && mode & S_ISUID) { + info->mode[3] = 's'; + } else if (mode & S_ISUID) { + info->mode[3] = 'S'; + } else if (mode & S_IXUSR) { + info->mode[3] = 'x'; + } else { + info->mode[3] = '-'; + } + + /* parse info for the group bits */ + info->mode[4] = (mode & S_IRGRP) ? 'r' : '-'; + info->mode[5] = (mode & S_IWGRP) ? 'w' : '-'; + if (mode & S_IXGRP && mode & S_ISGID) { + info->mode[6] = 's'; + } else if (mode & S_ISGID) { + info->mode[6] = 'S'; + } else if (mode & S_IXGRP) { + info->mode[6] = 'x'; + } else { + info->mode[6] = '-'; + } + + /* parse info for the other bits */ + info->mode[7] = (mode & S_IROTH) ? 'r' : '-'; + info->mode[8] = (mode & S_IWOTH) ? 'w' : '-'; + if (mode & S_IXOTH && mode & S_ISVTX) { + info->mode[9] = 't'; + } else if (mode & S_ISVTX) { + info->mode[9] = 'T'; + } else if (mode & S_IXOTH) { + info->mode[9] = 'x'; + } else { + info->mode[9] = '-'; + } + + /* make a null terminated string */ + info->mode[10] = '\0'; +} + +/** + * Gets information about a given file_name in a directory and + * stores it into a fileinfo struct + * @param file_name the name of the file in the directory currently + * stored in the path buffer + * @param info the file info struct to store the data into + * @returns if successfull in reading the file + */ static bool get_file_info(const char* file_name, struct FileInfo* info) { uid_t uid; @@ -85,412 +193,507 @@ static bool get_file_info(const char* file_name, struct FileInfo* info) { uid = getuid(); gid = getgid(); + /* default to all false */ memset(&s, 0, sizeof(struct stat)); + /* push the file to the path buffer to stat */ save = push_path_buffer(file_name); + /* get file stats */ if (lstat(get_path_buffer(), &s) < 0) { error_s("failed to read file '%s'", get_path_buffer()); pop_path_buffer(save); return false; } + /* get file type */ ty = (s.st_mode & S_IFMT) >> 12; - - info->set_uid = false; - info->set_gid = false; - info->exec = false; - switch (ty) { - case DT_BLK: - info->mode[0] = 'b'; - break; - case DT_CHR: - info->mode[0] = 'c'; - break; - case DT_DIR: - info->mode[0] = 'd'; - break; - case DT_FIFO: - info->mode[0] = 'f'; - break; - case DT_LNK: - info->mode[0] = 'l'; - break; - case DT_SOCK: - info->mode[0] = 's'; - break; - case DT_UNKNOWN: - info->mode[0] = 'u'; - break; - case DT_WHT: - info->mode[0] = 'w'; - break; - default: - info->mode[0] = '-'; - break; - } - - info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-'; - info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-'; - if (s.st_mode & S_IXUSR && s.st_mode & S_ISUID) { - info->mode[3] = 's'; - info->set_uid = true; - if (!info->exec) info->exec = info->usr->pw_uid == uid; - } else if (s.st_mode & S_ISUID) { - info->mode[3] = 'S'; - info->set_uid = true; - } else if (s.st_mode & S_IXUSR) { - info->mode[3] = 'x'; - if (!info->exec) info->exec = info->usr->pw_uid == uid; + /* parse long list mode as octets or human readable */ + if (flags.octets) { + info->mode[0] = '0' + (s.st_mode / (8 * 8 * 8) % 8); + info->mode[1] = '0' + (s.st_mode / (8 * 8 * 1) % 8); + info->mode[2] = '0' + (s.st_mode / (8 * 1 * 1) % 8); + info->mode[3] = '0' + (s.st_mode / (1 * 1 * 1) % 8); + info->mode[4] = '\0'; } else { - info->mode[3] = '-'; + parse_file_mode(info, s.st_mode, ty); } - info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-'; - info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-'; - if (s.st_mode & S_IXGRP && s.st_mode & S_ISGID) { - info->mode[6] = 's'; - info->set_gid = true; - if (!info->exec) info->exec = info->grp->gr_gid == gid; - } else if (s.st_mode & S_ISGID) { - info->mode[6] = 'S'; - info->set_gid = true; - } else if (s.st_mode & S_IXGRP) { - info->mode[6] = 'x'; - if (!info->exec) info->exec = info->grp->gr_gid == gid; - } else { - info->mode[6] = '-'; - } - - info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-'; - info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-'; - if (s.st_mode & S_IXOTH && s.st_mode & S_ISVTX) { - info->mode[9] = 't'; - info->set_gid = true; - info->exec = true; - } else if (s.st_mode & S_ISVTX) { - info->mode[9] = 'T'; - info->set_gid = true; - } else if (s.st_mode & S_IXOTH) { - info->mode[9] = 'x'; + /* check if the file is executable by the user */ + if ( + (s.st_mode & S_IXUSR && s.st_uid == uid) || + (s.st_mode & S_IXGRP && s.st_gid == gid) || + s.st_mode & S_IXOTH + ) { info->exec = true; } else { - info->mode[9] = '-'; + info->exec = false; } + + /* get setuid and setgid bits */ + info->set_uid = s.st_mode & S_ISUID; + info->set_gid = s.st_mode & S_ISGID; - info->mode[10] = '\0'; - + /* get user information */ info->usr = getpwuid(s.st_uid); if (info->usr == NULL) { - error_s("failed to get user from `%s`\n", get_path_buffer()); + error_s("failed to get user id: `%s`", get_path_buffer()); pop_path_buffer(save); return false; } + /* get group information */ info->grp = getgrgid(s.st_gid); if (info->grp == NULL) { - error_s("failed to get user from `%s`\n", get_path_buffer()); + error_s("failed to get user id: `%s`", get_path_buffer()); pop_path_buffer(save); return false; } + /* update inode links and file type */ info->links = s.st_nlink; info->type = ty; + /* copy the file name into the info */ file_len = strlen(file_name) + 1; info->name = xalloc(file_len); memcpy(info->name, file_name, file_len); + /* get the date and time for the file */ print_file_size(s.st_size, info->size); print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000000, info->date); - + + /* convert the bytes into human readable */ info->bytes = (s.st_size + s.st_blksize - 1) / s.st_blksize; + /* cleanup */ pop_path_buffer(save); return true; } +/** + * Gets the file color from its mode + * @pram info the file info to get the color from + * @return the anscii escape code for the file color + */ static char* get_file_color(struct FileInfo* info) { char* color; if (info->type == DT_DIR) { if (info->mode[8] == 'w') { - color = DIR_COLOR_EXEC; + color = DIR_COLOR_EXEC; /* directory is green if anyone can write it */ } else { - color = DIR_COLOR; + color = DIR_COLOR; /* other whise as the french say it, bleu */ } } else if (info->type == DT_LNK) { - color = LINK_COLOR; + color = LINK_COLOR; /* symlink color */ } else if (info->type == DT_SOCK) { - color = SOCK_COLOR; + color = SOCK_COLOR; /* sockt color */ } else if ( info->type == DT_CHR || info->type == DT_BLK ) { - color = BLK_COLOR; + color = BLK_COLOR; /* the weird ones are yellow, cool */ } else { if (info->set_uid) { - color = SET_UID_COLOR; + color = SET_UID_COLOR; /* hightlighted red if setuid */ } else if (info->set_gid) { - color = SET_GID_COLOR; + color = SET_GID_COLOR; /* highlighted yellow if setgid and not setuid */ } else if (info->exec) { - color = EXEC_COLOR; + color = EXEC_COLOR; /* green if executable */ } else { - color = FILE_COLOR; + color = FILE_COLOR; /* white if basic and boring */ } } return color; } -static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info) { - - struct winsize w; +/** + * Lists a file in long info mode + * @param info the global info for a directory + * @param finfo the file specific info + */ +static void list_file_long(struct FileListInfo* info, struct FileInfo* finfo) { char* color; - int column_width, row_count, i; + color = get_file_color(finfo); + + printf("%s %*d %*s %*s %*s %s %s%s%s", /* print data */ + finfo->mode, + info->max_link, + finfo->links, + info->max_usr, + finfo->usr->pw_name, + info->max_grp, + finfo->grp->gr_name, + info->max_size, + finfo->size, + finfo->date, + flags.colored != NO ? color : "", /* if not colored dont color */ + finfo->name, + flags.colored != NO ? "\x1b[0m" : "" + ); + + /* if it is a symlink, add the path where it points to */ + if (finfo->type == DT_LNK) { + int save = push_path_buffer(finfo->name); + + /* get the link */ + char lnk[PATH_MAX]; + ssize_t n; + if ((n = readlink(get_path_buffer(), lnk, PATH_MAX)) != -1) { + printf(" -> %.*s\n", (int)n, lnk); /* add if successfull */ + } else { + putchar('\n'); /* dont if not */ + } + + pop_path_buffer(save); /* cleanup */ + } else { + putchar('\n'); /* if not link put newline now */ + } +} + +/** + * Print one file in column mode + * @param finfo the file info to be printed + */ +static void list_file_column(struct FileInfo* finfo) { + char* color; + + color = get_file_color(finfo); + + printf("%s%s%s\n", /* print the data */ + flags.colored != NO ? color : "", finfo->name, /* if not colored dont color */ + flags.colored != NO ? "\x1b[0m" : "" + ); +} + +/** + * List a file normally (without -l or -1) + * @param info the global folder information + * @param finfo the file specific info + * @param tty_width the width of the tty + * @param index, the index in the direcory that has been printed + * @param column_width the width of the column + * @param row_count the count of rows + */ +static void list_file_normal( + struct FileListInfo* info, + struct FileInfo* finfo, + int tty_width, + int index, + int column_width, + int row_count +) { + char* color; + + color = get_file_color(finfo); /* get file color */ + + if (info->total_len > tty_width) { /* snap to column if the files span multiple lines */ + if (index != 0 && index % row_count == 0) putchar('\n'); /* if at the end of the column put a new line */ + printf("%s%*s%s", flags.colored != NO ? color : "", -column_width, + finfo->name, flags.colored != NO ? "\x1b[0m" : ""); /* if not colored dont color */ + } else { /* otherwise just print next to eachother seperated by two spaces */ + printf("%s%s%s ", flags.colored != NO ? color : "", finfo->name, + flags.colored != NO ? "\x1b[0m" : ""); + } +} + +/** + * Get the width of the tty, set to 0 if not a tty + * @param width the int pointer to store the width in + */ +static void get_window_size(int* width) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + if (!isatty(1)) { + *width = 0; + } else { + *width = w.ws_col; + } +} + +/** + * List a set of files + * @param files a array of file info structs containing file data + * @param file_len the amount of files + * @param info the global folder infomation + */ +static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info) { + + /* pre define variables to make gcc happy */ + int column_width = 0, row_count = 0, i = 0, width = 0; + get_window_size(&width); /* get screen width */ + + /* if your not a tty, set to column mode and disable color if not forced */ + if (width > 0) { + column_width = info.max_name + 1; + row_count = width / column_width; + } else { + flags.one_column = true; + if (flags.colored == AUTO) + flags.colored = NO; + } + + /* if set to long list, display folder size */ if (flags.more_info) { char total[13]; print_file_size(info.total_size, total); printf("total %s\n", total); } - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - - if (!isatty(1)) { - flags.one_column = true; - if (flags.colored == AUTO) - flags.colored = NO; - } - - column_width = info.max_name + 1; - row_count = w.ws_col / column_width; - + /* list files base on list mode */ for (i = 0; i < file_len; i++) { struct FileInfo finfo = files[i]; - color = get_file_color(&finfo); - if (flags.more_info) { - printf("%s %*d %*s %*s %*s %s %s%s%s", - finfo.mode, - info.max_link, - finfo.links, - info.max_usr, - finfo.usr->pw_name, - info.max_grp, - finfo.grp->gr_name, - info.max_size, - finfo.size, - finfo.date, - flags.colored != NO ? color : "", - finfo.name, - flags.colored != NO ? "\x1b[0m" : "" - ); - if (finfo.type == DT_LNK) { - int save = push_path_buffer(finfo.name); - - char lnk[PATH_MAX]; - ssize_t n; - if ((n = readlink(get_path_buffer(), lnk, PATH_MAX)) != -1) { - printf(" -> %.*s\n", (int)n, lnk); - } else { - putchar('\n'); - } - - pop_path_buffer(save); - } else { - putchar('\n'); - } - } else if (flags.one_column) { - printf("%s%s%s\n", flags.colored != NO ? color : "", finfo.name, flags.colored != NO ? "\x1b[0m" : ""); - } else { - if (info.total_len > w.ws_col) { - if (i != 0 && i % row_count == 0) putchar('\n'); - printf("%s%*s%s", flags.colored != NO ? color : "", -column_width, - finfo.name, flags.colored != NO ? "\x1b[0m" : ""); - } else { - printf("%s%s%s ", flags.colored != NO ? color : "", finfo.name, - flags.colored != NO ? "\x1b[0m" : ""); - } + if (flags.more_info) { /* -l long list */ + list_file_long(&info, &finfo); + } else if (flags.one_column) { /* -1 column list */ + list_file_column(&finfo); + } else { /* normal default listing */ + list_file_normal(&info, &finfo, width, i, column_width, row_count); } - free(finfo.name); + free(finfo.name); /* cleanup */ } - if (!flags.more_info) printf("\n"); + if (!flags.more_info) printf("\n"); /* only long long list prints a new line at the end */ } +/** + * Get the number of places a number fills + * @param n the number to check + * @returns the num of places the number filles + */ static int num_places (int n) { int r = 1; if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n; - while (n > 9) { + while (n > 9) { /* keep going for each place */ n /= 10; r++; } return r; } +/** + * Read and push a file info the given array + * @param file a pointer to the list of files + * @param info the global folder info to update as read + * @param size the amount of files currently in the array + * @param capacity the capacity of the array + * @param file_path the path of the file + */ static void push_file( struct FileInfo** files, struct FileListInfo* info, int* size, int* capacity, const char* file_path ) { + /* allocate variables on the stack */ struct FileInfo finfo; int user_len, group_len, name_len, size_len, link_len; + /* if you fail to get the file info, abort */ if (!get_file_info(file_path, &finfo)) return; + /* if the array is full realloc to make it bigger */ if (*size == *capacity) { *capacity *= 2; *files = xrealloc(*files, sizeof(struct FileInfo) * *capacity); } + /* update user name length if its larger than global */ user_len = strlen(finfo.usr->pw_name); if (user_len > info->max_usr) info->max_usr = user_len; + /* update group name length if its larger than global */ group_len = strlen(finfo.grp->gr_name); if (group_len > info->max_grp) info->max_grp = group_len; + /* update file name length if its larger than global */ name_len = strlen(file_path); if (name_len > info->max_name) info->max_name = name_len; + /* update file size human readable length if its larger than global */ size_len = strlen(finfo.size); if (size_len > info->max_size) info->max_size = size_len; + /* update inode link place count length if its larger than global */ link_len = num_places(finfo.links); if (link_len > info->max_link) info->max_link = link_len; + /* update final info */ info->total_len += name_len + 2; info->total_size += finfo.bytes; + /* insert file data into array */ (*files)[*size] = finfo; - (*size)++; + (*size)++; /* increment size counter */ } +/** + * When recursing, read all files and folders, recurses each folder, and prints each file + * @param dir_name the dir_name relative to the current path buffer + */ static void recurse_directory(char* dir_name) { + /* allocate variables on the stack */ DIR* d; int capacity, size, save; struct dirent* file; struct FileInfo* files; struct FileListInfo info; + /* push the dir name to the path buffer */ save = push_path_buffer(dir_name); + /* if failed to open dir abort */ d = get_directory(get_path_buffer()); if (d == NULL) { return; } + /* allocate initial array*/ capacity = 8; size = 0; files = xalloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); + memset(&info, 0, sizeof(struct FileListInfo)); /* zero out default directory globals */ + /* for each file read, recurse if directory, read file if file */ while((file = readdir(d)) != NULL) { - if (!flags.hidden && prefix(".", file->d_name)) continue; - if (flags.hide_dot && is_dot_dir(file->d_name)) continue; - if (file->d_type == DT_DIR && !is_dot_dir(file->d_name)) { - recurse_directory(file->d_name); + if (!flags.hidden && prefix(".", file->d_name)) continue; /* if not set to show hidden, skip */ + if (flags.hide_dot && is_dot_dir(file->d_name)) continue; /* if its set to hide dot skip */ + if (file->d_type == DT_DIR && !is_dot_dir(file->d_name)) { /* dont recurse dot files or scary infinite loop */ + recurse_directory(file->d_name); /* recurse if directory */ } else { - push_file(&files, &info, &size, &capacity, file->d_name); + push_file(&files, &info, &size, &capacity, file->d_name); /* read file if a file */ } } - - + + /* print dir name */ if (flags.colored == NO) { printf("\n%s:\n", get_path_buffer()); } else { printf("\n%s%s:%s\n", DIR_COLOR, get_path_buffer(), FILE_COLOR); } + /* list files */ list_files(files, size, info); - free(files); - - if (!flags.more_info) printf("\n"); - + free(files); /* clean up */ closedir(d); - pop_path_buffer(save); } +/** + * List directory non recursivly or recursivly + * @param path the name of the directory to search + */ static void list_directory(char* path) { + /* allocate variables on the stack */ DIR* d; int capacity, size, save; struct FileInfo* files; struct FileListInfo info; struct dirent* file; + /* if set to recurse, run the recurse function */ if (flags.recurse) { recurse_directory(path); return; } + /* if failed to open directory, abort */ d = get_directory(path); if (d == NULL) return; + /* push folder to path buffer */ save = push_path_buffer(path); + /* set array defaults */ capacity = 8; size = 0; - + + /* allocate file array */ files = xalloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); + memset(&info, 0, sizeof(struct FileListInfo)); /* zero out directory globals */ + /* add each file regardless if its a directory or file since not recursing */ while ((file = readdir(d)) != NULL) { - if (!flags.hidden && prefix(".", file->d_name)) continue; - if (flags.hide_dot && is_dot_dir(file->d_name)) continue; - push_file(&files, &info, &size, &capacity, file->d_name); + if (!flags.hidden && prefix(".", file->d_name)) continue; /* if not set to show hidden ship */ + if (flags.hide_dot && is_dot_dir(file->d_name)) continue; /* if set to hide dot skip */ + push_file(&files, &info, &size, &capacity, file->d_name); /* add file to array */ } + /* only list files if there are any to list */ if (size > 0) list_files(files, size, info); + + /* cleanup */ free(files); - - pop_path_buffer(save); - closedir(d); + pop_path_buffer(save); } +/* return if a path is a directory */ static bool is_dir(const char* path) { struct stat s; if (stat(path, &s) < 0) return false; return S_ISDIR(s.st_mode); } +/** + * Goes though each argument and prints the argument if it is a file + * @param start the argument to start at + * @param argc the argument count + * @param argv, the argument data + */ static void list_file_args(int start, int argc, char** argv) { + /* allocate variables on the stack */ int capacity, size, i; struct FileInfo* files; struct FileListInfo info; + /* array defaults */ capacity = 8; size = 0; - files = xalloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); + files = xalloc(sizeof(struct FileInfo) * capacity); /* allocate array */ + memset(&info, 0, sizeof(struct FileListInfo)); /* zero out globals */ for (i = start; i < argc; i++) { - if (is_dir(argv[i])) continue; + if (is_dir(argv[i])) continue; /* if the argument is a dir skip */ push_file(&files, &info, &size, &capacity, argv[i]); } + /* print file args */ if (size > 0) list_files(files, size, info); + /* clean up */ free(files); } +/** + * Prints help message for ls + */ static void help(void) { printf("Usage: ls [FILE]...\n\n"); printf("List directory contents\n\n"); printf("\t-1\tOne column output\n"); printf("\t-l\tLong format\n"); + printf("\t-o\tList raw octets instead of romanized file mode\n"); printf("\t-a\tInclude names starting with .\n"); printf("\t-A\tLike -a but without . and ..\n"); printf("\t-R\tRecurse\n"); } +/** + * Takes in each argument that has a single - and parses it + * @param c the character after the - + * @param next the next argument in argv that hasnt been parsed + * @reutrn if the next arg was used or if the arg was invalid + */ static int short_arg(char c, char* next) { - UNUSED(next); + UNUSED(next); /* next arg unused */ switch (c) { case 'R': flags.recurse = true; @@ -508,15 +711,24 @@ static int short_arg(char c, char* next) { case 'l': flags.more_info = true; break; + case 'o': + flags.octets = true; + break; default: return ARG_INVALID; } return ARG_UNUSED; } +/** + * Takes in each long argument (--) + * @param cur the current argument + * @param next the next argument in argv that hasnt been parsed + * @param if the next arg was used or if the arg was invalid + */ static int long_arg(char* cur, char* next) { - UNUSED(next); - if (prefix("--color=", cur)) { + UNUSED(next); /* next arg unused */ + if (prefix("--color=", cur)) { /* parse --color argument */ char* arg = cur + 8; if (streql("yes", arg) || streql("always", arg)) { flags.colored = YES; @@ -528,38 +740,47 @@ static int long_arg(char* cur, char* next) { error("invalid color options: %s", arg); } } else { - return ARG_IGNORE; + return ARG_IGNORE; /* stop reading arguments */ } - return ARG_UNUSED; + return ARG_UNUSED; /* return */ } +/** + * Lists a directorys contents + */ COMMAND(ls_main) { int start, i; bool titled; + /* initalize flag defaults */ flags.hidden = false; flags.more_info = false; flags.hide_dot = false; flags.one_column = false; flags.recurse = false; flags.colored = NO; + flags.octets = false; + /* parse given arguments */ start = parse_args(argc, argv, help, short_arg, long_arg); - + + /* if not arguments are given list current directory */ if (argc - start == 0) { list_directory("."); return EXIT_SUCCESS; } + /* list all arguments that are files */ list_file_args(start, argc, argv); + /* list each directory in the arguments */ titled = argc - start > 1; for (i = start; i < argc; i++) { - if (!is_dir(argv[i])) continue; + if (!is_dir(argv[i])) continue; /* if not directory skip */ - if (titled && !flags.recurse) { + if (titled && !flags.recurse) { /* list title of title is forced and not set to recurse*/ if (flags.colored != NO) { printf("\n%s%s:%s\n", DIR_COLOR, argv[i], FILE_COLOR); } else { @@ -567,8 +788,8 @@ COMMAND(ls_main) { } } - list_directory(argv[i]); + list_directory(argv[i]); /* parse and list directory */ } - return EXIT_SUCCESS; + return EXIT_SUCCESS; /* return */ } diff --git a/compile_flags.txt b/compile_flags.txt index 638047e..396bda2 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -7,7 +7,6 @@ -Isrc -Icommand -Ilib --Ihash -D_DEFAULT_SOURCE -DMAJOR=0 -DMINOR=0