poe

Уређивач .po фајлова
git clone https://git.sr.ht/~strahinja/poe
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

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 }*/