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
144
145
146
147
148
149
150
151
152
153
154
|
/**
* @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
* @param flags REPLACE_TRUNCATE_AFTER_MATCH
* @return 0 on success, -1 on error
*/
int replace_text(char *original, const char *target, const char *replacement, unsigned flags) {
char buffer[STASIS_BUFSIZ] = {0};
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
* @param flags REPLACE_TRUNCATE_AFTER_MATCH
* @return 0 on success, -1 on error
*/
int file_replace_text(const char* filename, const char* target, const char* replacement, unsigned flags) {
char buffer[STASIS_BUFSIZ];
char tempfilename[] = "tempfileXXXXXX";
FILE *fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "unable to open for reading: %s\n", filename);
return -1;
}
FILE *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
int result = 0;
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
if (strstr(buffer, target)) {
if (replace_text(buffer, target, replacement, flags)) {
result = -1;
}
}
fputs(buffer, tfp);
}
fflush(tfp);
// Replace original with modified copy
fclose(fp);
fp = fopen(filename, "w+");
if (!fp) {
SYSERROR("unable to reopen %s for writing", filename);
return -1;
}
// Update original file
rewind(tfp);
while (fgets(buffer, sizeof(buffer), tfp) != NULL) {
fputs(buffer, fp);
}
fclose(fp);
fclose(tfp);
remove(tempfilename);
return result;
}
|