summaryrefslogtreecommitdiff
path: root/src/strutil.c
blob: cf0fa68b16b1b228f03175ad1f00a15c803c704e (plain)
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
#include "strutil.h"

#include "arena.h"        // struct arena, new
#include <assert.h>       // assert
#include <stdarg.h>       // va_*
#include <stdbool.h>      // bool, false
#include <stdio.h>        // vsnprintf
#include <string.h>       // strlen, strncmp
#include <errno.h>        // errno, E* macros

int aprintf(struct arena *a, char **out, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	int ret = vaprintf(a, out, fmt, ap);
	va_end(ap);
	return ret;
}

int vaprintf(struct arena *a, char **out, const char *fmt, va_list args) {
	// Calculate size.
	va_list tmp;
	va_copy(tmp, args);
	int size = vsnprintf(NULL, 0, fmt, args);
	va_end(tmp);

	// If e.g. the format string was broken, we cannot continue.
	if (size < 0) {
		return -1;
	}

	// Arena allocation cannot fail.
	*out = new(a, char, size + 1);

	int t = vsnprintf(*out, size + 1, fmt, args);
	assert(t == size);

	return size;
}

char *joinpath(struct arena *a, const char *path_a, const char *path_b) {
	char *out;
	int ret = aprintf(a, &out, "%s/%s", path_a, path_b);
	assert(ret > 0 && "should be infallible");
	return out;
}

bool endswith(const char *haystack, const char *needle) {
	assert(haystack != NULL);
	assert(needle != NULL);

	size_t haystack_len = strlen(haystack);
	size_t needle_len = strlen(needle);

	if (needle_len > haystack_len) {
		return false;
	}

	return strncmp(haystack + (haystack_len - needle_len), needle, needle_len) == 0;
}

char *replace_suffix(struct arena *a, const char *orig, const char *suffix, const char *with)
{
	size_t orig_len = strlen(orig);
	size_t suffix_len = strlen(suffix);
	size_t with_len = strlen(with);

	size_t new_len = orig_len - suffix_len + with_len;
	char *new = new(a, char, new_len + 1);

	memcpy(new, orig, orig_len - suffix_len);
	memcpy(new + orig_len - suffix_len, with, with_len);

	new[new_len] = '\0';

	return new;
}

// Based on <https://stackoverflow.com/a/779960>
char *replace(struct arena *a, const char *orig, const char *rep, const char *with) {
        assert(orig != NULL);
        assert(rep != NULL);

        char *tmp;      // varies

        size_t len_rep = strlen(rep);
        if (len_rep == 0) {
                errno = EINVAL; // empty rep causes infinite loop during count
                return NULL;
        }

        size_t len_with;
        if (with == NULL)
                with = "";
        len_with = strlen(with);

        // count the number of replacements needed
        const char *ins; // the next insert point
        int count;       // number of replacements
        ins = orig;
        for (count = 0; (tmp = strstr(ins, rep)) != NULL; ++count) {
                ins = tmp + len_rep;
        }

        char *result;
	tmp = result = new(a, char, strlen(orig) + (len_with - len_rep) * count + 1);

        // first time through the loop, all the variable are set correctly
        // from here on,
        // tmp points to the end of the result string
        // ins points to the next occurrence of rep in orig
        // orig points to the remainder of orig after "end of rep"
        while (count--) {
                ins = strstr(orig, rep);
                ssize_t len_front = ins - orig;
                tmp = strncpy(tmp, orig, len_front) + len_front;
                tmp = strcpy(tmp, with) + len_with;
                orig += len_front + len_rep; // move to next "end of rep"
        }
        strcpy(tmp, orig);
        return result;
}