#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dir_management.h"
#include "ls.h"
#include "MyLinkedList.h"
#include "print.h"
#include "utils.h"
void
process_directory(const char* dirpath)
{
DIR* dir = opendir(dirpath);
if (dir == NULL)
{
if (errno != ENAMETOOLONG)
{
fprintf(stderr, "%s: Could not open directory: %s: %s\n",
getprogname(), dirpath, strerror(errno));
return;
}
errno = 0;
int status = 0;
dir = opendir_cd_crawl(dirpath, &status);
if (status == SYMLINK_ENCOUNTERED)
{
return;
}
if (dir == NULL)
{
fprintf(stderr,
"%s: Could not open directory %s via cd crawl: %s\n",
getprogname(), dirpath, strerror(errno));
return;
}
}
LinkedList* entries = list_create(free_ls_entry);
if (entries == NULL)
{
fprintf(stderr, "%s: Could not create list for directory entries\n",
getprogname());
if (closedir(dir) == -1)
{
fprintf(stderr, "%s: Warning - Could not close directory %s: %s\n",
getprogname(), dirpath, strerror(errno));
}
return;
}
struct dirent* dent;
while ((dent = readdir(dir)) != NULL)
{
/* allow hidden and dot files */
if (ls_options.include_all_files)
{
/* allow all files */
}
else if (ls_options.include_hidden_files)
{
/* allow only hidden files, no dots */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
{
continue;
}
}
else /* don't allow either */
{
if (dent->d_name[0] == '.')
{
continue;
}
}
int dirlen = strlen(dirpath);
int is_slash = (dirpath[dirlen - 1] != '/');
size_t fullpath_size =
dirlen + strlen(dent->d_name) + (is_slash ? 2 : 1);
char* fullpath = malloc(fullpath_size);
if (fullpath == NULL)
{
fprintf(stderr,
"%s: Malloc failed while trying to allocate memory for "
"full path name: %s\n",
getprogname(), strerror(errno));
continue;
}
/* solely to address the bug where / was printed twice when traversing
* over the root directory */
if (!is_slash)
{
snprintf(fullpath, fullpath_size, "%s%s", dirpath, dent->d_name);
}
else
{
snprintf(fullpath, fullpath_size, "%s/%s", dirpath, dent->d_name);
}
struct stat st;
/* do not follow symlinks */
if (lstat(fullpath, &st) == -1)
{
if (errno != ENAMETOOLONG)
{
fprintf(stderr, "%s: Could not stat: %s: %s\n", getprogname(),
fullpath, strerror(errno));
free(fullpath);
continue;
}
/* Mark as not an error, for readability than anything */
errno = 0;
if (lstat_cd_crawl(fullpath, &st) == -1)
{
fprintf(stderr, "%s: Could not stat (after crawl): %s: %s\n",
getprogname(), fullpath, strerror(errno));
free(fullpath);
continue;
}
}
if (dent->d_type == DT_LNK && S_ISDIR(st.st_mode))
{
/*
* Don't follow symlinks.
* Prevents a weird recursion bug where the traversing/printing,
* which looks like:
* a/
* a/a
* a/a/b
* a/a/ <- wrong
* a/ <- wrong
* Only applies to the cd_opendir_crawl though...
*/
free(fullpath);
continue;
}
file_entry_t* fe = malloc(sizeof(file_entry_t));
if (fe == NULL)
{
fprintf(stderr,
"%s: Malloc failed while trying to allocate memory for "
"file entry: %s\n",
getprogname(), strerror(errno));
free(fullpath);
continue;
}
fe->pathname = fullpath;
fe->filename = strdup(dent->d_name);
fe->statbuf = st;
fe->is_cmdline_arg = 0;
if (list_add_last(entries, fe))
{
fprintf(stderr, "%s: Could not add entry '%s' to the list.\n",
getprogname(), fe->filename);
free_ls_entry(fe); /* also frees the fullpath malloced above */
continue;
}
}
if (closedir(dir) == -1)
{
fprintf(stderr, "%s: Warning - Could not close directory %s: %s\n",
getprogname(), dirpath, strerror(errno));
/*
* We don't necessarily need to return here per say,
* as this directory should never be accessed again,
* by this process, but either way the user should
* probably be made aware just in case.
*/
}
ls_sort_entries(entries);
if (ls_options.long_format ||
(ls_options.show_blocks_used && isatty(STDOUT_FILENO)))
{
char nice_buf[BUFSIZ];
long total = get_total_dir_blocks(entries);
if (ls_options.report_size_nicely)
{
if (humanize_number(nice_buf, HUMANIZE_BUFSIZ, total, "",
HN_AUTOSCALE, HUMANIZE_OPTIONS) == -1)
{
fprintf(stderr, "%s: Warning - could not humanize total.\n",
getprogname());
goto humanize_fallback;
}
}
else
{
humanize_fallback:
snprintf(nice_buf, sizeof(nice_buf), "%ld", total);
}
printf("total %s\n", nice_buf);
}
/* Try recurse into directories */
if (ls_options.recurse)
{
/*
* prints all files in that directory first before checking and
* traversing through subdirectories
*/
list_foreach(entries, reg_print_entry);
list_foreach_where(entries, process_entry, f_entry_is_dir);
}
else
{
list_foreach(entries, process_entry);
}
/* Deallocate all the malloced memory */
list_destroy(entries);
}
void
process_entry(void* ventry)
{
file_entry_t* entry = (file_entry_t*)ventry;
/* Prevent recursion into . and .. */
if (!entry->is_cmdline_arg && (strcmp(entry->filename, ".") == 0 ||
strcmp(entry->filename, "..") == 0))
{
if (!ls_options.recurse)
{
print_entry(entry->pathname, &entry->statbuf);
}
return;
}
if (S_ISDIR(entry->statbuf.st_mode) &&
!ls_options.directories_as_files_and_no_symlink_indir)
{
if (entry->is_cmdline_arg)
{
/* The ls -R command specifically acts different by showing
* the first directory name followed by a colon and newline
* if and only if there are > 1 valid arguments passed.
*/
if (ls_options.valid_arguments_passed > 1 &&
S_ISDIR(entry->statbuf.st_mode))
{
force_printf(entry->pathname, strlen(entry->pathname));
printf(":\n");
process_directory(entry->pathname);
printf("\n");
}
else
{
process_directory(entry->pathname);
}
}
else if (ls_options.recurse)
{
printf("\n\n");
force_printf(entry->pathname, strlen(entry->pathname));
printf(":\n");
process_directory(entry->pathname);
}
else
{
print_entry(entry->pathname, &entry->statbuf);
}
}
else
{
print_entry(entry->pathname, &entry->statbuf);
}
}