util.c (11059B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-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 "draw.h" 12 #include "termbox.h" 13 #include "po.h" 14 #include "util.h" 15 16 /* 32-bit string constants */ 17 const uint32_t bslash_en[] = {0x0000005c, 0x0000006e, 0}; 18 const uint32_t space_dot[] = {0x00000020, 0x0000002e, 0}; 19 20 extern int match_msgstr_ending; 21 22 /* max - max Unicode characters to convert from UTF-8 23 * Returns: number of processed Unicode characters */ 24 size_t 25 u8_string_to_unicode(uint32_t* us, const char* s, const size_t max) 26 { 27 uint32_t* pus = us; 28 uint32_t uch; 29 size_t added = 0; 30 int u8_len = 0; 31 32 assert((s != NULL) && (us != NULL)); 33 while (*s) 34 { 35 u8_len = tb_utf8_char_to_unicode(&uch, s); 36 if (added + 1 > max) 37 break; 38 if (u8_len > 0) 39 { 40 *pus++ = uch; 41 s += u8_len; 42 added++; 43 } 44 } 45 *pus++ = 0; 46 return added; 47 } 48 49 /* max - max Unicode characters to convert to UTF-8 50 * Returns: number of processed Unicode characters */ 51 size_t 52 unicode_string_to_u8(char* s, const uint32_t* us, const size_t max) 53 { 54 char ch[8]; 55 char* ps = NULL; 56 const uint32_t* pus = us; 57 size_t added = 0; 58 size_t len; 59 60 assert((s != NULL) && (us != NULL)); 61 ps = s; 62 while (*pus) 63 { 64 len = tb_utf8_unicode_to_char(ch, *pus); 65 if (added + 1 > max) 66 return added; 67 memccpy(ps, ch, 0, len); 68 ps += len; 69 added++; 70 pus++; 71 } 72 *ps = 0; 73 return added; 74 } 75 76 /* 77 * Returns: -1 on error 78 */ 79 ssize_t 80 u32_match_msgid_ending(struct DrawState* state, const uint32_t* msgid, 81 uint32_t* msgstr, size_t max) 82 { 83 uint32_t* buf = NULL; 84 ssize_t buf_len = 0; 85 ssize_t msgid_len = 0; 86 87 assert((state != NULL) && (msgstr != NULL)); 88 if (!msgid) 89 return 0; 90 91 buf = calloc(max + 1, sizeof(uint32_t)); 92 if (!buf) 93 goto u32_match_msgid_ending_alloc_error; 94 95 buf_len = u32_strncpy(buf, msgstr, (max + 1) * sizeof(uint32_t)); 96 msgid_len = u32_strlen(msgid); 97 98 if (msgid_len > 1 && u32_starts_with(msgid + msgid_len - 2, bslash_en)) 99 { 100 if (!(buf_len > 1 101 && u32_starts_with(buf + buf_len - 2, bslash_en))) 102 { 103 buf_len += 2; 104 buf[buf_len - 2] = BSLASH; 105 buf[buf_len - 1] = EN; 106 buf[buf_len] = 0; 107 } 108 } 109 else if (match_msgstr_ending && msgid_len > 0 110 && u32_strstr(space_dot, msgid + msgid_len - 1)) 111 { 112 if (!(buf_len > 0 && u32_strstr(space_dot, buf + buf_len - 1))) 113 { 114 buf_len++; 115 buf[buf_len - 1] = msgid[msgid_len - 1]; 116 buf[buf_len] = 0; 117 } 118 } 119 else if (match_msgstr_ending && buf_len > 1 120 && u32_starts_with(buf + buf_len - 2, bslash_en)) 121 { 122 buf_len -= 2; 123 buf[buf_len] = 0; 124 } 125 else if (match_msgstr_ending && buf_len > 0 126 && u32_strstr(space_dot, buf + buf_len - 1)) 127 { 128 buf_len--; 129 buf[buf_len] = 0; 130 } 131 132 u32_strncpy(msgstr, buf, max); 133 free(buf); 134 return buf_len; 135 136 u32_match_msgid_ending_alloc_error: 137 state->error = ERR_CANT_ALLOC; 138 return -1; 139 } 140 141 uint32_t* 142 u32_memccpy(uint32_t* dst, const uint32_t* src, uint32_t c, size_t len) 143 { 144 size_t copied = 0; 145 assert((dst != NULL) && (src != NULL)); 146 while ((*dst++ = *src++) != c && copied < len) 147 copied++; 148 if (copied == len) 149 return NULL; 150 return dst; 151 } 152 153 /* len is the max length of src in bytes */ 154 uint32_t* 155 u32_u8_memccpy(uint32_t* dst, const char* src, uint32_t c, size_t len) 156 { 157 const char* psrc = src; 158 uint32_t uch; 159 size_t copied = 0; 160 int u8_len; 161 162 assert((dst != NULL) && (src != NULL)); 163 do 164 { 165 if (!*psrc) 166 { 167 *dst++ = 0; 168 break; 169 } 170 u8_len = tb_utf8_char_to_unicode(&uch, psrc); 171 if (u8_len < 0) 172 { 173 /* error, replace uch with "error char" */ 174 uch = 0xFFFD; 175 u8_len = -u8_len; 176 } 177 psrc += u8_len; 178 copied += u8_len; 179 if (copied >= len) 180 return NULL; 181 } while ((*dst++ = uch) != c && copied < len); 182 return dst; 183 } 184 185 size_t 186 u32_count_chars(const uint32_t* s, const uint32_t ch) 187 { 188 const uint32_t* ps = s; 189 size_t result = 0; 190 assert(s != NULL); 191 while (*ps) 192 { 193 if (*ps == ch) 194 result++; 195 ps++; 196 } 197 return result; 198 } 199 200 size_t 201 u32_count_strings(const uint32_t* haystack, const uint32_t* needle) 202 { 203 const uint32_t* phaystack = haystack; 204 size_t needle_len = 0; 205 size_t result = 0; 206 207 assert((haystack != NULL) && (needle != NULL)); 208 needle_len = u32_strlen(needle); 209 if (!needle_len) 210 return 0; 211 212 while (*phaystack) 213 { 214 if (u32_starts_with(phaystack, needle)) 215 result++; 216 phaystack++; 217 } 218 return result; 219 } 220 221 /* 222 * Returns: -1 on error 223 */ 224 ssize_t 225 u32_encode_tabs(struct DrawState* state, uint32_t* s, size_t max) 226 { 227 uint32_t* buf = NULL; 228 uint32_t* pbuf = NULL; 229 uint32_t* ps = s; 230 size_t newlen = 0; 231 232 assert((state != NULL) && (s != NULL)); 233 buf = calloc(max + 1, sizeof(uint32_t)); 234 if (!buf) 235 goto u32_encode_tabs_alloc_error; 236 pbuf = buf; 237 while (*ps) 238 { 239 if (*ps == '\t') 240 { 241 *pbuf++ = '\\'; 242 *pbuf++ = 't'; 243 ps++; 244 newlen += 2; 245 } 246 else 247 { 248 *pbuf++ = *ps++; 249 newlen++; 250 } 251 } 252 *pbuf = 0; 253 u32_strncpy(s, buf, max); 254 free(buf); 255 return newlen; 256 257 u32_encode_tabs_alloc_error: 258 state->error = ERR_CANT_ALLOC; 259 return -1; 260 } 261 262 /* 263 * Returns: -1 on error 264 */ 265 ssize_t 266 u32_decode_tabs(struct DrawState* state, uint32_t* s, size_t max) 267 { 268 uint32_t* buf = NULL; 269 uint32_t* pbuf = NULL; 270 uint32_t* ps = s; 271 size_t newlen = 0; 272 273 assert((state != NULL) && (s != NULL)); 274 buf = calloc(max + 1, sizeof(uint32_t)); 275 if (!buf) 276 goto u32_decode_tabs_alloc_error; 277 pbuf = buf; 278 while (*ps && (size_t)(ps - s) < max) 279 { 280 if (*ps == '\\' && *(ps + 1) == 't') 281 { 282 *pbuf++ = '\t'; 283 ps += 2; 284 newlen++; 285 } 286 else 287 { 288 *pbuf++ = *ps++; 289 newlen++; 290 } 291 } 292 *pbuf = 0; 293 u32_strncpy(s, buf, max); 294 free(buf); 295 return newlen; 296 297 u32_decode_tabs_alloc_error: 298 state->error = ERR_CANT_ALLOC; 299 return -1; 300 } 301 302 size_t 303 u8_strlen(const char* s) 304 { 305 const char* ps = s; 306 uint32_t uc; 307 size_t len; 308 size_t result = 0; 309 assert(s != NULL); 310 while (*ps) 311 { 312 len = tb_utf8_char_to_unicode(&uc, ps); 313 result += len; 314 ps++; 315 } 316 return result; 317 } 318 319 size_t 320 u32_strlen(const uint32_t* s) 321 { 322 const uint32_t* ps = s; 323 size_t len = 0; 324 assert(s != NULL); 325 while (*ps++) 326 len++; 327 return len; 328 } 329 330 const uint32_t* 331 u32_strstr(const uint32_t* haystack, const uint32_t* needle) 332 { 333 const uint32_t* ph = haystack; 334 assert((haystack != NULL) && (needle != NULL)); 335 while (*ph) 336 { 337 if (u32_starts_with(ph, needle)) 338 return ph; 339 ph++; 340 } 341 return NULL; 342 } 343 344 const uint32_t* 345 u32_strchr(const uint32_t* haystack, const uint32_t needle) 346 { 347 const uint32_t* ph = haystack; 348 assert(haystack != NULL); 349 while (*ph) 350 { 351 if (*ph == needle) 352 return ph; 353 ph++; 354 } 355 return NULL; 356 } 357 358 size_t 359 u32_strncpy(uint32_t* to, const uint32_t* from, size_t max) 360 { 361 const uint32_t* pfrom = from; 362 size_t copied = 0; 363 assert((to != NULL) && (from != NULL)); 364 /* pfrom - from < max - 1 ... except we can't reliably subtract with 365 * size_t */ 366 while (*pfrom && pfrom + 1 < max + from) 367 { 368 *to++ = *pfrom++; 369 copied++; 370 } 371 *to = 0; 372 return copied; 373 } 374 375 /* 376 * to - u32 string to concatenate to 377 * from - u32 string to concatenate 378 * max - total combined string size */ 379 size_t 380 u32_strncat(uint32_t* to, const uint32_t* from, size_t max) 381 { 382 const uint32_t* pfrom = from; 383 size_t copied = 0; 384 assert((to != NULL) && (from != NULL)); 385 while (*to && copied + 1 < max) 386 { 387 to++; 388 copied++; 389 } 390 while (*pfrom && copied + 1 < max) 391 { 392 *to++ = *pfrom++; 393 copied++; 394 } 395 *to = 0; 396 return copied; 397 } 398 399 size_t 400 u32_u8_strncpy(uint32_t* to, const char* from, size_t max) 401 { 402 uint32_t* ufrom = calloc(max + 1, sizeof(uint32_t)); 403 size_t len; 404 assert(to != NULL); 405 if (!ufrom) 406 return 0; 407 u8_string_to_unicode(ufrom, from, max); 408 len = u32_strncpy(to, ufrom, max); 409 free(ufrom); 410 return len; 411 } 412 413 /* 414 * Returns: -1 on error 415 */ 416 ssize_t 417 u8_u32_strncpy(char* to, const uint32_t* from, size_t max) 418 { 419 char* cfrom = NULL; 420 size_t len; 421 assert(to != NULL); 422 cfrom = calloc(max + 1, 1); 423 if (!cfrom) 424 return -1; 425 unicode_string_to_u8(cfrom, from, max); 426 len = strlen(cfrom); 427 strncpy(to, cfrom, max); 428 if (len && len < max) 429 to[len] = 0; 430 free(cfrom); 431 return len; 432 } 433 434 /* wrap - if >0, also wrap lines 435 */ 436 size_t 437 u32_lines_in_string(const uint32_t* s, const int include_eol, const size_t wrap) 438 { 439 size_t result = 1; 440 const uint32_t* ps = NULL; 441 // int last_empty = 0; 442 443 if (!s) 444 return result; 445 446 ps = s; 447 while (*ps) 448 { 449 u32_next_line(s, &ps, include_eol, wrap); 450 if (*ps) 451 result++; 452 } 453 /*if (ps > s + 1 && *(ps - 2) == '\\' && *(ps - 1) == 'n') 454 last_empty = 1;*/ 455 return result; // + last_empty; 456 } 457 458 /* 459 * returns line length 460 * s - entire string (multiple lines) 461 * next_line - pointer to the next line; if NULL, only line length is returned 462 * wrap - wrap lines to this many characters (runes); if 0, don't wrap 463 */ 464 size_t 465 u32_next_line(const uint32_t* s, const uint32_t** next_line, 466 const int include_eol, const size_t wrap) 467 { 468 size_t result = 0; 469 const uint32_t* ps = NULL; 470 const uint32_t* start = NULL; 471 size_t last_nonblank = 0; 472 int have_last_nonblank = 0; 473 474 if (!s) 475 return result; 476 477 start = s; 478 if (next_line) 479 start = *next_line; 480 ps = start; 481 while (*ps) 482 { 483 if (wrap > 0 && result > wrap) 484 { 485 size_t cutoff = wrap; 486 487 if (have_last_nonblank) 488 cutoff = last_nonblank + 1; 489 if (next_line) 490 *next_line = start + cutoff; 491 492 return cutoff; 493 } 494 495 if (*ps == '\\' && *(ps + 1) == 'n') 496 { 497 if (next_line) 498 *next_line = ps + 2; 499 return result + (include_eol ? 2 : 0); 500 } 501 502 if (!u32_is_word_boundary(*ps, 1) 503 && u32_is_word_boundary(*(ps + 1), 1)) 504 { 505 last_nonblank = ps + 1 - start; 506 have_last_nonblank = 1; 507 } 508 509 ps++; 510 result++; 511 } 512 513 if (next_line) 514 *next_line = ps; 515 516 return result; 517 } 518 519 int 520 is_word_boundary(const char ch, const int strictly_whitespace) 521 { 522 return strictly_whitespace ? strchr(B_WS, ch) != NULL 523 : strchr(B_FULL, ch) != NULL; 524 } 525 526 int 527 u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace) 528 { 529 static uint32_t uws[sizeof(B_WS) + 1]; 530 static uint32_t ufull[sizeof(B_FULL) + 1]; 531 if (!*uws) 532 { 533 (void)u8_string_to_unicode(uws, B_WS, sizeof(B_WS)); 534 (void)u8_string_to_unicode(ufull, B_FULL, sizeof(B_FULL)); 535 } 536 return strictly_whitespace ? u32_strchr(uws, ch) != NULL 537 : u32_strchr(ufull, ch) != NULL; 538 } 539 540 int 541 starts_with(const char* s, const char* with) 542 { 543 assert((s != NULL) && (with != NULL)); 544 while (*s && *s == *with) 545 { 546 if (*s != *with) 547 return 0; 548 s++; 549 with++; 550 } 551 return *with == 0; 552 } 553 554 int 555 u32_starts_with(const uint32_t* s, const uint32_t* with) 556 { 557 assert((s != NULL) && (with != NULL)); 558 while (*s && *s == *with) 559 { 560 if (*s != *with) 561 return 0; 562 s++; 563 with++; 564 } 565 return *with == 0; 566 } 567 568 const char* 569 u8_basename(const char* path) 570 { 571 const char* ppath; 572 assert(path != NULL); 573 ppath = path + strlen(path); 574 while (ppath != path && *ppath != '/') 575 ppath--; 576 if (*ppath == '/') 577 ppath++; 578 return ppath; 579 } 580 581 /* 582 void 583 poe_log(const char* msg, ...) 584 { 585 va_list args; 586 va_start(args, msg); 587 FILE* log = fopen("/tmp/poe.log", "at"); 588 vfprintf(log, msg, args); 589 va_end(args); 590 fclose(log); 591 }*/