util.c (4979B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2022-2026 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 #include <assert.h> 5 #include <stdarg.h> 6 #include <stddef.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "limit.h" 12 #include "termbox.h" 13 #include "util.h" 14 15 /* Whitespace definition for determining word boundaries */ 16 #define B_WS " \v\t" 17 /* Full word boundary delimiter definition */ 18 #define B_FULL " \v\t()[]{},.;:/\\" 19 20 size_t 21 u8_string_to_unicode(uint32_t* us, const char* s, const ssize_t max) 22 { 23 uint32_t uch; 24 uint32_t* pus = us; 25 const char* ps = s; 26 int u8_len = 0; 27 size_t added = 0; 28 29 assert(s != NULL); 30 while (ps - s < max && *ps) 31 { 32 u8_len = tb_utf8_char_to_unicode(&uch, ps); 33 if (!u8_len) 34 { 35 uch = U32_REPL_CHAR; 36 u8_len = 1; 37 } 38 added++; 39 ps += u8_len; 40 *pus++ = uch; 41 } 42 *pus = 0; 43 return added; 44 } 45 46 size_t 47 unicode_string_to_u8(char* s, const uint32_t* us, const ssize_t max) 48 { 49 char ch[8]; 50 char* ps = NULL; 51 const uint32_t* pus = us; 52 size_t len; 53 size_t added = 0; 54 55 assert((s != NULL) && (us != NULL)); 56 ps = s; 57 while (pus - us < max && *pus) 58 { 59 len = tb_utf8_unicode_to_char(ch, *pus); 60 if (len < 0) 61 return added; 62 memccpy(ps, ch, 0, len); 63 ps += len; 64 added++; 65 pus++; 66 } 67 *ps = 0; 68 return added; 69 } 70 71 int 72 u32_has_nonblanks(const uint32_t* s) 73 { 74 const uint32_t* ps = s; 75 76 assert(s != NULL); 77 while (*ps) 78 { 79 if (!u32_is_word_boundary(*ps, 1)) 80 return 1; 81 ps++; 82 } 83 return 0; 84 } 85 86 uint32_t* 87 u32_memccpy(uint32_t* dst, const uint32_t* src, uint32_t c, size_t len) 88 { 89 size_t copied = 0; 90 assert((dst != NULL) && (src != NULL)); 91 while ((*dst++ = *src++) != c && copied < len) 92 copied++; 93 if (copied == len) 94 return NULL; 95 return dst; 96 } 97 98 /* len is the max length of src in bytes */ 99 uint32_t* 100 u32_u8_memccpy(uint32_t* dst, const char* src, uint32_t c, size_t len) 101 { 102 const char* psrc = src; 103 uint32_t uch; 104 size_t copied = 0; 105 int u8_len; 106 107 assert((dst != NULL) && (src != NULL)); 108 do 109 { 110 if (!*psrc) 111 { 112 *dst++ = 0; 113 break; 114 } 115 u8_len = tb_utf8_char_to_unicode(&uch, psrc); 116 if (u8_len < 0) 117 { 118 /* error, replace uch with "error char" */ 119 uch = 0xFFFD; 120 u8_len = -u8_len; 121 } 122 psrc += u8_len; 123 copied += u8_len; 124 if (copied >= len) 125 return NULL; 126 } while ((*dst++ = uch) != c && copied < len); 127 return dst; 128 } 129 130 size_t 131 u8_strlen(const char* s) 132 { 133 const char* ps = s; 134 uint32_t uc; 135 size_t len; 136 size_t result = 0; 137 assert(s != NULL); 138 while (*ps) 139 { 140 len = tb_utf8_char_to_unicode(&uc, ps); 141 result += len; 142 ps++; 143 } 144 return result; 145 } 146 147 size_t 148 u32_strlen(const uint32_t* s) 149 { 150 const uint32_t* ps = s; 151 size_t len = 0; 152 assert(s != NULL); 153 while (*ps++) 154 len++; 155 return len; 156 } 157 158 const uint32_t* 159 u32_strstr(const uint32_t* haystack, const uint32_t* needle) 160 { 161 const uint32_t* ph = haystack; 162 assert((haystack != NULL) && (needle != NULL)); 163 while (*ph) 164 { 165 if (u32_starts_with(ph, needle)) 166 return ph; 167 ph++; 168 } 169 return NULL; 170 } 171 172 const uint32_t* 173 u32_strrstr(const uint32_t* haystack, const uint32_t* needle, 174 const uint32_t* haystack_tail) 175 { 176 size_t needle_len = u32_strlen(needle); 177 const uint32_t* ph; 178 179 assert((haystack != NULL) && (haystack_tail != NULL) 180 && (needle != NULL)); 181 if (needle_len > (size_t)(haystack_tail - haystack)) 182 return NULL; 183 184 ph = haystack_tail - needle_len; 185 186 strrstr_loop: 187 if (u32_starts_with(ph, needle)) 188 return ph; 189 if (ph != haystack) 190 ph--; 191 else 192 goto strrstr_end; 193 goto strrstr_loop; 194 strrstr_end: 195 return NULL; 196 } 197 198 const uint32_t* 199 u32_strchr(const uint32_t* haystack, const uint32_t needle) 200 { 201 const uint32_t* ph = haystack; 202 assert(haystack != NULL); 203 while (*ph) 204 { 205 if (*ph == needle) 206 return ph; 207 ph++; 208 } 209 return NULL; 210 } 211 212 int 213 is_word_boundary(const char ch, const int strictly_whitespace) 214 { 215 return strictly_whitespace ? strchr(B_WS, ch) != NULL 216 : strchr(B_FULL, ch) != NULL; 217 } 218 219 int 220 u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace) 221 { 222 static uint32_t uws[sizeof(B_WS) + 1]; 223 static uint32_t ufull[sizeof(B_FULL) + 1]; 224 if (!*uws) 225 { 226 (void)u8_string_to_unicode(uws, B_WS, sizeof(B_WS)); 227 (void)u8_string_to_unicode(ufull, B_FULL, sizeof(B_FULL)); 228 } 229 return strictly_whitespace ? u32_strchr(uws, ch) != NULL 230 : u32_strchr(ufull, ch) != NULL; 231 } 232 233 int 234 starts_with(const char* s, const char* with) 235 { 236 assert((s != NULL) && (with != NULL)); 237 while (*s && *s == *with) 238 { 239 if (*s != *with) 240 return 0; 241 s++; 242 with++; 243 } 244 return *with == 0; 245 } 246 247 int 248 u32_starts_with(const uint32_t* s, const uint32_t* with) 249 { 250 assert((s != NULL) && (with != NULL)); 251 while (*s && *s == *with) 252 { 253 if (*s != *with) 254 return 0; 255 s++; 256 with++; 257 } 258 return *with == 0; 259 } 260 261 const char* 262 u8_basename(const char* path) 263 { 264 const char* ppath; 265 assert(path != NULL); 266 ppath = path + strlen(path); 267 while (ppath != path && *ppath != '/') 268 ppath--; 269 if (*ppath == '/') 270 ppath++; 271 return ppath; 272 }