aboutsummaryrefslogtreecommitdiff
path: root/lib/cleanpath.c
blob: c3a7e81c8a24021d1d81795eef25c5f70f699f80 (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
129
130
131
132
133
134
135
136
137
138
139
140
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "cleanpath.h"

#if !OS_WINDOWS
#include <regex.h>
#endif

/**
 * Split path into parts by sep
 * @param path a string (e.g. "/path1:/path2:/pathN")
 * @param sep a delimiter string (e.g. ":")
 * @note CleanPath->data string is modified. part are pointers to addresses of path (i.e. do not free())
 * @return a pointer to a CleanPath structure
 */
struct CleanPath *cleanpath_init(const char *path, const char *sep) {
    struct CleanPath *result;
    char *elem;
    size_t i;
    result = NULL;

    if (!path || !sep) {
        goto cleanpath_init__failure;
    }

    result = calloc(1, sizeof(*result));
    if (result == NULL) {
        goto cleanpath_init__failure;
    }

    result->data = strdup(path);
    if (result->data == NULL) {
        return NULL;
    }

    result->data_len = strlen(result->data) + 2; // + 2 to handle an empty PATH
    result->sep = strdup(sep);
    if (result->sep == NULL) {
        goto cleanpath_init__failure;
    }

    // Split string by separator
    elem = strtok(result->data, sep);
    for (i = 0; i < CLEANPATH_PART_MAX && elem != NULL; i++) {
        result->part[i] = elem;
        elem = strtok(NULL, sep);
    }
    result->part_nelem = i;

    cleanpath_init__failure:
    return result;
}

/**
 * Filter elements in CleanPath->part array by pattern
 * @param path a pointer to a CleanPath structure
 * @param mode an integer CLEANPATH_FILTER_EXACT, CLEANPATH_FILTER_LOOSE, or CLEANPATH_FILTER_REGEX
 * @param pattern a string
 * @see cleanpath_init()
 * @note CLEANPATH_FILTER_EXACT is the default mode
 */
void cleanpath_filter(struct CleanPath *path, unsigned mode, const char *pattern) {
#if !OS_WINDOWS
    regex_t regex;
#endif
    int match;

    for (size_t i = 0; i < CLEANPATH_PART_MAX && path->part[i]; i++) {
        match = 0;
        switch(mode) {
            case CLEANPATH_FILTER_LOOSE:
                match = strstr(path->part[i], pattern) != NULL ? 1 : 0;
                break;
            case CLEANPATH_FILTER_REGEX:
#if !OS_WINDOWS
                if (regcomp(&regex, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
                    return;
                }
                match = regexec(&regex, path->part[i], 0, NULL, 0) == 0 ? 1 : 0;
                regfree(&regex);
#else
                fprintf(stderr, "WARNING: --regex|-r is not implemented on Windows. Using default filter mode.\n");
#endif
                break;
            case CLEANPATH_FILTER_EXACT:
            default:
                match = strcmp(path->part[i], pattern) == 0 ? 1 : 0;
                break;
        }

        if (match)
            *path->part[i] = '\0';
    }
}

/**
 * Reconstruct CleanPath->data using CleanPath->part and return a copy of the string
 * @param path a pointer to a CleanPath structure
 * @see cleanpath_filter()
 * @return a string
 */
char *cleanpath_read(struct CleanPath *path) {
    char *result;
    size_t result_len;

    result = calloc(path->data_len, sizeof(char));
    if (result == NULL) {
        goto cleanpath_read__failure;
    }

    for (size_t i = 0; i < CLEANPATH_PART_MAX && path->part[i] != NULL; i++) {
        // Ignore filtered paths
        if (*path->part[i] == '\0') {
            continue;
        }
        strcat(result, path->part[i]);
        strcat(result, path->sep);
    }

    result_len = strlen(result);
    if (result_len) {
        result[result_len - 1] = '\0';
    }

    cleanpath_read__failure:
    return result;
}

/**
 * Free memory allocated by cleanpath_init()
 * @param path a pointer to a CleanPath structure
 */
void cleanpath_free(struct CleanPath *path) {
    free(path->data);
    free(path->sep);
    free(path);
}