#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "dir_management.h"
#include "ls.h"
#include "MyLinkedList.h"
#include "print.h"
#include "utils.h"
int
ls_count_dirs(LinkedList* entries)
{
int count = 0;
Node* node = entries->head;
while (node != NULL)
{
file_entry_t* entry = (file_entry_t*)node->data;
if (S_ISDIR(entry->statbuf.st_mode))
{
count++;
}
node = node->next;
}
return count;
}
void
ls_sort_entries(LinkedList* entries)
{
if (ls_options.sort_none)
{
return;
}
if (ls_options.sort_size)
{
list_sort(entries, compare_file_size);
}
else if (ls_options.sort_time)
{
switch (ls_options.time_field)
{
case 'c':
list_sort(entries, compare_by_status_change_time);
break;
case 'u':
list_sort(entries, compare_by_access_time);
break;
case 'm':
default:
list_sort(entries, compare_by_modification_time);
break;
}
}
if (!ls_options.sort_size && !ls_options.sort_time)
{
list_sort(entries, compare_by_lexicographic);
}
if (ls_options.sort_reverse)
{
list_reverse(entries);
}
}
int
compare_by_lexicographic(void* a, void* b)
{
const file_entry_t* fa = (const file_entry_t*)a;
const file_entry_t* fb = (const file_entry_t*)b;
return strcmp(fa->filename, fb->filename);
}
int
compare_file_size(void* a, void* b)
{
file_entry_t* fa = (file_entry_t*)a;
file_entry_t* fb = (file_entry_t*)b;
if (fa->statbuf.st_size > fb->statbuf.st_size)
{
return -1;
}
if (fa->statbuf.st_size < fb->statbuf.st_size)
{
return 1;
}
/* Man page does not explicitly specify that if two files are of
* the same size that we should default to lexicographic.
* So in line with the spec, we shant.
*/
return 0;
}
int
compare_by_access_time(void* a, void* b)
{
file_entry_t* fa = (file_entry_t*)a;
file_entry_t* fb = (file_entry_t*)b;
if (fa->statbuf.st_atime > fb->statbuf.st_atime)
{
return -1;
}
if (fa->statbuf.st_atime < fb->statbuf.st_atime)
{
return 1;
}
return compare_by_lexicographic(a, b);
}
int
compare_by_status_change_time(void* a, void* b)
{
file_entry_t* fa = (file_entry_t*)a;
file_entry_t* fb = (file_entry_t*)b;
if (fa->statbuf.st_ctime > fb->statbuf.st_ctime)
{
return -1;
}
if (fa->statbuf.st_ctime < fb->statbuf.st_ctime)
{
return 1;
}
return compare_by_lexicographic(a, b);
}
int
compare_by_modification_time(void* a, void* b)
{
file_entry_t* fa = (file_entry_t*)a;
file_entry_t* fb = (file_entry_t*)b;
if (fa->statbuf.st_mtime > fb->statbuf.st_mtime)
{
return -1;
}
if (fa->statbuf.st_mtime < fb->statbuf.st_mtime)
{
return 1;
}
/*
* -t "Sort by time modified (most recently modified first)
* **BEFORE** sorting the operands by lexicographical order."
*/
return compare_by_lexicographic(a, b);
}
int
f_entry_is_dir(void* entry)
{
file_entry_t* file_entry = (file_entry_t*)entry;
return S_ISDIR(file_entry->statbuf.st_mode);
}
int
f_entry_isnt_dir(void* entry)
{
file_entry_t* file_entry = (file_entry_t*)entry;
return !S_ISDIR(file_entry->statbuf.st_mode);
}
void
free_ls_entry(void* entry)
{
file_entry_t* file_entry = (file_entry_t*)entry;
free(file_entry->pathname);
free(file_entry->filename);
free(file_entry);
}
long
get_blocks_from_sectors(struct stat* st)
{
long blocks_used = st->st_blocks;
long block_size_pref = ls_options.block_size;
long actual_bytes_used = st->st_size;
if (ls_options.reports_size_in_kilobytes)
{
block_size_pref = 1024;
}
/* -h flag specifies it should be in bytes */
if (ls_options.report_size_nicely)
{
return actual_bytes_used;
}
return (((blocks_used * BLOCKSIZE_DEFAULT) + block_size_pref - 1) /
block_size_pref);
}
long
get_total_dir_blocks(LinkedList* entries)
{
long total = 0;
Node* node = entries->head;
while (node != NULL)
{
file_entry_t* entry = (file_entry_t*)node->data;
total += ls_options.report_size_nicely ? entry->statbuf.st_size
: entry->statbuf.st_blocks;
node = node->next;
}
if (ls_options.report_size_nicely)
{
return total;
}
long block_size_pref = ls_options.block_size;
if (ls_options.reports_size_in_kilobytes)
{
block_size_pref = 1024;
}
return (((total * BLOCKSIZE_DEFAULT) + block_size_pref - 1) /
block_size_pref);
}
ssize_t
read_link_crawl(const char* linkpath, char* target, size_t target_size)
{
const char* ptr = linkpath;
char next_dir_buf[PATH_MAX];
char cwd[PATH_MAX];
ssize_t len = -1;
if (getcwd(cwd, sizeof(cwd)) == NULL)
{
fprintf(stderr, "%s: could not get current working directory: %s\n",
getprogname(), strerror(errno));
return -1;
}
/* skip the initial ./ */
if (ptr[0] == '.' && ptr[1] == '/')
{
ptr += 2;
}
while (*ptr != '\0')
{
const char* start = ptr;
while (*ptr != '\0' && *ptr != '/')
{
ptr++;
}
/* build the next path */
int count = ptr - start;
next_dir_buf[0] = '\0';
strncat(next_dir_buf, "./", strlen("./") + 1);
strncat(next_dir_buf, start, count);
if (*ptr == '\0')
{
len = readlink(next_dir_buf, target, target_size - 1);
if (len == -1)
{
fprintf(stderr, "%s: readlink failed on '%s': %s\n",
getprogname(), next_dir_buf, strerror(errno));
goto exit;
}
target[len] = '\0';
goto exit;
}
if (chdir(next_dir_buf) == -1)
{
fprintf(stderr, "%s: could not chdir to '%s': %s\n", getprogname(),
next_dir_buf, strerror(errno));
goto exit;
}
if (*ptr == '/')
{
ptr++;
}
}
exit:
if (chdir(cwd) == -1)
{
fprintf(stderr,
"%s: could not return to original directory '%s': %s. Cannot "
"proceed.\n",
getprogname(), cwd, strerror(errno));
exit(EXIT_FAILURE);
}
return len;
}
DIR*
opendir_cd_crawl(const char* dirpath, int* status)
{
const char* ptr = dirpath;
char next_dir_buf[PATH_MAX];
char cwd[PATH_MAX];
DIR* ret_val = NULL;
struct stat st;
*status = 0;
if (getcwd(cwd, sizeof(cwd)) == NULL)
{
fprintf(stderr, "%s: could not get current working directory: %s\n",
getprogname(), strerror(errno));
return NULL;
}
/* skip the initial ./ */
if (ptr[0] == '.' && ptr[1] == '/')
{
ptr += 2;
}
while (*ptr != '\0')
{
const char* start = ptr;
while (*ptr != '\0' && *ptr != '/')
{
ptr++;
}
/* build the next path */
int count = ptr - start;
next_dir_buf[0] = '\0';
strncat(next_dir_buf, "./", strlen("./") + 1);
strncat(next_dir_buf, start, count);
if (lstat(next_dir_buf, &st) == -1)
{
fprintf(stderr, "%s: lstat failed on '%s': %s\n", getprogname(),
next_dir_buf, strerror(errno));
goto exit;
}
if (S_ISLNK(st.st_mode))
{
*status = SYMLINK_ENCOUNTERED;
goto exit;
}
if (chdir(next_dir_buf) == -1)
{
fprintf(stderr, "%s: could not chdir to '%s': %s\n", getprogname(),
next_dir_buf, strerror(errno));
goto exit;
}
if (*ptr == '/')
{
ptr++;
}
}
if ((ret_val = opendir(".")) == NULL)
{
fprintf(stderr, "%s: could not opendir at end of crawl: %s\n",
getprogname(), strerror(errno));
}
exit:
if (chdir(cwd) == -1)
{
fprintf(stderr,
"%s: could not return to original directory '%s': %s. Cannot "
"proceed.\n",
getprogname(), cwd, strerror(errno));
exit(EXIT_FAILURE);
}
return ret_val;
}
int
lstat_cd_crawl(const char* dirpath, struct stat* st)
{
char cwd[PATH_MAX];
char next_dir_buf[PATH_MAX];
char last_entry[NAME_MAX + 1];
const char* ptr = dirpath;
int ret_val = -1;
if (getcwd(cwd, sizeof(cwd)) == NULL)
{
fprintf(stderr, "%s: could not get current working directory: %s\n",
getprogname(), strerror(errno));
return ret_val;
}
/* skip the initial ./ */
if (ptr[0] == '.' && ptr[1] == '/')
{
ptr += 2;
}
while (*ptr != '\0')
{
const char* start = ptr;
while (*ptr != '\0' && *ptr != '/')
{
ptr++;
}
int count = ptr - start;
if (*ptr == '\0')
{
if (count > NAME_MAX)
{
count = NAME_MAX;
}
strncpy(last_entry, start, count);
last_entry[count] = '\0';
break;
}
/* build the next path */
next_dir_buf[0] = '\0';
strncat(next_dir_buf, "./", strlen("./") + 1);
strncat(next_dir_buf, start, count);
if (chdir(next_dir_buf) == -1)
{
fprintf(stderr, "%s: could not chdir to %s: %s\n", getprogname(),
next_dir_buf, strerror(errno));
goto exit;
}
if (*ptr == '/')
{
ptr++;
}
}
if (lstat(last_entry, st) == -1)
{
fprintf(stderr, "%s: could not lstat '%s': %s\n", getprogname(),
last_entry, strerror(errno));
goto exit;
}
else
{
/* Success */
ret_val = 0;
}
exit:
if (chdir(cwd) == -1)
{
fprintf(stderr,
"%s: could not return to original directory '%s': %s. Cannot "
"proceed.\n",
getprogname(), cwd, strerror(errno));
exit(EXIT_FAILURE);
}
return ret_val;
}