/Users/alexjokela/projects/lattice/src/string_ops.c
Line | Count | Source |
1 | | #include "string_ops.h" |
2 | | |
3 | | #include <ctype.h> |
4 | | #include <stdlib.h> |
5 | | #include <string.h> |
6 | | |
7 | | /* Helper: treat NULL as "" */ |
8 | 366 | static const char *safe_str(const char *s) { |
9 | 366 | return s ? s : ""; |
10 | 366 | } |
11 | | |
12 | 5 | bool lat_str_contains(const char *s, const char *substr) { |
13 | 5 | s = safe_str(s); |
14 | 5 | substr = safe_str(substr); |
15 | 5 | return strstr(s, substr) != NULL; |
16 | 5 | } |
17 | | |
18 | 11 | char **lat_str_split(const char *s, const char *delim, size_t *out_count) { |
19 | 11 | s = safe_str(s); |
20 | 11 | delim = safe_str(delim); |
21 | | |
22 | 11 | size_t slen = strlen(s); |
23 | 11 | size_t dlen = strlen(delim); |
24 | | |
25 | | /* Empty delimiter: split into individual characters */ |
26 | 11 | if (dlen == 0) { |
27 | 0 | size_t count = slen > 0 ? slen : 1; |
28 | 0 | char **result = malloc(count * sizeof(char *)); |
29 | 0 | if (!result) { |
30 | 0 | *out_count = 0; |
31 | 0 | return NULL; |
32 | 0 | } |
33 | 0 | if (slen == 0) { |
34 | 0 | result[0] = strdup(""); |
35 | 0 | *out_count = 1; |
36 | 0 | return result; |
37 | 0 | } |
38 | 0 | for (size_t i = 0; i < slen; i++) { |
39 | 0 | result[i] = malloc(2); |
40 | 0 | if (result[i]) { |
41 | 0 | result[i][0] = s[i]; |
42 | 0 | result[i][1] = '\0'; |
43 | 0 | } |
44 | 0 | } |
45 | 0 | *out_count = slen; |
46 | 0 | return result; |
47 | 0 | } |
48 | | |
49 | | /* Count occurrences to pre-allocate */ |
50 | 11 | size_t count = 1; |
51 | 11 | const char *p = s; |
52 | 16 | while ((p = strstr(p, delim)) != NULL) { |
53 | 5 | count++; |
54 | 5 | p += dlen; |
55 | 5 | } |
56 | | |
57 | 11 | char **result = malloc(count * sizeof(char *)); |
58 | 11 | if (!result) { |
59 | 0 | *out_count = 0; |
60 | 0 | return NULL; |
61 | 0 | } |
62 | | |
63 | 11 | size_t idx = 0; |
64 | 11 | const char *start = s; |
65 | 11 | p = s; |
66 | 16 | while ((p = strstr(p, delim)) != NULL) { |
67 | 5 | size_t seg_len = (size_t)(p - start); |
68 | 5 | result[idx] = malloc(seg_len + 1); |
69 | 5 | if (result[idx]) { |
70 | 5 | memcpy(result[idx], start, seg_len); |
71 | 5 | result[idx][seg_len] = '\0'; |
72 | 5 | } |
73 | 5 | idx++; |
74 | 5 | p += dlen; |
75 | 5 | start = p; |
76 | 5 | } |
77 | | |
78 | | /* Final segment */ |
79 | 11 | result[idx] = strdup(start); |
80 | 11 | idx++; |
81 | | |
82 | 11 | *out_count = idx; |
83 | 11 | return result; |
84 | 11 | } |
85 | | |
86 | 43 | char *lat_str_trim(const char *s) { |
87 | 43 | s = safe_str(s); |
88 | 43 | size_t len = strlen(s); |
89 | | |
90 | | /* Find first non-whitespace */ |
91 | 43 | size_t start = 0; |
92 | 46 | while (start < len && isspace((unsigned char)s[start])) { |
93 | 3 | start++; |
94 | 3 | } |
95 | | |
96 | | /* Find last non-whitespace */ |
97 | 43 | size_t end = len; |
98 | 50 | while (end > start && isspace((unsigned char)s[end - 1])) { |
99 | 7 | end--; |
100 | 7 | } |
101 | | |
102 | 43 | size_t trimmed_len = end - start; |
103 | 43 | char *result = malloc(trimmed_len + 1); |
104 | 43 | if (result) { |
105 | 43 | memcpy(result, s + start, trimmed_len); |
106 | 43 | result[trimmed_len] = '\0'; |
107 | 43 | } |
108 | 43 | return result; |
109 | 43 | } |
110 | | |
111 | 45 | bool lat_str_starts_with(const char *s, const char *prefix) { |
112 | 45 | s = safe_str(s); |
113 | 45 | prefix = safe_str(prefix); |
114 | 45 | size_t plen = strlen(prefix); |
115 | 45 | if (plen > strlen(s)) { |
116 | 0 | return false; |
117 | 0 | } |
118 | 45 | return memcmp(s, prefix, plen) == 0; |
119 | 45 | } |
120 | | |
121 | 2 | bool lat_str_ends_with(const char *s, const char *suffix) { |
122 | 2 | s = safe_str(s); |
123 | 2 | suffix = safe_str(suffix); |
124 | 2 | size_t slen = strlen(s); |
125 | 2 | size_t suflen = strlen(suffix); |
126 | 2 | if (suflen > slen) { |
127 | 0 | return false; |
128 | 0 | } |
129 | 2 | return memcmp(s + slen - suflen, suffix, suflen) == 0; |
130 | 2 | } |
131 | | |
132 | 4 | char *lat_str_replace(const char *s, const char *old_str, const char *new_str) { |
133 | 4 | s = safe_str(s); |
134 | 4 | old_str = safe_str(old_str); |
135 | 4 | new_str = safe_str(new_str); |
136 | | |
137 | 4 | size_t slen = strlen(s); |
138 | 4 | size_t old_len = strlen(old_str); |
139 | 4 | size_t new_len = strlen(new_str); |
140 | | |
141 | | /* Empty old_str: return copy of s */ |
142 | 4 | if (old_len == 0) { |
143 | 0 | return strdup(s); |
144 | 0 | } |
145 | | |
146 | | /* Count occurrences */ |
147 | 4 | size_t count = 0; |
148 | 4 | const char *p = s; |
149 | 14 | while ((p = strstr(p, old_str)) != NULL) { |
150 | 10 | count++; |
151 | 10 | p += old_len; |
152 | 10 | } |
153 | | |
154 | 4 | if (count == 0) { |
155 | 0 | return strdup(s); |
156 | 0 | } |
157 | | |
158 | | /* Allocate result */ |
159 | 4 | size_t result_len = slen + count * (new_len - old_len); |
160 | 4 | char *result = malloc(result_len + 1); |
161 | 4 | if (!result) { |
162 | 0 | return NULL; |
163 | 0 | } |
164 | | |
165 | 4 | char *dst = result; |
166 | 4 | const char *src = s; |
167 | 14 | while ((p = strstr(src, old_str)) != NULL) { |
168 | 10 | size_t seg_len = (size_t)(p - src); |
169 | 10 | memcpy(dst, src, seg_len); |
170 | 10 | dst += seg_len; |
171 | 10 | memcpy(dst, new_str, new_len); |
172 | 10 | dst += new_len; |
173 | 10 | src = p + old_len; |
174 | 10 | } |
175 | | |
176 | | /* Copy remaining */ |
177 | 4 | strcpy(dst, src); |
178 | 4 | return result; |
179 | 4 | } |
180 | | |
181 | 3 | char *lat_str_to_upper(const char *s) { |
182 | 3 | s = safe_str(s); |
183 | 3 | size_t len = strlen(s); |
184 | 3 | char *result = malloc(len + 1); |
185 | 3 | if (result) { |
186 | 24 | for (size_t i = 0; i < len; i++) { |
187 | 21 | result[i] = (char)toupper((unsigned char)s[i]); |
188 | 21 | } |
189 | 3 | result[len] = '\0'; |
190 | 3 | } |
191 | 3 | return result; |
192 | 3 | } |
193 | | |
194 | 2 | char *lat_str_to_lower(const char *s) { |
195 | 2 | s = safe_str(s); |
196 | 2 | size_t len = strlen(s); |
197 | 2 | char *result = malloc(len + 1); |
198 | 2 | if (result) { |
199 | 18 | for (size_t i = 0; i < len; i++) { |
200 | 16 | result[i] = (char)tolower((unsigned char)s[i]); |
201 | 16 | } |
202 | 2 | result[len] = '\0'; |
203 | 2 | } |
204 | 2 | return result; |
205 | 2 | } |
206 | | |
207 | 64 | char *lat_str_substring(const char *s, int64_t start, int64_t end) { |
208 | 64 | s = safe_str(s); |
209 | 64 | int64_t len = (int64_t)strlen(s); |
210 | | |
211 | | /* Clamp to [0, len] */ |
212 | 64 | if (start < 0) start = 0; |
213 | 64 | if (end < 0) end = 0; |
214 | 64 | if (start > len) start = len; |
215 | 64 | if (end > len) end = len; |
216 | | |
217 | | /* start >= end: return empty string */ |
218 | 64 | if (start >= end) { |
219 | 0 | return strdup(""); |
220 | 0 | } |
221 | | |
222 | 64 | size_t sub_len = (size_t)(end - start); |
223 | 64 | char *result = malloc(sub_len + 1); |
224 | 64 | if (result) { |
225 | 64 | memcpy(result, s + start, sub_len); |
226 | 64 | result[sub_len] = '\0'; |
227 | 64 | } |
228 | 64 | return result; |
229 | 64 | } |
230 | | |
231 | 54 | int64_t lat_str_index_of(const char *s, const char *substr) { |
232 | 54 | s = safe_str(s); |
233 | 54 | substr = safe_str(substr); |
234 | 54 | const char *p = strstr(s, substr); |
235 | 54 | if (!p) { |
236 | 26 | return -1; |
237 | 26 | } |
238 | 28 | return (int64_t)(p - s); |
239 | 54 | } |
240 | | |
241 | 0 | int64_t lat_str_char_code_at(const char *s, size_t idx) { |
242 | 0 | s = safe_str(s); |
243 | 0 | if (idx >= strlen(s)) { |
244 | 0 | return -1; |
245 | 0 | } |
246 | 0 | return (int64_t)(unsigned char)s[idx]; |
247 | 0 | } |
248 | | |
249 | 0 | char *lat_str_from_char_code(int64_t code) { |
250 | 0 | if (code < 0 || code > 127) { |
251 | 0 | return strdup(""); |
252 | 0 | } |
253 | 0 | char *result = malloc(2); |
254 | 0 | if (result) { |
255 | 0 | result[0] = (char)code; |
256 | 0 | result[1] = '\0'; |
257 | 0 | } |
258 | 0 | return result; |
259 | 0 | } |
260 | | |
261 | 4 | char *lat_str_repeat(const char *s, size_t count) { |
262 | 4 | s = safe_str(s); |
263 | 4 | size_t len = strlen(s); |
264 | | |
265 | 4 | if (count == 0 || len == 0) { |
266 | 2 | return strdup(""); |
267 | 2 | } |
268 | | |
269 | 2 | size_t total = len * count; |
270 | 2 | char *result = malloc(total + 1); |
271 | 2 | if (result) { |
272 | 2 | char *dst = result; |
273 | 8 | for (size_t i = 0; i < count; i++) { |
274 | 6 | memcpy(dst, s, len); |
275 | 6 | dst += len; |
276 | 6 | } |
277 | 2 | *dst = '\0'; |
278 | 2 | } |
279 | 2 | return result; |
280 | 4 | } |
281 | | |
282 | 4 | char *lat_str_reverse(const char *s) { |
283 | 4 | s = safe_str(s); |
284 | 4 | size_t len = strlen(s); |
285 | 4 | char *result = malloc(len + 1); |
286 | 4 | if (result) { |
287 | 14 | for (size_t i = 0; i < len; i++) { |
288 | 10 | result[i] = s[len - 1 - i]; |
289 | 10 | } |
290 | 4 | result[len] = '\0'; |
291 | 4 | } |
292 | 4 | return result; |
293 | 4 | } |