aboutsummaryrefslogtreecommitdiff
path: root/src/dir_monitor.c
blob: b2257cead9335a679036aa1128df680a7dd46ec5 (plain) (blame)
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;
}