util.c (8693B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-2024 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 5 #include <stdarg.h> 6 #include <stddef.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "termbox.h" 12 #include "po.h" 13 #include "util.h" 14 15 enum { UTLERRNONE = 0, UTLERROVFL = 1 }; 16 17 int utlerrno = UTLERRNONE; 18 19 /* max - max Unicode characters to convert from UTF-8 20 * Returns: number of processed Unicode characters */ 21 size_t 22 u8_string_to_unicode(uint32_t* us, const char* s, const size_t max) 23 { 24 uint32_t uch; 25 uint32_t* pus = us; 26 int u8_len = 0; 27 size_t added = 0; 28 29 while (s && *s && u8_len != TB_ERR) 30 { 31 u8_len = tb_utf8_char_to_unicode(&uch, s); 32 if (added + 1 > max) 33 break; 34 *pus++ = uch; 35 if (u8_len != TB_ERR) 36 { 37 s += u8_len; 38 added++; 39 } 40 } 41 *pus++ = 0; 42 return added; 43 } 44 45 /* max - max Unicode characters to convert to UTF-8 46 * Returns: number of processed Unicode characters */ 47 size_t 48 unicode_string_to_u8(char* s, const size_t s_size, const uint32_t* us, 49 const size_t max) 50 { 51 char ch[8]; 52 const uint32_t* pus = us; 53 size_t len; 54 size_t added = 0; 55 56 if (!pus) 57 return added; 58 59 *s = 0; 60 while (*pus) 61 { 62 size_t res; 63 64 len = tb_utf8_unicode_to_char(ch, *pus); 65 ch[len] = 0; 66 res = strlcat(s, ch, s_size); 67 if (res >= s_size) 68 { 69 utlerrno = UTLERROVFL; 70 return added; 71 } 72 added++; 73 pus++; 74 if (added + 1 > max) 75 break; 76 } 77 utlerrno = UTLERRNONE; 78 return added; 79 } 80 81 size_t 82 u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max) 83 { 84 uint32_t* buf = calloc(max + 1, sizeof(uint32_t)); 85 if (!buf) 86 return 0; 87 size_t buf_len = 0; 88 size_t msgid_len = 0; 89 90 buf_len = u32_strncpy(buf, msgstr, (max + 1) * sizeof(uint32_t)); 91 msgid_len = u32_strlen(msgid); 92 93 if (msgid_len > 1 94 && u32_starts_with(msgid + msgid_len - 2, (uint32_t*)L"\\n")) 95 { 96 if (!(buf_len > 1 97 && u32_starts_with(buf + buf_len - 2, 98 (uint32_t*)L"\\n"))) 99 { 100 buf_len += 2; 101 buf[buf_len - 2] = '\\'; 102 buf[buf_len - 1] = 'n'; 103 buf[buf_len] = 0; 104 } 105 } 106 #ifdef MATCH_MSGSTR_ENDING 107 else if (msgid_len > 0 108 && u32_strstr((uint32_t*)L" .", msgid + msgid_len - 1)) 109 { 110 if (!(buf_len > 0 111 && u32_strstr((uint32_t*)L" .", buf + buf_len - 1))) 112 { 113 buf_len++; 114 buf[buf_len - 1] = msgid[msgid_len - 1]; 115 buf[buf_len] = 0; 116 } 117 } 118 else if (buf_len > 1 119 && u32_starts_with(buf + buf_len - 2, (uint32_t*)L"\\n")) 120 { 121 buf_len -= 2; 122 buf[buf_len] = 0; 123 } 124 else if (buf_len > 0 && u32_strstr((uint32_t*)L" .", buf + buf_len - 1)) 125 { 126 buf_len--; 127 buf[buf_len] = 0; 128 } 129 #endif 130 131 u32_strncpy(msgstr, buf, max); 132 133 return buf_len; 134 } 135 136 size_t 137 u32_count_chars(const uint32_t* s, const uint32_t ch) 138 { 139 size_t result = 0; 140 const uint32_t* ps = s; 141 while (*ps) 142 { 143 if (*ps == ch) 144 result++; 145 ps++; 146 } 147 return result; 148 } 149 150 size_t 151 u32_count_strings(const uint32_t* haystack, const uint32_t* needle) 152 { 153 size_t result = 0; 154 const uint32_t* phaystack = haystack; 155 size_t needle_len = 0; 156 157 if (!haystack || !needle) 158 return 0; 159 160 needle_len = u32_strlen(needle); 161 if (!needle_len) 162 return 0; 163 164 while (*phaystack) 165 { 166 if (u32_starts_with(phaystack, needle)) 167 result++; 168 phaystack++; 169 } 170 return result; 171 } 172 173 size_t 174 u32_encode_tabs(uint32_t* s, size_t max) 175 { 176 uint32_t* buf = calloc(max + 1, sizeof(uint32_t)); 177 if (!buf) 178 return 0; 179 uint32_t* pbuf = buf; 180 uint32_t* ps = s; 181 size_t newlen = 0; 182 183 while (*ps) 184 { 185 if (*ps == '\t') 186 { 187 *pbuf++ = '\\'; 188 *pbuf++ = 't'; 189 ps++; 190 newlen += 2; 191 } 192 else 193 { 194 *pbuf++ = *ps++; 195 newlen++; 196 } 197 } 198 *pbuf = 0; 199 u32_strncpy(s, buf, max); 200 free(buf); 201 return newlen; 202 } 203 204 size_t 205 u32_decode_tabs(uint32_t* s, size_t max) 206 { 207 uint32_t* buf = calloc(max + 1, sizeof(uint32_t)); 208 if (!buf) 209 return 0; 210 uint32_t* pbuf = buf; 211 uint32_t* ps = s; 212 size_t newlen = 0; 213 214 while (*ps) 215 { 216 if (*ps == '\\' && *(ps + 1) == 't') 217 { 218 *pbuf++ = '\t'; 219 ps += 2; 220 newlen++; 221 } 222 else 223 { 224 *pbuf++ = *ps++; 225 newlen++; 226 } 227 } 228 *pbuf = 0; 229 u32_strncpy(s, buf, max); 230 free(buf); 231 return newlen; 232 } 233 234 size_t 235 u32_strlen(const uint32_t* s) 236 { 237 const uint32_t* ps = s; 238 size_t len = 0; 239 if (!s) 240 return 0; 241 while (*ps++) 242 len++; 243 return len; 244 } 245 246 const uint32_t* 247 u32_strstr(const uint32_t* haystack, const uint32_t* needle) 248 { 249 const uint32_t* ph = haystack; 250 while (*ph) 251 { 252 if (u32_starts_with(ph, needle)) 253 return ph; 254 ph++; 255 } 256 return NULL; 257 } 258 259 const uint32_t* 260 u32_strchr(const uint32_t* haystack, const uint32_t needle) 261 { 262 const uint32_t* ph = haystack; 263 while (*ph) 264 { 265 if (*ph == needle) 266 return ph; 267 ph++; 268 } 269 return NULL; 270 } 271 272 size_t 273 u32_strncpy(uint32_t* to, const uint32_t* from, size_t max) 274 { 275 const uint32_t* pfrom = from; 276 size_t copied = 0; 277 /* pfrom - from < max - 1 ... except we can't reliably subtract with 278 * size_t */ 279 while (*pfrom && pfrom + 1 < max + from) 280 { 281 *to++ = *pfrom++; 282 copied++; 283 } 284 *to = 0; 285 return copied; 286 } 287 288 /* 289 * to - u32 string to concatenate to 290 * from - u32 string to concatenate 291 * max - total combined string size */ 292 size_t 293 u32_strncat(uint32_t* to, const uint32_t* from, size_t max) 294 { 295 const uint32_t* pfrom = from; 296 size_t copied = 0; 297 while (*to && copied + 1 < max) 298 { 299 to++; 300 copied++; 301 } 302 while (*pfrom && copied + 1 < max) 303 { 304 *to++ = *pfrom++; 305 copied++; 306 } 307 *to = 0; 308 return copied; 309 } 310 311 size_t 312 u32_u8_strncpy(uint32_t* to, const char* from, size_t max) 313 { 314 uint32_t* ufrom = calloc(max + 1, sizeof(uint32_t)); 315 size_t len; 316 if (!ufrom) 317 return 0; 318 u8_string_to_unicode(ufrom, from, max); 319 len = u32_strncpy(to, ufrom, max); 320 free(ufrom); 321 return len; 322 } 323 324 size_t 325 u8_u32_strncpy(char* to, const uint32_t* from, size_t max) 326 { 327 char* cfrom = calloc(max + 1, 1); 328 size_t len; 329 if (!cfrom) 330 return 0; 331 unicode_string_to_u8(cfrom, max + 1, from, max); 332 len = strlen(cfrom); 333 strncpy(to, cfrom, max); 334 if (len && len < max) 335 to[len] = 0; 336 free(cfrom); 337 return len; 338 } 339 340 /* wrap - if >0, also wrap lines 341 * */ 342 size_t 343 u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap) 344 { 345 size_t result = 1; 346 const uint32_t* ps = NULL; 347 int last_empty = 0; 348 349 if (!s) 350 return result; 351 352 ps = s; 353 while (*ps) 354 { 355 u32_next_line(s, &ps, include_eol, wrap); 356 if (*ps) 357 result++; 358 } 359 if (ps > s + 1 && *(ps - 2) == '\\' && *(ps - 1) == 'n') 360 last_empty = 1; 361 return result + last_empty; 362 } 363 364 /* 365 * returns line length 366 * s - entire string (multiple lines) 367 * next_line - pointer to the next line; if NULL, only line length is returned 368 * wrap - wrap lines to this many characters (runes); if 0, don't wrap 369 */ 370 size_t 371 u32_next_line(const uint32_t* s, const uint32_t** next_line, 372 const int include_eol, const size_t wrap) 373 { 374 size_t result = 0; 375 const uint32_t* ps = NULL; 376 const uint32_t* start = NULL; 377 size_t last_nonblank = 0; 378 int have_last_nonblank = 0; 379 380 if (!s) 381 return result; 382 383 start = s; 384 if (next_line) 385 start = *next_line; 386 ps = start; 387 while (*ps) 388 { 389 if (wrap > 0 && result > wrap) 390 { 391 size_t cutoff = wrap; 392 393 if (have_last_nonblank) 394 cutoff = last_nonblank + 1; 395 if (next_line) 396 *next_line = start + cutoff; 397 398 return cutoff; 399 } 400 401 if (*ps == '\\' && *(ps + 1) == 'n') 402 { 403 if (next_line) 404 *next_line = ps + 2; 405 return result + (include_eol ? 2 : 0); 406 } 407 408 if (!u32_is_word_boundary(*ps, 1) 409 && u32_is_word_boundary(*(ps + 1), 1)) 410 { 411 last_nonblank = ps - start; 412 have_last_nonblank = 1; 413 } 414 415 ps++; 416 result++; 417 } 418 419 if (next_line) 420 *next_line = ps; 421 422 return result; 423 } 424 425 const int 426 is_word_boundary(const char ch, const int strictly_whitespace) 427 { 428 return strictly_whitespace ? strchr(" \v\t", ch) != NULL 429 : strchr(" \v\t()[]{},.;:/\\", ch) != NULL; 430 } 431 432 const int 433 u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace) 434 { 435 return strictly_whitespace 436 ? u32_strchr((uint32_t*)L" \v\t", ch) != NULL 437 : u32_strchr((uint32_t*)L" \v\t()[]{},.;:/\\", ch) != NULL; 438 } 439 440 const int 441 starts_with(const char* s, const char* with) 442 { 443 while (*s && *s == *with) 444 { 445 if (*s != *with) 446 return 0; 447 s++; 448 with++; 449 } 450 return *with == 0; 451 } 452 453 const int 454 u32_starts_with(const uint32_t* s, const uint32_t* with) 455 { 456 while (*s && *s == *with) 457 { 458 if (*s != *with) 459 return 0; 460 s++; 461 with++; 462 } 463 return *with == 0; 464 } 465 466 const char* 467 u8_basename(const char* path) 468 { 469 const char* ppath; 470 if (!path) 471 return NULL; 472 ppath = path + strlen(path); 473 while (ppath != path && *ppath != '/') 474 ppath--; 475 if (*ppath == '/') 476 ppath++; 477 return ppath; 478 } 479 480 /* 481 void 482 poe_log(const char* msg, ...) 483 { 484 va_list args; 485 va_start(args, msg); 486 FILE* log = fopen("/tmp/poe.log", "at"); 487 vfprintf(log, msg, args); 488 va_end(args); 489 fclose(log); 490 }*/