aboutsummaryrefslogtreecommitdiff
path: root/src/relocation.c
blob: 586d1ba851e1b6bfc9a4ad66af50bb86f9a4a045 (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
141
142
143
/**
 * @file relocation.c
 */
#include "relocation.h"
#include "str.h"

/**
 * Replace all occurrences of `target` with `replacement` in `original`
 *
 * ~~~{.c}
 * char *str = calloc(100, sizeof(char));
 * strcpy(str, "This are a test.");
 * if (replace_text(str, "are", "is")) {
 *     fprintf(stderr, "string replacement failed\n");
 *     exit(1);
 * }
 * // str is: "This is a test."
 * free(str);
 * ~~~
 *
 * @param original string to modify
 * @param target string value to replace
 * @param replacement string value
 * @return 0 on success, -1 on error
 */
int replace_text(char *original, const char *target, const char *replacement, unsigned flags) {
    char buffer[OMC_BUFSIZ];
    char *pos = original;
    char *match = NULL;
    size_t original_len = strlen(original);
    size_t target_len = strlen(target);
    size_t rep_len = strlen(replacement);
    size_t buffer_len = 0;

    if (original_len > sizeof(buffer)) {
        errno = EINVAL;
        SYSERROR("The original string is larger than buffer: %zu > %zu\n", original_len, sizeof(buffer));
        return -1;
    }

    memset(buffer, 0, sizeof(buffer));
    if ((match = strstr(pos, target))) {
        while (*pos != '\0') {
            // append to buffer the bytes leading up to the match
            strncat(buffer, pos, match - pos);
            // position in the string jump ahead to the beginning of the match
            pos = match;

            // replacement is shorter than the target
            if (rep_len < target_len) {
                // shrink the string
                strcat(buffer, replacement);
                memmove(pos, pos + target_len, strlen(pos) - target_len);
                memset(pos + (strlen(pos) - target_len), 0, target_len);
            } else { // replacement is longer than the target
                // write the replacement value to the buffer
                strcat(buffer, replacement);
                // target consumed. jump to the end of the substring.
                pos += target_len;
            }
            if (flags & REPLACE_TRUNCATE_AFTER_MATCH) {
                if (strstr(pos, LINE_SEP)) {
                    strcat(buffer, LINE_SEP);
                }
                break;
            }
            // find more matches
            if (!(match = strstr(pos, target))) {
                // no more matches
                // append whatever remains to the buffer
                strcat(buffer, pos);
                // stop
                break;
            }
        }
    } else {
        return 0;
    }

    buffer_len = strlen(buffer);
    if (buffer_len < original_len) {
        // truncate whatever remains of the original buffer
        memset(original + buffer_len, 0, original_len - buffer_len);
    }
    // replace original with contents of buffer
    strcpy(original, buffer);
    return 0;
}

/**
 * Replace `target` with `replacement` in `filename`
 *
 * ~~~{.c}
 * if (file_replace_text("/path/to/file.txt", "are", "is")) {
 *     fprintf(stderr, "failed to replace strings in file\n");
 *     exit(1);
 * }
 * ~~~
 *
 * @param filename path to file
 * @param target string value to replace
 * @param replacement string
 * @return 0 on success, -1 on error
 */
int file_replace_text(const char* filename, const char* target, const char* replacement, unsigned flags) {
    int result;
    char buffer[OMC_BUFSIZ];
    char tempfilename[] = "tempfileXXXXXX";
    FILE *fp;
    FILE *tfp;

    fp = fopen(filename, "r");
    if (!fp) {
        fprintf(stderr, "unable to open for reading: %s\n", filename);
        return -1;
    }

    tfp = fopen(tempfilename, "w");
    if (!tfp) {
        SYSERROR("unable to open temporary fp for writing: %s", tempfilename);
        fclose(fp);
        return -1;
    }

    // Write modified strings to temporary file
    result = 0;
    while (fgets(buffer, sizeof(buffer), fp)) {
        if (strstr(buffer, target)) {
            if (replace_text(buffer, target, replacement, flags)) {
                result = -1;
            }
        }
        fputs(buffer, tfp);
    }

    fclose(fp);
    fclose(tfp);

    // Replace original with modified copy
    remove(filename);
    rename(tempfilename, filename);
    return result;
}