poe

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

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