Add symbolic link support to ls(1).
This commit is contained in:
parent
b1a2817ada
commit
2b2dd347ec
172
utils/ls.cpp
172
utils/ls.cpp
|
@ -24,10 +24,12 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -111,7 +113,7 @@ int argv_mtim_compare(const void* a_void, const void* b_void)
|
||||||
|
|
||||||
struct stat a_st;
|
struct stat a_st;
|
||||||
struct stat b_st;
|
struct stat b_st;
|
||||||
if ( stat(a, &a_st) == 0 && stat(b, &b_st) == 0 )
|
if ( lstat(a, &a_st) == 0 && lstat(b, &b_st) == 0 )
|
||||||
{
|
{
|
||||||
if ( a_st.st_mtim.tv_sec < b_st.st_mtim.tv_sec )
|
if ( a_st.st_mtim.tv_sec < b_st.st_mtim.tv_sec )
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -151,67 +153,149 @@ int sort_dirents(const void* a_void, const void* b_void)
|
||||||
return strcmp(a->d_name, b->d_name);
|
return strcmp(a->d_name, b->d_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned char mode_to_dt(mode_t mode)
|
||||||
|
{
|
||||||
|
if ( S_ISBLK(mode) )
|
||||||
|
return DT_BLK;
|
||||||
|
if ( S_ISCHR(mode) )
|
||||||
|
return DT_CHR;
|
||||||
|
if ( S_ISDIR(mode) )
|
||||||
|
return DT_DIR;
|
||||||
|
if ( S_ISFIFO(mode) )
|
||||||
|
return DT_FIFO;
|
||||||
|
if ( S_ISLNK(mode) )
|
||||||
|
return DT_LNK;
|
||||||
|
if ( S_ISREG(mode) )
|
||||||
|
return DT_REG;
|
||||||
|
if ( S_ISSOCK(mode) )
|
||||||
|
return DT_SOCK;
|
||||||
|
return DT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void color_entry(bool enable_colors,
|
||||||
|
const char** pre_ptr,
|
||||||
|
const char** post_ptr,
|
||||||
|
unsigned char type,
|
||||||
|
struct stat* st,
|
||||||
|
bool error_situation)
|
||||||
|
{
|
||||||
|
const char* pre = "";
|
||||||
|
const char* post = "";
|
||||||
|
|
||||||
|
if ( error_situation )
|
||||||
|
{
|
||||||
|
pre = "\e[31m";
|
||||||
|
post = "\e[m";
|
||||||
|
}
|
||||||
|
else if ( enable_colors )
|
||||||
|
{
|
||||||
|
post = "\e[m";
|
||||||
|
switch ( type )
|
||||||
|
{
|
||||||
|
case DT_UNKNOWN: pre = "\e[91m"; break;
|
||||||
|
case DT_BLK: pre = "\e[93m"; break;
|
||||||
|
case DT_CHR: pre = "\e[93m"; break;
|
||||||
|
case DT_DIR: pre = "\e[36m"; break;
|
||||||
|
case DT_FIFO: pre = "\e[33m"; break;
|
||||||
|
case DT_LNK: pre = "\e[96m"; break;
|
||||||
|
case DT_SOCK: pre = "\e[35m"; break;
|
||||||
|
case DT_REG:
|
||||||
|
if ( st && (st->st_mode & 0111) )
|
||||||
|
pre = "\e[32m";
|
||||||
|
else
|
||||||
|
post = "";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
post = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pre_ptr = pre;
|
||||||
|
*post_ptr = post;
|
||||||
|
}
|
||||||
|
|
||||||
int handle_entry_internal(const char* fullpath, const char* name, unsigned char type)
|
int handle_entry_internal(const char* fullpath, const char* name, unsigned char type)
|
||||||
{
|
{
|
||||||
// TODO: Use openat and fstat.
|
// TODO: Use openat and fstat.
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
memset(&st, 0, sizeof(st));
|
||||||
bool stat_error = false;
|
bool stat_error = false;
|
||||||
if ( (option_long_format || type == DT_UNKNOWN || type == DT_REG) )
|
if ( option_long_format ||
|
||||||
|
type == DT_UNKNOWN ||
|
||||||
|
type == DT_REG ||
|
||||||
|
type == DT_LNK )
|
||||||
{
|
{
|
||||||
if ( stat(fullpath, &st) == 0 )
|
if ( lstat(fullpath, &st) == 0 )
|
||||||
|
type = mode_to_dt(st.st_mode);
|
||||||
|
else
|
||||||
|
stat_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* link_dest = NULL;
|
||||||
|
bool link_stat_error = false;
|
||||||
|
struct stat link_st;
|
||||||
|
memset(&link_st, 0, sizeof(link_st));
|
||||||
|
unsigned char link_type = DT_UNKNOWN;
|
||||||
|
if ( type == DT_LNK )
|
||||||
|
{
|
||||||
|
size_t link_dest_length = 0 <= st.st_size ? st.st_size : 256;
|
||||||
|
link_dest = (char*) malloc(link_dest_length + 1);
|
||||||
|
assert(link_dest);
|
||||||
|
ssize_t readlink_ret = readlink(fullpath, link_dest, link_dest_length);
|
||||||
|
if ( readlink_ret <= 0 )
|
||||||
{
|
{
|
||||||
if ( S_ISBLK(st.st_mode) )
|
free(link_dest);
|
||||||
type = DT_BLK;
|
link_dest = NULL;
|
||||||
if ( S_ISCHR(st.st_mode) )
|
link_stat_error = true;
|
||||||
type = DT_CHR;
|
|
||||||
if ( S_ISDIR(st.st_mode) )
|
|
||||||
type = DT_DIR;
|
|
||||||
if ( S_ISFIFO(st.st_mode) )
|
|
||||||
type = DT_FIFO;
|
|
||||||
if ( S_ISLNK(st.st_mode) )
|
|
||||||
type = DT_LNK;
|
|
||||||
if ( S_ISREG(st.st_mode) )
|
|
||||||
type = DT_REG;
|
|
||||||
if ( S_ISSOCK(st.st_mode) )
|
|
||||||
type = DT_SOCK;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memset(&st, 0, sizeof(st));
|
link_dest[readlink_ret] = '\0';
|
||||||
stat_error = true;
|
const char* next_link_dest = link_dest;
|
||||||
|
char* new_link_dest = NULL;
|
||||||
|
if ( link_dest[0] != '/' )
|
||||||
|
{
|
||||||
|
char* fullpath_dir_raw = strdup(fullpath);
|
||||||
|
const char* fullpath_dir = dirname(fullpath_dir_raw);
|
||||||
|
asprintf(&new_link_dest, "%s/%s", fullpath_dir, link_dest);
|
||||||
|
assert(new_link_dest);
|
||||||
|
free(fullpath_dir_raw);
|
||||||
|
next_link_dest = new_link_dest;
|
||||||
|
}
|
||||||
|
if ( lstat(next_link_dest, &link_st) == 0 )
|
||||||
|
link_type = mode_to_dt(link_st.st_mode);
|
||||||
|
else
|
||||||
|
link_stat_error = true;
|
||||||
|
free(new_link_dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* color_pre = "";
|
const char* color_pre = "";
|
||||||
const char* color_post = "";
|
const char* color_post = "";
|
||||||
if ( option_colors )
|
color_entry(option_colors,
|
||||||
{
|
&color_pre,
|
||||||
color_post = "\e[m";
|
&color_post,
|
||||||
switch ( type )
|
type,
|
||||||
{
|
!stat_error ? &st : NULL,
|
||||||
case DT_UNKNOWN: color_pre = "\e[91m"; break;
|
link_stat_error);
|
||||||
case DT_BLK: color_pre = "\e[93m"; break;
|
|
||||||
case DT_CHR: color_pre = "\e[93m"; break;
|
const char* link_color_pre = "";
|
||||||
case DT_DIR: color_pre = "\e[36m"; break;
|
const char* link_color_post = "";
|
||||||
case DT_FIFO: color_pre = "\e[33m"; break;
|
if ( type == DT_LNK )
|
||||||
case DT_LNK: color_pre = "\e[96m"; break;
|
color_entry(option_colors,
|
||||||
case DT_SOCK: color_pre = "\e[35m"; break;
|
&link_color_pre,
|
||||||
case DT_REG:
|
&link_color_post,
|
||||||
if ( type == DT_REG && !stat_error && (st.st_mode & 0111) )
|
link_type,
|
||||||
color_pre = "\e[32m";
|
!link_stat_error ? &link_st : NULL,
|
||||||
else
|
link_stat_error);
|
||||||
color_post = "";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color_post = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( option_inode )
|
if ( option_inode )
|
||||||
printf("%ju ", (uintmax_t) st.st_ino);
|
printf("%ju ", (uintmax_t) st.st_ino);
|
||||||
if ( !option_long_format )
|
if ( !option_long_format )
|
||||||
{
|
{
|
||||||
printf("%s%s%s\n", color_pre, name, color_post);
|
printf("%s%s%s\n", color_pre, name, color_post);
|
||||||
|
free(link_dest);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
char perms[11];
|
char perms[11];
|
||||||
|
@ -249,12 +333,16 @@ int handle_entry_internal(const char* fullpath, const char* name, unsigned char
|
||||||
strftime(time_str, 64, "%b %e %H:%M", &mod_tm);
|
strftime(time_str, 64, "%b %e %H:%M", &mod_tm);
|
||||||
else
|
else
|
||||||
strftime(time_str, 64, "%b %e %Y", &mod_tm);
|
strftime(time_str, 64, "%b %e %Y", &mod_tm);
|
||||||
printf("%s %3ju root root %4ju%s %s %s%s%s\n",
|
printf("%s %3ju root root %4ju%s %s %s%s%s",
|
||||||
perms,
|
perms,
|
||||||
(uintmax_t) st.st_nlink,
|
(uintmax_t) st.st_nlink,
|
||||||
(uintmax_t) size, sizeunit,
|
(uintmax_t) size, sizeunit,
|
||||||
time_str,
|
time_str,
|
||||||
color_pre, name, color_post);
|
color_pre, name, color_post);
|
||||||
|
if ( type == DT_LNK && link_dest )
|
||||||
|
printf(" -> %s%s%s", link_color_pre, link_dest, link_color_post);
|
||||||
|
printf("\n");
|
||||||
|
free(link_dest);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue