aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/data_management.c57
-rw-r--r--src/dir_monitor.c5
-rw-r--r--src/options.c10
-rw-r--r--src/output.c140
4 files changed, 180 insertions, 32 deletions
diff --git a/src/data_management.c b/src/data_management.c
index 1ee8134..a6d8a0b 100644
--- a/src/data_management.c
+++ b/src/data_management.c
@@ -9,7 +9,10 @@
#include <string.h>
#include <errno.h>
#include <dirent.h>
+#include <unistd.h>
+#include <stdbool.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include "output.h"
#include "data_management.h"
@@ -50,6 +53,7 @@ inline struct list_node *create_node(char *fname, struct stat *ln_stat) {
length = 255;
}
node->next = NULL;
+ node->symlink_target = NULL;
explicit_bzero(node->fname, 256);
memcpy(node->fname, fname, length);
memcpy(&(node->ln_stat), ln_stat, sizeof(struct stat));
@@ -64,10 +68,16 @@ inline void destroy_list(struct list_head *list) {
while (list->first != NULL) {
ptr = list->first->next;
if (ptr == NULL) {
+ if (list->first->symlink_target != NULL) {
+ free(list->first->symlink_target);
+ }
free(list->first);
break;
}
list->first->next = ptr->next;
+ if (ptr->symlink_target != NULL) {
+ free(ptr->symlink_target);
+ }
free(ptr);
}
free(list);
@@ -193,6 +203,35 @@ inline struct list_head *create_list_sort_reversed(struct list_head *list) {
return lh;
}
+inline char *get_symlink_target_name(char *symlink, struct stat st) {
+ char *name = NULL;
+ ssize_t name_length = 0;
+ size_t tmp = 0;
+
+ if ( symlink == NULL || (! S_ISLNK(st.st_mode)) ) {
+ return NULL;
+ }
+
+ /* Suppressing warning __off_t to size_t only with the failsafe check */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+ tmp = ( (st.st_size<0) ? 0 : st.st_size+1 );
+#pragma GCC diagnostic pop
+ if (tmp == 0) { return NULL; }
+
+ if ((name = calloc(tmp, sizeof(char))) == NULL) {
+ LOGERR("ERROR: Failed to allocate heap memory.\n");
+ return NULL;
+ }
+
+ if ((name_length = readlink(symlink, name, tmp)) == -1) {
+ LOGERR("ERROR: readlink(\"%s\", name, %lu) failed: %s (errno %d)\n",
+ symlink, (st.st_size+1), strerror(errno), errno);
+ free(name);
+ return NULL;
+ }
+ return name;
+}
/* This function contains the only time critical code. The loop over
* the directory content.
@@ -252,6 +291,9 @@ struct list_head *get_data_from_directory(char *path) {
LOGERR("ERROR: Skipping entry %s\n", de->d_name);
continue;
}
+ if (option_resolve_symlinks && S_ISLNK(stat_res.st_mode)) {
+ tmp->symlink_target = get_symlink_target_name(fullpath, stat_res);
+ }
/* Performance: Put every node on the stack and do the sorting afterwards */
tmp->next = lnstack;
lnstack = tmp;
@@ -286,3 +328,18 @@ struct list_head *get_data_from_directory(char *path) {
return list;
}
+bool contains_resolved_symlinks(struct list_head *list) {
+ struct list_node *ptr = NULL;
+
+ if (list == NULL) { return false; }
+
+ ptr = list->first;
+ while (ptr != NULL) {
+ if (ptr->symlink_target != NULL)
+ return true;
+ ptr = ptr->next;
+ }
+
+ return false;
+}
+
diff --git a/src/dir_monitor.c b/src/dir_monitor.c
index 935aa74..419b9f4 100644
--- a/src/dir_monitor.c
+++ b/src/dir_monitor.c
@@ -39,6 +39,11 @@ int main(int argc, char **argv) {
list = get_data_from_directory(".");
}
+ /* Make output code simpler, check whether there were any symlinks resolved */
+ if (option_resolve_symlinks) {
+ option_resolve_symlinks = contains_resolved_symlinks(list);
+ }
+
if (option_format_string == NULL) {
print_list(list);
} else {
diff --git a/src/options.c b/src/options.c
index b975794..235792d 100644
--- a/src/options.c
+++ b/src/options.c
@@ -23,6 +23,7 @@ struct option long_options[] = {
{ "long-timestamp", no_argument, 0, 0 },
{ "print-boxed", no_argument, 0, 0 },
{ "print-header", no_argument, 0, 0 },
+ { "resolve-symlinks", no_argument, 0, 0 },
{ "reverse-sort", no_argument, 0, 0 },
{ "show-hidden-entries", no_argument, 0, 0 },
{ "sort-by", required_argument, 0, 0 },
@@ -34,6 +35,7 @@ bool option_sort_reverse_order = false;
enum esort_type option_sort_type = SORT_BY_SIZE;
bool option_print_boxed_table = false;
bool option_print_header = false;
+bool option_resolve_symlinks = false;
bool option_show_hidden_entries = false;
bool option_timestamp_long = false;
char *option_format_string = NULL;
@@ -63,6 +65,8 @@ void usage(char *executable) {
"Print a boxed table");
fprintf(stderr, " %-25s %2s %10s - %s\n", "--print-header", "-H", "",
"Print a header above the columns");
+ fprintf(stderr, " %-25s %2s %10s - %s\n", "--resolve-symlinks", "", "",
+ "the destination (name) of a symlink is resolved");
fprintf(stderr, " %-25s %2s %10s - %s\n", "--reverse-sort", "", "",
"Sort reversed");
fprintf(stderr, " %-25s %2s %10s - %s\n", "--show-hidden-entries", "-a", "",
@@ -81,6 +85,7 @@ void usage(char *executable) {
fprintf(stderr, " C - change time\n");
fprintf(stderr, " G - group name\n");
fprintf(stderr, " g - group id\n");
+ fprintf(stderr, " l - symlink (--resolve-symlinks required)\n");
fprintf(stderr, " M - modification time\n");
fprintf(stderr, " n - file name\n");
fprintf(stderr, " p - permissions\n");
@@ -111,6 +116,11 @@ void set_option(const char *option_name, char *option_argument) {
return;
}
+ if (strcmp("resolve-symlinks", option_name) == 0) {
+ option_resolve_symlinks = true;
+ return;
+ }
+
if (strcmp("reverse-sort", option_name) == 0) {
option_sort_reverse_order = true;
return;
diff --git a/src/output.c b/src/output.c
index f94e3c9..a928140 100644
--- a/src/output.c
+++ b/src/output.c
@@ -37,22 +37,26 @@
#define out_vsep() fputc(box_vsep, stdout)
#define out_print_newline() fputc('\n', stdout)
-#define out_print_fname(x) printf(" %s ", x->fname)
-#define out_print_uid(x) printf(" %4u ", x->ln_stat.st_gid)
-#define out_print_gid(x) printf(" %4u ", x->ln_stat.st_uid)
+void out_print_fname(struct list_node *ptr, size_t longest_fname);
+void out_print_gid(struct list_node *ptr);
void out_print_group_name(struct list_node *ptr);
void out_print_permissions(struct list_node *ptr);
void out_print_size(struct list_node *ptr);
+void out_print_symlink(struct list_node *ptr, size_t longest_fname);
void out_print_time(time_t tv);
void out_print_time_by_option(struct list_node *ptr);
void out_print_type(struct list_node *ptr);
+void out_print_uid(struct list_node *ptr);
void out_print_user_name(struct list_node *ptr);
-void out_print_format_string_header(const char *format, size_t longest_fname);
+void out_print_format_string_header(const char *format,
+ size_t longest_fname, size_t longest_symlink);
-size_t estimate_formatted_line_length(const char *format, size_t longest_fname);
+size_t estimate_formatted_line_length(const char *format,
+ size_t longest_fname, size_t longest_symlink);
size_t get_longest_filename(struct list_head *list);
+size_t get_longest_symlink(struct list_head *list);
char *global_sep_line = NULL;
@@ -102,7 +106,7 @@ inline int fputc_width_x(char c, size_t x, FILE *fdout) {
inline size_t get_longest_filename(struct list_head *list) {
size_t rc = 4, tmp = 0;
struct list_node *ptr = NULL;
- if (list == NULL) { return 0; }
+ if (list == NULL) { return rc; }
ptr = list->first;
while (ptr != NULL) {
tmp = strlen(ptr->fname);
@@ -112,8 +116,66 @@ inline size_t get_longest_filename(struct list_head *list) {
return rc;
}
+/* basic 7 due to formatted output */
+inline size_t get_longest_symlink(struct list_head *list) {
+ size_t rc = 7, tmp = 0;
+ struct list_node *ptr = NULL;
+ if (list == NULL) { return rc; }
+ ptr = list->first;
+ while(ptr != NULL) {
+ if (ptr->symlink_target != NULL) {
+ tmp = strlen(ptr->symlink_target);
+ } else { tmp = 0; }
+ if (tmp>rc) { rc = tmp; }
+ ptr = ptr->next;
+ }
+ return rc;
+}
+
/* === FORMATTING RELATED FUNCTIONS === */
+
+inline void out_print_uid(struct list_node *ptr) {
+ printf(" %4u ", ptr->ln_stat.st_gid);
+ if (option_print_boxed_table) { out_vsep(); }
+}
+
+
+inline void out_print_gid(struct list_node *ptr) {
+ printf(" %4u ", ptr->ln_stat.st_uid);
+ if (option_print_boxed_table) { out_vsep(); }
+}
+
+
+inline void out_print_fname(struct list_node *ptr, size_t longest_fname) {
+ size_t i = 0;
+ printf(" %s ", ptr->fname);
+ if (option_print_boxed_table) {
+ for (i=0; i<(longest_fname - strlen(ptr->fname)); i++) {
+ fputc(' ', stdout);
+ }
+ out_vsep();
+ }
+}
+
+
+inline void out_print_symlink(struct list_node *ptr, size_t longest_symlink) {
+ size_t i = 0, sll = 0;
+ if (ptr->symlink_target != NULL) {
+ sll = strlen(ptr->symlink_target);
+ printf(" %s ", ptr->symlink_target);
+ } else {
+ printf(" ");
+ }
+ if (option_print_boxed_table) {
+ for (i=0; i<(longest_symlink - sll); i++) {
+ fputc(' ', stdout);
+ }
+ out_vsep();
+ }
+}
+
+
/* Be aware: getting the name via uid takes a lot of time depending on the
* systems setup. local, NIS, LDAP ...
* Caching happens outside the executable, minimal here for skipping funcion calls. */
@@ -152,6 +214,7 @@ inline void out_print_group_name(struct list_node *ptr) {
explicit_bzero(prev_group_name, 64);
memcpy(prev_group_name, group->gr_name, group_name_length);
printf(" %-10s ", prev_group_name);
+ if (option_print_boxed_table) { out_vsep(); }
}
/* Reminder: resolving may take some time even with cache in libraries,
@@ -191,6 +254,7 @@ inline void out_print_user_name(struct list_node *ptr) {
explicit_bzero(prev_user_name, 64);
memcpy(prev_user_name, pwd->pw_name, user_name_length);
printf(" %-10s ", prev_user_name);
+ if (option_print_boxed_table) { out_vsep(); }
}
inline void out_print_size(struct list_node *ptr) {
@@ -208,6 +272,7 @@ inline void out_print_size(struct list_node *ptr) {
}
}
printf(" %8ld %3s ", tmp, unit);
+ if (option_print_boxed_table) { out_vsep(); }
}
inline void out_print_time(time_t tv) {
@@ -222,6 +287,7 @@ inline void out_print_time(time_t tv) {
strftime(timestamp, sizeof(timestamp), "%H:%M:%S", tm);
printf(" %-8s ", timestamp);
}
+ if (option_print_boxed_table) { out_vsep(); }
}
inline void out_print_time_by_option(struct list_node *ptr) {
@@ -282,6 +348,7 @@ inline void out_print_type(struct list_node *ptr) {
}
/* The ones longer than 12 characters are rare. */
printf(" %-12s ", entry);
+ if (option_print_boxed_table) { out_vsep(); }
}
inline void out_print_permissions(struct list_node *ptr) {
@@ -327,11 +394,14 @@ inline void out_print_permissions(struct list_node *ptr) {
mode |= S_ISUID;
}
printf(" %4o ", mode);
+ if (option_print_boxed_table) { out_vsep(); }
}
+/* === END OF FORMATTING FUNCTIONS === */
+
/* Interpret the format string and based on it generate a size which will be
* able to store everything including visual additions. Use strlen later. */
-inline size_t estimate_formatted_line_length(const char *format, size_t longest_fname) {
+inline size_t estimate_formatted_line_length(const char *format, size_t longest_fname, size_t longest_symlink) {
size_t rc = 0, i=0, format_len=0;
static int login_name_max = -1;
if (login_name_max < 0) {
@@ -347,6 +417,7 @@ inline size_t estimate_formatted_line_length(const char *format, size_t longest_
format_len = strnlen(format, DM_OUT_FORMAT_MAX_LEN);
for (i=0; i<format_len; i++) {
switch (format[i]) {
+ case 'l': if (option_resolve_symlinks) { rc += longest_symlink+7; } break;
case 'n': rc += longest_fname+4; break;
case 'G':
case 'U': rc += ((unsigned int)login_name_max); break;
@@ -361,7 +432,9 @@ inline size_t estimate_formatted_line_length(const char *format, size_t longest_
return rc;
}
-inline void out_print_format_string_header(const char *format, size_t longest_fname) {
+
+inline void out_print_format_string_header(const char *format,
+ size_t longest_fname, size_t longest_symlink) {
size_t i=0, j=0, k=0, format_len=0, est_length=0, tmp = 0;
char *header = NULL, *sep_line = NULL;
char *pos = NULL;
@@ -371,7 +444,7 @@ inline void out_print_format_string_header(const char *format, size_t longest_fn
format_len = strnlen(format, DM_OUT_FORMAT_MAX_LEN);
- est_length = estimate_formatted_line_length(format, longest_fname);
+ est_length = estimate_formatted_line_length(format, longest_fname, longest_symlink);
if ((header=calloc(est_length+1, sizeof(char))) == NULL) {
LOGERR("ERROR: Failed to allocate memory for header\n");
return;
@@ -401,7 +474,7 @@ inline void out_print_format_string_header(const char *format, size_t longest_fn
for (i=0; (i < format_len) && (strlen(header) < 4064); i++) {
switch (format[i]) {
case 'n':
- sprintf(pos, " FILE ");
+ sprintf(pos, " %-4s ", "FILE");
if (option_print_boxed_table) {
tmp = strlen(pos);
for (k=0; k<longest_fname-4; k++) {
@@ -410,6 +483,18 @@ inline void out_print_format_string_header(const char *format, size_t longest_fn
}
LOCAL_EXTEND_SEP_LINE();
break;
+ case 'l':
+ if (option_resolve_symlinks) {
+ sprintf(pos, " %-7s ", "SYMLINK");
+ if (option_print_boxed_table) {
+ tmp = strlen(pos);
+ for (k=0; k<longest_symlink-7; k++) {
+ pos[tmp+k] = ' ';
+ }
+ }
+ LOCAL_EXTEND_SEP_LINE();
+ }
+ break;
case 's':
sprintf(pos, " %12s ", "SIZE ");
LOCAL_EXTEND_SEP_LINE();
@@ -500,8 +585,6 @@ inline void out_print_format_string_header(const char *format, size_t longest_fn
out_print_newline();
#undef LOCAL_EXTEND_SEP_LINE
}
-/* === END OF FORMATTING FUNCTIONS === */
-
/* The most simple default output. */
@@ -519,6 +602,7 @@ void print_list(struct list_head *list) {
* G - group name
* g - group id
* p - permissions and special bits
+ * l - symlink
*
* Time related
* t - timestamp set by option_time_field
@@ -529,8 +613,8 @@ void print_list(struct list_head *list) {
void print_list_formatted(const char *format, struct list_head *list) {
struct list_head *lh = list;
struct list_node *ptr;
- size_t format_len = 0, i = 0, j = 0;
- size_t longest_fname = 0;
+ size_t format_len = 0, i = 0;
+ size_t longest_fname = 0, longest_symlink = 0;
size_t total_size = 0;
if (format == NULL || format[0] == '\0') {
@@ -558,8 +642,12 @@ void print_list_formatted(const char *format, struct list_head *list) {
longest_fname = get_longest_filename(list);
+ if (option_resolve_symlinks) {
+ longest_symlink = get_longest_symlink(list);
+ }
+
if (option_print_header) {
- out_print_format_string_header(format, longest_fname);
+ out_print_format_string_header(format, longest_fname, longest_symlink);
}
ptr = lh->first;
@@ -568,57 +656,45 @@ void print_list_formatted(const char *format, struct list_head *list) {
for (i=0; i < format_len; i++) {
switch (format[i]) {
case 'n':
- out_print_fname(ptr);
- if (option_print_boxed_table) {
- for (j=0; j<(longest_fname - strlen(ptr->fname)); j++) {
- fputc(' ', stdout);
- }
- out_vsep();
+ out_print_fname(ptr, longest_fname);
+ break;
+ case 'l':
+ if (option_resolve_symlinks) {
+ out_print_symlink(ptr, longest_symlink);
}
break;
case 's':
out_print_size(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 't':
out_print_time_by_option(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'A':
out_print_time(ptr->ln_stat.st_atim.tv_sec);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'C':
out_print_time(ptr->ln_stat.st_ctim.tv_sec);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'M':
out_print_time(ptr->ln_stat.st_mtim.tv_sec);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'T':
out_print_type(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'u':
out_print_uid(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'g':
out_print_gid(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'U':
out_print_user_name(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'G':
out_print_group_name(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case 'p':
out_print_permissions(ptr);
- if (option_print_boxed_table) { out_vsep(); }
break;
case ' ': /* just ignore this without warning */
break;