1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright 2025 Thorsten Töpper
*
* dir_monitor - print specified stat() information from entries of a given
* directory to stdout. Call it via watch for repeated output to a terminal.
*
* vim:ts=4:sw=4:expandtab
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include "output.h"
#include "list_management.h"
#include "options.h"
/* === DEFINITIONS === */
struct list_head *get_data_from_directory(char *path);
/* === IMPLEMENTATION === */
/* This function contains the only time critical code. The loop over
* the directory content.
*
* Note for future:
* I myself use the dir_monitor in combination with watch on a directory
* containing temporary data, it may happen that a run into an error occurs,
* but it's not tragic. To reduce the chance of the error the first step
* would be to optimize the loop with putting the data into a list in stack
* form and moving the nodes into a sorted list afterwards.
*/
struct list_head *get_data_from_directory(char *path) {
char *fullpath = NULL, *fname_in_path = NULL;
DIR *dir = NULL;
size_t l;
struct dirent *de = NULL;
struct list_node *tmp = NULL;
struct list_head *list = NULL;
struct stat stat_res;
if (path == NULL) {
LOGERR("ERROR: No path given.\n");
return NULL;
}
l = strlen(path);
/* 258 filename and possibly required PATH_SEP and of course string termination */
if ((fullpath = calloc(l+258, sizeof(char))) == NULL) {
LOGERR("ERROR: Failed to allocate memory for list head.\n");
return NULL;
}
sprintf(fullpath, "%s%c", path, ((path[l-1] == PATH_SEP) ? '\0' : PATH_SEP));
fname_in_path = (fullpath[l] == PATH_SEP) ? &(fullpath[l+1]) : &(fullpath[l]) ;
if ((dir = opendir(path)) == NULL) {
LOGERR("ERROR: Failed to open directory '%s': %s (errno %d)\n",
path, strerror(errno), errno);
return NULL;
}
/* TBH: In case those few bytes are not available, the process will be killed anyways */
if ((list = calloc(1, sizeof(struct list_head))) == NULL) {
LOGERR("ERROR: Failed to allocate memory for list head.\n");
closedir(dir);
return NULL;
}
errno = 0;
/* Currently the code is specific UNIXoid, needs to be adjusted in case someone ports it. */
while ((de = readdir(dir)) != NULL) {
if (de->d_name[0] == '.' && option_show_hidden_entries == false)
continue;
sprintf(fname_in_path, "%s", de->d_name);
DBGTRC("DEBUG: fullpath: '%s'\n", fullpath);
if (lstat(fullpath, &stat_res) != 0) {
LOGERR("ERROR: lstat call on '%s' failed: %s (errno %d)\n",
fullpath, strerror(errno), errno);
continue;
}
if ((tmp = create_node(de->d_name, stat_res.st_size, stat_res.st_mtime)) == NULL) {
LOGERR("ERROR: Skipping entry %s\n", de->d_name);
continue;
}
/* SORT_BY_SIZE is the default value, always check it first. */
switch (option_sort_type) {
case SORT_BY_SIZE:
insert_sorted_by_size(list, tmp);
break;
case SORT_BY_TIME:
insert_sorted_by_time(list, tmp);
break;
};
}
free(fullpath);
closedir(dir);
return list;
}
int main(int argc, char **argv) {
struct list_head *list = NULL;
int path_index = 1;
/* TODO: Handle options */
if (argc < 2) {
usage(argv[0]);
return EXIT_FAILURE;
}
path_index = parse_arguments(argc, argv);
if (path_index == argc) {
usage(argv[0]);
return EXIT_FAILURE;
}
list = get_data_from_directory(argv[path_index]);
print_list(list);
destroy_list(list);
return EXIT_SUCCESS;
}
|