Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_str.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
28#ifndef NO_NSTR
29
30#include "nilorea/n_common.h"
31#include "nilorea/n_log.h"
32#include "nilorea/n_str.h"
33#include "nilorea/n_hash.h"
34
35#include <errno.h>
36#include <pthread.h>
37#include <math.h>
38#include <ctype.h>
39#include <stdio.h>
40#include <string.h>
41#include <limits.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <dirent.h>
45
46#ifdef __windows__
53const char* strcasestr(const char* s1, const char* s2) {
54 __n_assert(s1, return NULL);
55 __n_assert(s2, return NULL);
56
57 size_t n = strlen(s2);
58 while (*s1) {
59 if (!strnicmp(s1++, s2, n))
60 return (s1 - 1);
61 }
62 return NULL;
63} /* strcasestr */
64#endif
65
70void free_nstr_ptr(void* ptr) {
71 N_STR* strptr = (N_STR*)ptr;
72 if (ptr && strptr) {
73 FreeNoLog(strptr->data);
74 FreeNoLog(strptr);
75 }
76} /* free_nstr_ptr( ... ) */
77
83int _free_nstr(N_STR** ptr) {
84 __n_assert(ptr && (*ptr), return FALSE);
85
86 FreeNoLog((*ptr)->data);
87 FreeNoLog((*ptr));
88
89 return TRUE;
90} /* free_nstr( ... ) */
91
98 if ((*ptr)) {
99 FreeNoLog((*ptr)->data);
100 FreeNoLog((*ptr));
101 }
102
103 return TRUE;
104} /* free_nstr( ... ) */
105
110void free_nstr_ptr_nolog(void* ptr) {
111 N_STR* strptr = (N_STR*)ptr;
112 if (strptr) {
113 FreeNoLog(strptr->data);
114 FreeNoLog(strptr);
115 }
116} /* free_nstr_ptr_nolog( ... ) */
117
123char* trim_nocopy(char* s) {
124 __n_assert(s, return NULL);
125
126 if (strlen(s) == 0)
127 return s;
128
129 char* start = s;
130
131 /* skip spaces at start */
132 while (*start && isspace((unsigned char)*start))
133 start++;
134
135 char* end = s + strlen(s) - 1;
136 /* iterate over the rest remebering last non-whitespace */
137 while (*end && isspace((unsigned char)*end) && end > s) {
138 end--;
139 }
140 end++;
141 /* write the terminating zero after last non-whitespace */
142 *end = 0;
143
144 return start;
145} /* trim_nocopy */
146
152char* trim(const char* s) {
153 __n_assert(s, return NULL);
154
155 char* copy = strdup(s);
156 __n_assert(copy, return NULL);
157
158 const char* trimmed = trim_nocopy(copy);
159 char* result = strdup(trimmed);
160 free(copy);
161 return result;
162} /* trim */
163
171char* nfgets(char* buffer, NSTRBYTE size, FILE* stream) {
172 __n_assert(buffer, return NULL);
173 __n_assert(stream, return NULL);
174
175 if (size >= INT_MAX) {
176 n_log(LOG_ERR, "size of %zu too big, >INT_MAX for buffer %p", size, buffer);
177 return NULL;
178 }
179
180 if (!fgets(buffer, (int)size, stream)) {
181 return NULL;
182 }
183
184 return buffer;
185} /* nfgets(...) */
186
192int empty_nstr(N_STR* nstr) {
193 __n_assert(nstr, return FALSE);
194 __n_assert(nstr->data, return FALSE);
195
196 nstr->written = 0;
197 memset(nstr->data, 0, nstr->length);
198
199 return TRUE;
200}
201
210 N_STR* str = NULL;
211
212 if (size == (NSTRBYTE)-1) {
213 n_log(LOG_ERR, "new_nstr: size %zu would wrap the terminator allocation", (size_t)size);
214 return NULL;
215 }
216
217 Malloc(str, N_STR, 1);
218 __n_assert(str, return NULL);
219
220 str->written = 0;
221 if (size == 0) {
222 str->data = NULL;
223 str->length = 0;
224 } else {
225 Malloc(str->data, char, size + 1);
226 __n_assert(str->data, Free(str); return NULL);
227 str->length = size;
228 }
229 return str;
230} /* new_nstr(...) */
231
239int char_to_nstr_ex(const char* from, NSTRBYTE nboct, N_STR** to) {
240 if ((*to)) {
241 n_log(LOG_ERR, "destination N_STR **str is not NULL (%p), it contain (%s). You must provide an empty destination.", (*to), ((*to) && (*to)->data) ? (*to)->data : "DATA_IS_NULL");
242 n_log(LOG_ERR, "Data to copy: %s", _str(from));
243 return FALSE;
244 };
245
246 (*to) = new_nstr(nboct + 1);
247 __n_assert(to && (*to), return FALSE);
248 /* added a sizeof( void * ) to add a consistant and secure padding at the end */
249 __n_assert((*to)->data, Free((*to)); return FALSE);
250
251 memcpy((*to)->data, from, nboct);
252 (*to)->written = nboct;
253
254 return TRUE;
255} /* char_to_nstr(...) */
256
262N_STR* char_to_nstr(const char* src) {
263 __n_assert(src, return NULL);
264 N_STR* strptr = NULL;
265 size_t length = strlen(src);
266 char_to_nstr_ex(src, length, &strptr);
267 return strptr;
268} /* char_to_str(...) */
269
276 __n_assert(src, return NULL);
277
278 N_STR* to = NULL;
279 to = new_nstr(0);
280 __n_assert(to, return NULL);
281
282 to->data = src;
283 to->written = strlen(src);
284 to->length = to->written + 1; // include src end of string
285
286 return to;
287} /* char_to_str_nocopy(...) */
288
295N_STR* file_to_nstr(char* filename) {
296 N_STR* tmpstr = NULL;
297 struct stat filestat;
298 FILE* in = NULL;
299 int error = 0;
300
301 __n_assert(filename, n_log(LOG_ERR, "Unable to make a string from NULL filename"); return NULL);
302
303 int fd = open(filename, O_RDONLY);
304 error = errno;
305 if (fd == -1) {
306 n_log(LOG_ERR, "Unable to open %s for reading. Errno: %s", filename, strerror(error));
307 return NULL;
308 }
309
310 if (fstat(fd, &filestat) != 0) {
311 error = errno;
312#ifdef __linux__
313 if (error == EOVERFLOW) {
314 n_log(LOG_ERR, "%s size is too big ,EOVERFLOW)", filename);
315 } else {
316#endif
317 n_log(LOG_ERR, "Couldn't stat %s. Errno: %s", filename, strerror(error));
318#ifdef __linux__
319 }
320#endif
321 close(fd);
322 return NULL;
323 }
324
325 if ((uint64_t)filestat.st_size >= (uint64_t)UINT32_MAX) {
326 n_log(LOG_ERR, "file size >= 4GB is not possible yet. %s is %lld oct", filename, (long long)filestat.st_size);
327 close(fd);
328 return NULL;
329 }
330
331 n_log(LOG_DEBUG, "%s file size is: %lld", filename, (long long)filestat.st_size);
332
333 in = fdopen(fd, "rb");
334 if (!in) {
335 n_log(LOG_ERR, "fdopen failed on %s (fd=%d)", filename, fd);
336 close(fd);
337 return NULL;
338 }
339
340 tmpstr = new_nstr((size_t)filestat.st_size + 1);
341 __n_assert(tmpstr, fclose(in); n_log(LOG_ERR, "Unable to get a new nstr of %lld octets", (long long)filestat.st_size + 1); return NULL);
342
343 tmpstr->written = (size_t)filestat.st_size;
344
345 /* an empty file (0 bytes) is a valid read, not an error: fread would be asked to
346 read 0 bytes and return 0, which is not a failure. Return the empty (calloc'd,
347 NUL-terminated) string so callers treat an empty file as empty content instead
348 of a read error. Only read when there is at least one byte. */
349 if (tmpstr->written > 0) {
350 if (fread(tmpstr->data, sizeof(char), tmpstr->written, in) == 0) {
351 n_log(LOG_ERR, "Couldn't read %s, fread return 0", filename);
352 free_nstr(&tmpstr);
353 fclose(in);
354 return NULL;
355 }
356 if (ferror(in)) {
357 n_log(LOG_ERR, "There were some errors when reading %s", filename);
358 free_nstr(&tmpstr);
359 fclose(in);
360 return NULL;
361 }
362 }
363
364 fclose(in);
365
366 return tmpstr;
367} /*file_to_nstr */
368
376
377/* Write a whole N_STR into a file */
378int nstr_to_fd(N_STR* str, FILE* out, int lock) {
379#ifdef __linux__
380 struct flock out_lock;
381#endif
382 __n_assert(out, return FALSE);
383 __n_assert(str, return FALSE);
384
385 int ret = TRUE;
386
387 if (lock == 1) {
388#ifdef __linux__
389 memset(&out_lock, 0, sizeof(out_lock));
390 out_lock.l_type = F_WRLCK;
391 /* Place a write lock on the file. */
392 fcntl(fileno(out), F_SETLKW, &out_lock);
393#else
394 // cppcheck-suppress uselessAssignmentArg ; intentional: suppress unused parameter warning on non-POSIX
395 lock = 2;
396#endif
397 }
398
399 size_t written_to_file = 0;
400 if ((written_to_file = fwrite(str->data, sizeof(char), str->written, out)) != (size_t)str->written) {
401 n_log(LOG_ERR, "Couldn't write file, fwrite %zu of %zu octets", written_to_file, str->written);
402 ret = FALSE;
403 }
404 if (ferror(out)) {
405 n_log(LOG_ERR, "There were some errors when writing to %d", fileno(out));
406 ret = FALSE;
407 }
408
409 if (lock == 1) {
410#ifdef __linux__
411 memset(&out_lock, 0, sizeof(out_lock));
412 out_lock.l_type = F_WRLCK;
413 /* Place a write lock on the file. */
414 fcntl(fileno(out), F_SETLKW, &out_lock);
415#else
416 // cppcheck-suppress uselessAssignmentArg
417 // cppcheck-suppress unreadVariable
418 lock = 2; /* compiler warning suppressor for non-POSIX */
419#endif
420 }
421
422 return ret;
423} /* nstr_to_fd( ... ) */
424
431int nstr_to_file(N_STR* str, char* filename) {
432 __n_assert(str, return FALSE);
433 __n_assert(filename, return FALSE);
434
435 int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600); // Secure permissions: rw-------
436 if (fd == -1) {
437 n_log(LOG_ERR, "Couldn't open %s for writing (0600). Errno: %s", _str(filename), strerror(errno));
438 return FALSE;
439 }
440
441 FILE* out = NULL;
442 out = fdopen(fd, "wb");
443 if (!out) {
444 n_log(LOG_ERR, "fdopen failed for %s (fd=%d). Errno: %s", _str(filename), fd, strerror(errno));
445 close(fd);
446 return FALSE;
447 }
448
449 int ret = nstr_to_fd(str, out, 1);
450
451 fclose(out);
452
453 n_log(LOG_DEBUG, "%s file size is: %lld", _str(filename), (long long)str->written);
454
455 return ret;
456} /* nstr_to_file(...) */
457
469int str_to_int_ex(const char* s, NSTRBYTE start, NSTRBYTE end, int* i, const int base) {
470 char* tmpstr = NULL;
471 char* endstr = NULL;
472 long int l = 0;
473
474 __n_assert(s, return FALSE);
475
476 if (start > end) return FALSE;
477
478 Malloc(tmpstr, char, sizeof(int) + end - start + 8);
479 __n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
480
481 memcpy(tmpstr, s + start, end - start);
482
483 errno = 0;
484 l = strtol(tmpstr, &endstr, base);
485 if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
486 n_log(LOG_ERR, "OVERFLOW reached when converting %s to int", tmpstr);
487 Free(tmpstr);
488 return FALSE;
489 }
490 if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
491 n_log(LOG_ERR, "UNDERFLOW reached when converting %s to int", tmpstr);
492 Free(tmpstr);
493 return FALSE;
494 }
495 if (*endstr != '\0' && *endstr != '\n') {
496 n_log(LOG_ERR, "Impossible conversion for %s", tmpstr);
497 Free(tmpstr);
498 return FALSE;
499 }
500 Free(tmpstr);
501 *i = (int)l;
502 return TRUE;
503} /* str_to_int_ex( ... ) */
504
517int str_to_int_nolog(const char* s, NSTRBYTE start, NSTRBYTE end, int* i, const int base, N_STR** infos) {
518 char* tmpstr = NULL;
519 char* endstr = NULL;
520 long int l = 0;
521
522 __n_assert(s, return FALSE);
523
524 if (start > end) return FALSE;
525
526 Malloc(tmpstr, char, sizeof(int) + end - start + 8);
527 __n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
528
529 memcpy(tmpstr, s + start, end - start);
530
531 errno = 0;
532 l = strtol(tmpstr, &endstr, base);
533 if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
534 nstrprintf((*infos), "OVERFLOW reached when converting %s to int", tmpstr);
535 Free(tmpstr);
536 return FALSE;
537 }
538 if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
539 nstrprintf((*infos), "UNDERFLOW reached when converting %s to int", tmpstr);
540 Free(tmpstr);
541 return FALSE;
542 }
543 if (*endstr != '\0' && *endstr != '\n') {
544 nstrprintf((*infos), "Impossible conversion for %s", tmpstr);
545 Free(tmpstr);
546 return FALSE;
547 }
548 Free(tmpstr);
549 *i = (int)l;
550 return TRUE;
551} /* str_to_int_nolog( ... ) */
552
560int str_to_int(const char* s, int* i, const int base) {
561 int ret = FALSE;
562 if (s) {
563 ret = str_to_int_ex(s, 0, strlen(s), i, base);
564 }
565 return ret;
566} /* str_to_int(...) */
567
579int str_to_long_ex(const char* s, NSTRBYTE start, NSTRBYTE end, long int* i, const int base) {
580 char* tmpstr = NULL;
581 char* endstr = NULL;
582 long l = 0;
583
584 __n_assert(s, return FALSE);
585
586 if (start > end) return FALSE;
587
588 Malloc(tmpstr, char, sizeof(int) + end - start + 8);
589 __n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
590
591 memcpy(tmpstr, s + start, end - start);
592
593 errno = 0;
594 l = strtol(tmpstr, &endstr, base);
595 int error = errno;
596
597 /* test return to number and errno values */
598 if (tmpstr == endstr) {
599 n_log(LOG_ERR, " number : %ld invalid (no digits found, 0 returned)", l);
600 Free(tmpstr);
601 return FALSE;
602 } else if (error == ERANGE && l == LONG_MIN) {
603 n_log(LOG_ERR, " number : %ld invalid (underflow occurred)", l);
604 Free(tmpstr);
605 return FALSE;
606 } else if (error == ERANGE && l == LONG_MAX) {
607 n_log(LOG_ERR, " number : %ld invalid (overflow occurred)", l);
608 Free(tmpstr);
609 return FALSE;
610 } else if (error == EINVAL) /* not in all c99 implementations - gcc OK */
611 {
612 n_log(LOG_ERR, " number : %ld invalid (base contains unsupported value)", l);
613 Free(tmpstr);
614 return FALSE;
615 } else if (error != 0 && l == 0) {
616 n_log(LOG_ERR, " number : %ld invalid (unspecified error occurred)", l);
617 Free(tmpstr);
618 return FALSE;
619 } else if (error == 0 && !*endstr) {
620 n_log(LOG_DEBUG, " number : %ld valid (and represents all characters read)", l);
621 } else if (error == 0 && *endstr != 0) {
622 n_log(LOG_DEBUG, " number : %ld valid (but additional characters remain", l);
623 }
624 Free(tmpstr);
625 *i = l;
626 return TRUE;
627} /* str_to_long_ex( ... ) */
628
638int str_to_long_long_ex(const char* s, NSTRBYTE start, NSTRBYTE end, long long int* i, const int base) {
639 char* tmpstr = NULL;
640 char* endstr = NULL;
641 long long l = 0;
642
643 __n_assert(s, return FALSE);
644
645 if (start > end) return FALSE;
646
647 Malloc(tmpstr, char, sizeof(int) + end - start + 8);
648 __n_assert(tmpstr, n_log(LOG_ERR, "Unable to Malloc( tmpstr , char , sizeof( int ) + %zu - %zu )", end, start); return FALSE);
649
650 memcpy(tmpstr, s + start, end - start);
651
652 errno = 0;
653 l = strtoll(tmpstr, &endstr, base);
654 int error = errno;
655
656 /* test return to number and errno values */
657 if (tmpstr == endstr) {
658 n_log(LOG_ERR, "number: '%s' invalid (no digits found, 0 returned)", s);
659 Free(tmpstr);
660 return FALSE;
661 } else if (error == ERANGE && l == LLONG_MIN) {
662 n_log(LOG_ERR, "number: '%s' invalid (underflow occurred)", s);
663 Free(tmpstr);
664 return FALSE;
665 } else if (error == ERANGE && l == LLONG_MAX) {
666 n_log(LOG_ERR, "number: '%s' invalid (overflow occurred)", s);
667 Free(tmpstr);
668 return FALSE;
669 } else if (error == EINVAL) /* not in all c99 implementations - gcc OK */
670 {
671 n_log(LOG_ERR, "number: '%s' invalid (base contains unsupported value)", s);
672 Free(tmpstr);
673 return FALSE;
674 } else if (error != 0 && l == 0) {
675 n_log(LOG_ERR, "number: '%s' invalid (unspecified error occurred)", s);
676 Free(tmpstr);
677 return FALSE;
678 } else if (error == 0 && !*endstr) {
679 n_log(LOG_DEBUG, "number : '%llu' valid (and represents all characters read)", l);
680 } else if (error == 0 && *endstr != 0) {
681 n_log(LOG_DEBUG, " number : '%llu' valid (remaining characters: '%s')", l, s);
682 }
683 Free(tmpstr);
684 *i = l;
685 return TRUE;
686} /* str_to_long_long_ex( ... ) */
687
695int str_to_long(const char* s, long int* i, const int base) {
696 int ret = FALSE;
697 if (s) {
698 ret = str_to_long_ex(s, 0, strlen(s), i, base);
699 }
700 return ret;
701} /* str_to_long(...) */
702
710int str_to_long_long(const char* s, long long int* i, const int base) {
711 int ret = FALSE;
712 if (s) {
713 ret = str_to_long_long_ex(s, 0, strlen(s), i, base);
714 }
715 return ret;
716} /* str_to_long(...) */
717
724 N_STR* new_str = NULL;
725
726 __n_assert(str, return NULL);
727 __n_assert(str->data, return NULL);
728
729 new_str = new_nstr(str->length);
730 if (new_str) {
731 if (new_str->data) {
732 memcpy(new_str->data, str->data, str->written);
733 new_str->length = str->length;
734 new_str->written = str->written;
735 } else {
736 Free(new_str);
737 n_log(LOG_ERR, "Error duplicating N_STR %p -> data", str);
738 }
739 } else {
740 n_log(LOG_ERR, "Error duplicating N_STR %p", str);
741 }
742 return new_str;
743} /* nstrdup(...) */
744
754int skipw(const char* string, char toskip, NSTRBYTE* iterator, int inc) {
755 int error_flag = 0;
756
757 __n_assert(string, return FALSE);
758
759 NSTRBYTE slen = (NSTRBYTE)strlen(string);
760 if (toskip == ' ') {
761 while (*iterator <= slen && isspace((unsigned char)string[*iterator])) {
762 if (inc < 0 && *iterator == 0) {
763 error_flag = 1;
764 break;
765 } else {
766 if (inc > 0)
767 *iterator = *iterator + (NSTRBYTE)inc;
768 else
769 *iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
770 }
771 }
772 } else {
773 while (*iterator <= slen && string[*iterator] == toskip) {
774 if (inc < 0 && *iterator == 0) {
775 error_flag = 1;
776 break;
777 } else {
778 if (inc > 0)
779 *iterator = *iterator + (NSTRBYTE)inc;
780 else
781 *iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
782 }
783 }
784 }
785 if (error_flag == 1 || *iterator > slen) {
786 return FALSE;
787 }
788
789 return TRUE;
790} /*skipw(...)*/
791
801int skipu(const char* string, char toskip, NSTRBYTE* iterator, int inc) {
802 int error_flag = 0;
803
804 __n_assert(string, return FALSE);
805
806 NSTRBYTE slen = (NSTRBYTE)strlen(string);
807 if (toskip == ' ') {
808 while (*iterator <= slen && !isspace((unsigned char)string[*iterator])) {
809 if (inc < 0 && *iterator == 0) {
810 error_flag = 1;
811 break;
812 } else {
813 if (inc > 0)
814 *iterator = *iterator + (NSTRBYTE)inc;
815 else
816 *iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
817 }
818 }
819 } else {
820 while (*iterator <= slen && string[*iterator] != toskip) {
821 if (inc < 0 && *iterator == 0) {
822 error_flag = 1;
823 break;
824 } else {
825 if (inc > 0)
826 *iterator = *iterator + (NSTRBYTE)inc;
827 else
828 *iterator = (*iterator > (NSTRBYTE)(-inc)) ? (*iterator - (NSTRBYTE)(-inc)) : 0;
829 }
830 }
831 }
832
833 if (error_flag == 1 || *iterator > slen) {
834 return FALSE;
835 }
836
837 return TRUE;
838} /*Skipu(...)*/
839
847int strup(const char* string, char* dest) {
848 NSTRBYTE it = 0;
849
850 __n_assert(string, return FALSE);
851 __n_assert(dest, return FALSE);
852
853 for (it = 0; it < (NSTRBYTE)strlen(string); it++)
854 dest[it] = (char)toupper(string[it]);
855
856 dest[strlen(string)] = '\0';
857
858 return TRUE;
859} /*strup(...)*/
860
868int strlo(const char* string, char* dest) {
869 NSTRBYTE it = 0;
870
871 __n_assert(string, return FALSE);
872 __n_assert(dest, return FALSE);
873
874 for (it = 0; it < (NSTRBYTE)strlen(string); it++)
875 dest[it] = (char)tolower(string[it]);
876
877 dest[strlen(string)] = '\0';
878
879 return TRUE;
880} /*strlo(...)*/
881
891int strcpy_u(const char* from, char* to, NSTRBYTE to_size, char delim, NSTRBYTE* it) {
892 NSTRBYTE _it = 0;
893
894 __n_assert(from, return FALSE);
895 __n_assert(to, return FALSE);
896 __n_assert(it, return FALSE);
897
898 while (_it < to_size && from[(*it)] != '\0' && from[(*it)] != delim) {
899 to[_it] = from[(*it)];
900 (*it) = (*it) + 1;
901 _it = _it + 1;
902 }
903
904 if (_it == to_size) {
905 _it--;
906 to[_it] = '\0';
907 n_log(LOG_DEBUG, "strcpy_u: not enough space to write %zu octet to dest (%zu max) , %s: %d", _it, to_size, __FILE__, __LINE__);
908 return FALSE;
909 }
910
911 to[_it] = '\0';
912
913 if (from[(*it)] != delim) {
914 n_log(LOG_DEBUG, "strcpy_u: delim value not found, written %zu octet to dest (%zu max) , %s: %d", _it, to_size, __FILE__, __LINE__);
915 return FALSE;
916 }
917 return TRUE;
918} /* strcpy_u(...) */
919
927char** split(const char* str, const char* delim, int empty) {
928 char** tab = NULL; /* result array */
929 const char* ptr = NULL; /* tmp pointer: it only ever walks the CONST input */
930 char** tmp = NULL; /* temporary pointer for realloc */
931 size_t sizeStr = 0; /* string token size */
932 size_t sizeTab = 0; /* array size */
933 const char* largestring = NULL; /* pointer to start of string */
934
935 __n_assert(str, return NULL);
936 __n_assert(delim, return NULL);
937
938 size_t sizeDelim = strlen(delim);
939 largestring = str;
940
941 while ((ptr = strstr(largestring, delim)) != NULL) {
942 sizeStr = (size_t)(ptr - largestring);
943 if (empty == 1 || sizeStr != 0) {
944 sizeTab++;
945 tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
946 __n_assert(tmp, goto error);
947 tab = tmp;
948
949 Malloc(tab[sizeTab - 1], char, sizeof(char) * (sizeStr + 1));
950 __n_assert(tab[sizeTab - 1], goto error);
951
952 memcpy(tab[sizeTab - 1], largestring, sizeStr);
953 tab[sizeTab - 1][sizeStr] = '\0';
954 }
955 ptr = ptr + sizeDelim;
956 largestring = ptr;
957 }
958
959 /* adding last part if any */
960 if (strlen(largestring) != 0) {
961 sizeStr = strlen(largestring);
962 sizeTab++;
963
964 tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
965 __n_assert(tmp, goto error);
966 tab = tmp;
967
968 Malloc(tab[sizeTab - 1], char, sizeof(char) * (sizeStr + 1));
969 __n_assert(tab[sizeTab - 1], goto error);
970
971 memcpy(tab[sizeTab - 1], largestring, sizeStr);
972 tab[sizeTab - 1][sizeStr] = '\0';
973 } else {
974 if (empty == 1) {
975 sizeTab++;
976
977 tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
978 __n_assert(tmp, goto error);
979 tab = tmp;
980
981 Malloc(tab[sizeTab - 1], char, (int)(sizeof(char) * 1));
982 __n_assert(tab[sizeTab - 1], goto error);
983
984 tab[sizeTab - 1][0] = '\0';
985 }
986 }
987
988 /* on ajoute une case a null pour finir le tableau */
989 sizeTab++;
990 tmp = (char**)realloc(tab, sizeof(char*) * sizeTab);
991 __n_assert(tmp, goto error);
992 tab = tmp;
993 tab[sizeTab - 1] = NULL;
994
995 return tab;
996
997error:
998 free_split_result(&tab);
999 return NULL;
1000} /* split( ... ) */
1001
1007int split_count(char** split_result) {
1008 __n_assert(split_result, return -1);
1009 __n_assert(split_result[0], return -1);
1010
1011 int it = 0;
1012 while (split_result[it]) {
1013 it++;
1014 }
1015 return it;
1016} /* split_count(...) */
1017
1023int free_split_result(char*** tab) {
1024 if (!(*tab))
1025 return FALSE;
1026
1027 int it = 0;
1028 while ((*tab)[it]) {
1029 // cppcheck-suppress identicalInnerCondition ; Free() macro rechecks pointer internally
1030 Free((*tab)[it]);
1031 it++;
1032 }
1033 Free((*tab));
1034
1035 return TRUE;
1036} /* free_split_result(...)*/
1037
1044char* join(char** splitresult, const char* delim) {
1045 size_t delim_length = 0;
1046 if (delim)
1047 delim_length = strlen(delim);
1048 size_t total_length = 0;
1049 int it = 0;
1050 while (splitresult[it]) {
1051 // n_log( LOG_DEBUG , "split entry %d: %s" , it , splitresult[ it ] );
1052 size_t entry_length = strlen(splitresult[it]);
1053 if (entry_length >= SIZE_MAX - total_length) {
1054 n_log(LOG_ERR, "join: total_length overflow accumulating entry %d", it);
1055 return NULL;
1056 }
1057 total_length += entry_length;
1058 // if there is a delimitor and 'it' isn't the last entry
1059 if (delim && splitresult[it + 1]) {
1060 if (delim_length >= SIZE_MAX - total_length) {
1061 n_log(LOG_ERR, "join: total_length overflow accumulating delimiter at entry %d", it);
1062 return NULL;
1063 }
1064 total_length += delim_length;
1065 }
1066 it++;
1067 }
1068 char* result = NULL;
1069 Malloc(result, char, total_length + 1);
1070 __n_assert(result, return NULL);
1071 size_t position = 0;
1072 it = 0;
1073 while (splitresult[it]) {
1074 size_t copy_size = strlen(splitresult[it]);
1075 memcpy(&result[position], splitresult[it], copy_size);
1076 position += copy_size;
1077 // if there is a delimitor and 'it' isn't the last entry
1078 if (delim && splitresult[it + 1]) {
1079 memcpy(&result[position], delim, delim_length);
1080 position += delim_length;
1081 }
1082 it++;
1083 }
1084 result[position] = '\0';
1085 return result;
1086} /* join */
1087
1096N_STR* nstrcat_ex(N_STR** dest, void* src, NSTRBYTE size, int resize_flag) {
1097 char* ptr = NULL;
1098 __n_assert(src, return NULL);
1099
1100 /* guard against size + 1 overflow (also protects new_nstr's internal +1). Must come before any arithmetic using size */
1101 if (size >= SIZE_MAX - 1) {
1102 n_log(LOG_ERR, "size too large in nstrcat_ex: %zu", size);
1103 return NULL;
1104 }
1105
1106 if (resize_flag == 0) {
1107 if ((*dest)) {
1108 /* check for written + size + 1 overflow before evaluating the expression */
1109 if (size > SIZE_MAX - (*dest)->written - 1) {
1110 n_log(LOG_ERR, "integer overflow in nstrcat_ex size calculation");
1111 return NULL;
1112 }
1113 if (((*dest)->written + size + 1) > (*dest)->length) {
1114 n_log(LOG_ERR, "%p to %p: not enough space. Resize forbidden. %zu needed, %zu available", src, (*dest), (*dest)->written + size + 1, (*dest)->length);
1115 return NULL;
1116 }
1117 } else {
1118 n_log(LOG_ERR, "%p to %p: not enough space. Resize forbidden. %zu needed, destination is NULL", src, (*dest), size + 1);
1119 return NULL;
1120 }
1121 }
1122
1123 if (!(*dest)) {
1124 (*dest) = new_nstr(size + 1);
1125 if (!(*dest)) {
1126 n_log(LOG_ERR, "could not allocate new N_STR of size %zu", size + 1);
1127 return NULL;
1128 }
1129 }
1130
1131 /* check for integer overflow before computing new size */
1132 if (size > SIZE_MAX - (*dest)->written - 1) {
1133 n_log(LOG_ERR, "integer overflow in nstrcat_ex size calculation");
1134 return NULL;
1135 }
1136
1137 if ((*dest)->length < (*dest)->written + size + 1) {
1138 (*dest)->length = (*dest)->written + size + 1;
1139 if (Reallocz((*dest)->data, char, (*dest)->written, (*dest)->length) == FALSE) {
1140 free_nstr(dest);
1141 return NULL;
1142 }
1143 }
1144
1145 ptr = (*dest)->data + (*dest)->written;
1146 memcpy(ptr, src, size);
1147 (*dest)->written += size;
1148
1149 (*dest)->data[(*dest)->written] = '\0';
1150
1151 return (*dest);
1152} /* nstrcat_ex( ... ) */
1153
1161N_STR* nstrcat_bytes_ex(N_STR** dest, void* data, NSTRBYTE size) {
1162 __n_assert(dest, return NULL);
1163
1164 if (size == 0) {
1165 n_log(LOG_ERR, "Could not copy 0 or less (%zu) octet!", size);
1166 return NULL;
1167 }
1168
1169 return nstrcat_ex(dest, data, size, 1);
1170} /* nstrcat_bytes_ex( ... )*/
1171
1194int write_and_fit_ex(char** dest, NSTRBYTE* size, NSTRBYTE* written, const char* src, NSTRBYTE src_size, NSTRBYTE additional_padding) {
1195 if (src_size == 0) return TRUE;
1196 NSTRBYTE needed_size = (*written) + src_size + 1;
1197 NSTRBYTE alloc_size = needed_size + additional_padding;
1198 /* unsigned overflow guard: also documents alloc_size >= needed_size >= 2
1199 * to the static analyzer (src_size >= 1 here, so needed_size >= 2). */
1200 if (alloc_size < needed_size) return FALSE;
1201
1202 /* realloc if needed, also if destination is not allocated */
1203 if ((needed_size >= (*size)) || !(*dest)) {
1204 if (!(*dest)) {
1205 (*written) = 0;
1206 (*size) = 0;
1207 }
1208 if (Reallocz((*dest), char, (*size), alloc_size) == FALSE) {
1209 n_log(LOG_ERR, "reallocation error !!!!");
1210 return FALSE;
1211 }
1212 /* *size tracks total allocation (including padding) so the next
1213 * appends that fit in the over-allocation skip the realloc. */
1214 (*size) = alloc_size;
1215 }
1216 char* ptr = (*dest) + (*written);
1217 memcpy(ptr, src, src_size);
1218 (*written) += src_size;
1219 (*dest)[(*written)] = '\0';
1220
1221 return TRUE;
1222} /* write_and_fit_ex( ...) */
1223
1236int write_and_fit(char** dest, NSTRBYTE* size, NSTRBYTE* written, const char* src) {
1237 return write_and_fit_ex(dest, size, written, src, strlen(src), 8);
1238} /* write_and_fit( ...) */
1239
1247int scan_dir(const char* dir, LIST* result, const int recurse) {
1248 return scan_dir_ex(dir, "*", result, recurse, 0);
1249}
1250
1260int scan_dir_ex(const char* dir, const char* pattern, LIST* result, const int recurse, const int mode) {
1261 DIR* dp = NULL;
1262 struct dirent* entry = NULL;
1263 struct stat statbuf;
1264
1265 if (!result)
1266 return FALSE;
1267
1268 if ((dp = opendir(dir)) == NULL) {
1269 n_log(LOG_ERR, "cannot open directory: %s", dir);
1270 return FALSE;
1271 }
1272
1273 N_STR* newname = NULL;
1274 while ((entry = readdir(dp)) != NULL) {
1275 if (!nstrprintf(newname, "%s/%s", dir, entry->d_name)) {
1276 n_log(LOG_ERR, "could not allocate newname for %s/%s", dir, entry->d_name);
1277 free_nstr(&newname);
1278 continue;
1279 }
1280
1281 if (stat(newname->data, &statbuf) >= 0) {
1282 if (S_ISDIR(statbuf.st_mode) != 0) {
1283 if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) {
1284 free_nstr(&newname);
1285 continue;
1286 }
1287
1288 /* Recurse */
1289 if (recurse != FALSE) {
1290 if (scan_dir_ex(newname->data, pattern, result, recurse, mode) != TRUE) {
1291 n_log(LOG_ERR, "scan_dir_ex( %s , %s , %p , %d , %d ) returned FALSE !", newname->data, pattern, result, recurse, mode);
1292 }
1293 }
1294 free_nstr(&newname);
1295 continue;
1296 } else if (S_ISREG(statbuf.st_mode) != 0) {
1297 if (wildmatcase(newname->data, pattern) == TRUE) {
1298 if (mode == 0) {
1299 char* file = strdup(newname->data);
1300 if (file) {
1301 list_push(result, file, &free);
1302 } else {
1303 n_log(LOG_ERR, "Error adding %s/%s to list", dir, entry->d_name);
1304 }
1305 } else if (mode == 1) {
1306 list_push(result, newname, &free_nstr_ptr);
1307 newname = NULL;
1308 }
1309 }
1310 }
1311 }
1312 if (newname)
1313 free_nstr(&newname);
1314 }
1315 closedir(dp);
1316 return TRUE;
1317} /*scan_dir(...) */
1318
1325int wildmat(register const char* text, register const char* p) {
1326 register int last = 0;
1327 register int matched = 0;
1328 register int reverse = 0;
1329
1330 for (; *p; text++, p++) {
1331 if (*text == '\0' && *p != '*')
1332 return WILDMAT_ABORT;
1333 switch (*p) {
1334 case '\\':
1335 /* Literal match with following character. */
1336 p++;
1337 /* FALLTHROUGH */
1338 default:
1339 if (*text != *p)
1340 return FALSE;
1341 continue;
1342 case '?':
1343 /* Match anything. */
1344 continue;
1345 case '*':
1346 while (*++p == '*')
1347 /* Consecutive stars act just like one. */
1348 ;
1349 if (*p == '\0')
1350 /* Trailing star matches everything. */
1351 return TRUE;
1352 while (*text)
1353 if ((matched = wildmat(text++, p)) != FALSE)
1354 return matched;
1355 return WILDMAT_ABORT;
1356 case '[':
1357 reverse = p[1] == WILDMAT_NEGATE_CLASS ? TRUE : FALSE;
1358 if (reverse)
1359 /* Inverted character class. */
1360 p++;
1361 matched = FALSE;
1362 if (p[1] == ']' || p[1] == '-')
1363 if (*++p == *text)
1364 matched = TRUE;
1365 for (last = *p; *++p && *p != ']'; last = *p)
1366 /* This next line requires a good C compiler. */
1367 if (*p == '-' && p[1] != ']'
1369 : *text == *p)
1370 matched = TRUE;
1371 if (matched == reverse)
1372 return FALSE;
1373 continue;
1374 }
1375 }
1376#ifdef WILDMAT_MATCH_TAR_PATTERN
1377 if (*text == '/')
1378 return TRUE;
1379#endif /* MATCH_TAR_ATTERN */
1380 return *text == '\0';
1381} /* wildmatch(...) */
1382
1389int wildmatcase(register const char* text, register const char* p) {
1390 register int last;
1391 register int matched;
1392 register int reverse;
1393
1394 for (; *p; text++, p++) {
1395 if (*text == '\0' && *p != '*')
1396 return WILDMAT_ABORT;
1397 switch (*p) {
1398 case '\\':
1399 /* Literal match with following character. */
1400 p++;
1401 /* FALLTHROUGH */
1402 default:
1403 if (toupper(*text) != toupper(*p))
1404 return FALSE;
1405 continue;
1406 case '?':
1407 /* Match anything. */
1408 continue;
1409 case '*':
1410 while (*++p == '*')
1411 /* Consecutive stars act just like one. */
1412 ;
1413 if (*p == '\0')
1414 /* Trailing star matches everything. */
1415 return TRUE;
1416 while (*text)
1417 if ((matched = wildmatcase(text++, p)) != FALSE)
1418 return matched;
1419 return WILDMAT_ABORT;
1420 case '[':
1421 reverse = p[1] == WILDMAT_NEGATE_CLASS ? TRUE : FALSE;
1422 if (reverse)
1423 /* Inverted character class. */
1424 p++;
1425 matched = FALSE;
1426 if (p[1] == ']' || p[1] == '-')
1427 if (toupper(*++p) == toupper(*text))
1428 matched = TRUE;
1429 for (last = toupper(*p); *++p && *p != ']'; last = toupper(*p))
1430 if (*p == '-' && p[1] != ']'
1431 ? toupper(*text) <= toupper(*++p) && toupper(*text) >= last
1432 : toupper(*text) == toupper(*p))
1433 matched = TRUE;
1434 if (matched == reverse)
1435 return FALSE;
1436 continue;
1437 }
1438 }
1439#ifdef WILDMAT_MATCH_TAR_PATTERN
1440 if (*text == '/')
1441 return TRUE;
1442#endif /* MATCH_TAR_ATTERN */
1443 return *text == '\0';
1444} /* wildmatcase(...) */
1445
1460char* str_replace(const char* string, const char* substr, const char* replacement) {
1461 size_t string_len = 0, substr_len = 0, replacement_len = 0, count = 0, result_len = 0;
1462 const char* scan = NULL;
1463 char* result = NULL;
1464 char* out = NULL;
1465
1466 if (!string) return NULL;
1467 if (!substr || !substr[0] || !replacement) return strdup(string);
1468 string_len = strlen(string);
1469 substr_len = strlen(substr);
1470 replacement_len = strlen(replacement);
1471 for (scan = strstr(string, substr); scan; scan = strstr(scan + substr_len, substr)) count++;
1472 /* string_len - count * substr_len + count * replacement_len + 1, each
1473 * step checked: count * substr_len cannot exceed string_len. */
1474 result_len = string_len - count * substr_len;
1475 if (replacement_len && count > (SIZE_MAX - 1 - result_len) / replacement_len) {
1476 n_log(LOG_ERR, "str_replace: the result would be too large (%zu occurrences of %zu bytes)", count,
1477 replacement_len);
1478 return NULL;
1479 }
1480 result_len += count * replacement_len;
1481 Malloc(result, char, result_len + 1);
1482 if (!result) {
1483 n_log(LOG_ERR, "str_replace: could not allocate %zu bytes", result_len + 1);
1484 return NULL;
1485 }
1486 out = result;
1487 for (scan = string; count > 0; count--) {
1488 const char* found = strstr(scan, substr);
1489 size_t before = (size_t)(found - scan);
1490
1491 memcpy(out, scan, before);
1492 out += before;
1493 memcpy(out, replacement, replacement_len);
1494 out += replacement_len;
1495 scan = found + substr_len;
1496 }
1497 memcpy(out, scan, strlen(scan) + 1);
1498 return result;
1499}
1500
1524int str_natncasecmp(const char* a, size_t alen, const char* b, size_t blen) {
1525 const unsigned char* pa = (const unsigned char*)a;
1526 const unsigned char* pb = (const unsigned char*)b;
1527 const unsigned char* const enda = pa + alen;
1528 const unsigned char* const endb = pb + blen;
1529 int zeros = 0, c = 0;
1530 size_t common = 0;
1531
1532 if (!a || !b) return (a == b) ? 0 : (!a ? -1 : 1);
1533 while (pa < enda && pb < endb) {
1534 if (*pa >= '0' && *pa <= '9' && *pb >= '0' && *pb <= '9') {
1535 const unsigned char* sa = pa;
1536 const unsigned char* sb = pb;
1537 const unsigned char* ea = NULL;
1538 const unsigned char* eb = NULL;
1539
1540 while (sa < enda && *sa == '0') sa++;
1541 while (sb < endb && *sb == '0') sb++;
1542 for (ea = sa; ea < enda && *ea >= '0' && *ea <= '9'; ea++);
1543 for (eb = sb; eb < endb && *eb >= '0' && *eb <= '9'; eb++);
1544 /* Without leading zeros, the longer run is the larger number. */
1545 if (ea - sa != eb - sb) return (ea - sa) < (eb - sb) ? -1 : 1;
1546 c = memcmp(sa, sb, (size_t)(ea - sa));
1547 if (c) return c < 0 ? -1 : 1;
1548 /* Same value: remember the first difference in padding, used
1549 * only if nothing after it tells the strings apart. */
1550 if (!zeros && (sa - pa) != (sb - pb)) zeros = (sa - pa) < (sb - pb) ? -1 : 1;
1551 pa = ea;
1552 pb = eb;
1553 } else {
1554 unsigned char ca = *pa, cb = *pb;
1555
1556 if (ca >= 'A' && ca <= 'Z') ca = (unsigned char)(ca - 'A' + 'a');
1557 if (cb >= 'A' && cb <= 'Z') cb = (unsigned char)(cb - 'A' + 'a');
1558 if (ca != cb) return ca < cb ? -1 : 1;
1559 pa++;
1560 pb++;
1561 }
1562 }
1563 if (pa < enda) return 1;
1564 if (pb < endb) return -1;
1565 if (zeros) return zeros;
1566 common = alen < blen ? alen : blen;
1567 c = common ? memcmp(a, b, common) : 0;
1568 if (c) return c < 0 ? -1 : 1;
1569 return alen == blen ? 0 : (alen < blen ? -1 : 1);
1570}
1571
1582int str_natcasecmp(const char* a, const char* b) {
1583 return str_natncasecmp(a, a ? strlen(a) : 0, b, b ? strlen(b) : 0);
1584}
1585
1595int str_sanitize_ex(char* string, const NSTRBYTE string_len, const char* mask, const NSTRBYTE masklen, const char replacement) {
1596 __n_assert(string, return FALSE);
1597 __n_assert(mask, return FALSE);
1598
1599 NSTRBYTE it = 0;
1600 for (it = 0; it < string_len; it++) {
1601 NSTRBYTE mask_it = 0;
1602 while (mask_it < masklen && mask[mask_it] != '\0') {
1603 if (string[it] == mask[mask_it])
1604 string[it] = replacement;
1605 mask_it++;
1606 }
1607 }
1608 return TRUE;
1609}
1610
1618int str_sanitize(char* string, const char* mask, const char replacement) {
1619 return str_sanitize_ex(string, strlen(string), mask, strlen(mask), replacement);
1620}
1621
1628int resize_nstr(N_STR* nstr, size_t size) {
1629 __n_assert(nstr, return FALSE);
1630 if (size == 0) {
1631 // Free the current buffer and reset fields
1632 Free(nstr->data);
1633 nstr->written = 0;
1634 nstr->length = 0;
1635 return TRUE;
1636 }
1637
1638 if (!nstr->data) {
1639 Malloc(nstr->data, char, size);
1640 __n_assert(nstr->data, return FALSE);
1641 } else {
1642 if (Reallocz(nstr->data, char, nstr->length, size) == FALSE) {
1643 return FALSE;
1644 }
1645 }
1646
1647 nstr->length = size;
1648
1649 return TRUE;
1650}
1651
1659N_STR* nstrprintf_ex(N_STR** nstr_var, const char* format, ...) {
1660 __n_assert(format, return NULL);
1661
1662 va_list args;
1663 va_list args_copy;
1664 int needed_size = 0;
1665
1666 // Calculate the required size for the formatted string
1667 va_start(args, format);
1668 va_copy(args_copy, args); // Copy args for reuse
1669 needed_size = vsnprintf(NULL, 0, format, args);
1670 va_end(args);
1671
1672 if (needed_size < 0) {
1673 va_end(args_copy);
1674 n_log(LOG_ERR, "there was an error while computing the new size according to format \"%s\" for N_STR %p", format, (*nstr_var));
1675 return NULL;
1676 }
1677
1678 size_t needed = (size_t)(needed_size + 1); // Include null-terminator
1679
1680 // Allocate or resize the N_STR object as needed
1681 if (!(*nstr_var)) {
1682 (*nstr_var) = new_nstr(needed);
1683 if (!(*nstr_var) || !(*nstr_var)->data) {
1684 va_end(args_copy);
1685 n_log(LOG_ERR, "could not allocate N_STR of size %zu", needed);
1686 return NULL;
1687 }
1688 } else if (needed > (*nstr_var)->length) {
1689 if (resize_nstr((*nstr_var), needed) == FALSE) {
1690 va_end(args_copy);
1691 n_log(LOG_ERR, "could not resize N_STR %p to size %zu", (*nstr_var), needed);
1692 return NULL;
1693 }
1694 }
1695
1696 // Write the formatted string into the buffer
1697 vsnprintf((*nstr_var)->data, needed, format, args_copy);
1698 va_end(args_copy);
1699
1700 (*nstr_var)->written = (size_t)needed_size;
1701 return (*nstr_var);
1702}
1703
1711N_STR* nstrprintf_cat_ex(N_STR** nstr_var, const char* format, ...) {
1712 __n_assert(format, return NULL);
1713
1714 va_list args;
1715 va_start(args, format);
1716
1717 // duplicate va_list
1718 va_list args_copy;
1719 va_copy(args_copy, args);
1720
1721 // compute needed size
1722 int needed_size = vsnprintf(NULL, 0, format, args_copy);
1723 va_end(args_copy);
1724
1725 if (needed_size < 0) {
1726 n_log(LOG_ERR, "vsnprintf size estimation failed for format \"%s\"", format);
1727 va_end(args);
1728 return NULL;
1729 }
1730
1731 size_t needed = (size_t)(needed_size + 1); // +1 for '\0'
1732
1733 // initialize if needed
1734 if (!(*nstr_var)) {
1735 (*nstr_var) = new_nstr(needed);
1736 if (!(*nstr_var)) {
1737 va_end(args);
1738 return NULL;
1739 }
1740 }
1741
1742 // check for overflow before computing total_needed
1743 if (needed > SIZE_MAX - (*nstr_var)->written) {
1744 va_end(args);
1745 n_log(LOG_ERR, "integer overflow computing total_needed in nstrprintf_cat_ex");
1746 return NULL;
1747 }
1748
1749 // resize if needed
1750 size_t total_needed = (*nstr_var)->written + needed;
1751 if (total_needed > (*nstr_var)->length) {
1752 if (resize_nstr((*nstr_var), total_needed) == FALSE) {
1753 va_end(args);
1754 n_log(LOG_ERR, "could not resize N_STR %p to size %zu", (*nstr_var), total_needed);
1755 return NULL;
1756 }
1757 }
1758
1759 // append formatted string
1760 char* write_ptr = (*nstr_var)->data + (*nstr_var)->written;
1761 int written_now = vsnprintf(write_ptr, needed, format, args);
1762 va_end(args);
1763
1764 if (written_now < 0) {
1765 n_log(LOG_ERR, "vsnprintf failed while writing at offset %zu in N_STR %p", (*nstr_var)->written, (*nstr_var));
1766 return NULL;
1767 }
1768
1769 (*nstr_var)->written += (size_t)written_now;
1770 (*nstr_var)->data[(*nstr_var)->written] = '\0'; // ensure final \0
1771
1772 return *nstr_var;
1773}
1774
1775N_STR* n_str_template_expand(const char* tmpl, HASH_TABLE* vars) {
1776 if (!tmpl) return NULL;
1777 N_STR* result = new_nstr(strlen(tmpl) + 256);
1778 if (!result) return NULL;
1779 const char* p = tmpl;
1780 while (*p) {
1781 if (p[0] == '{' && p[1] == '{') {
1782 const char* end = strstr(p + 2, "}}");
1783 if (end) {
1784 size_t klen = (size_t)(end - (p + 2));
1785 char key[256];
1786 if (klen < sizeof(key)) {
1787 memcpy(key, p + 2, klen);
1788 key[klen] = '\0';
1789 char* val = NULL;
1790 if (vars && ht_get_string(vars, key, &val) == TRUE && val) {
1791 nstrprintf_cat(result, "%s", val);
1792 } else {
1793 nstrprintf_cat(result, "{{%s}}", key);
1794 }
1795 p = end + 2;
1796 continue;
1797 }
1798 }
1799 }
1800 char ch[2] = {*p, '\0'};
1801 nstrprintf_cat(result, "%s", ch);
1802 p++;
1803 }
1804 return result;
1805}
1806
1819N_STR* n_str_url_encode(const char* src) {
1820 if (!src) return NULL;
1821 size_t in_len = strlen(src);
1822 /* worst case: every byte expands to "%HH" -> 3x */
1823 if (in_len > (SIZE_MAX - 1) / 3) return NULL;
1824 N_STR* out = new_nstr(in_len * 3 + 1);
1825 if (!out) return NULL;
1826 for (const unsigned char* p = (const unsigned char*)src; *p; p++) {
1827 unsigned char c = *p;
1828 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
1829 (c >= '0' && c <= '9') ||
1830 c == '-' || c == '_' || c == '.' || c == '~') {
1831 char ch[2] = {(char)c, '\0'};
1832 if (!nstrprintf_cat(out, "%s", ch)) {
1833 free_nstr(&out);
1834 return NULL;
1835 }
1836 } else {
1837 if (!nstrprintf_cat(out, "%%%02X", c)) {
1838 free_nstr(&out);
1839 return NULL;
1840 }
1841 }
1842 }
1843 return out;
1844}
1845
1846#endif /* #ifndef NOSTR */
1847
1855int n_str_utf8_valid(const char* s, size_t len) {
1856 const unsigned char* p = (const unsigned char*)s;
1857 size_t i = 0;
1858 if (!s)
1859 return len == 0 ? TRUE : FALSE;
1860 while (i < len) {
1861 unsigned char c = p[i];
1862 size_t need;
1863 unsigned long cp;
1864 if (c < 0x80) {
1865 i++;
1866 continue;
1867 }
1868 if (c >= 0xC2 && c <= 0xDF) {
1869 need = 1;
1870 cp = c & 0x1Fu;
1871 } else if (c >= 0xE0 && c <= 0xEF) {
1872 need = 2;
1873 cp = c & 0x0Fu;
1874 } else if (c >= 0xF0 && c <= 0xF4) {
1875 need = 3;
1876 cp = c & 0x07u;
1877 } else {
1878 return FALSE; /* stray continuation byte, C0/C1 overlong lead, or above F4 */
1879 }
1880 if (len - i - 1 < need)
1881 return FALSE;
1882 for (size_t k = 1; k <= need; k++) {
1883 if ((p[i + k] & 0xC0u) != 0x80u)
1884 return FALSE;
1885 cp = (cp << 6) | (p[i + k] & 0x3Fu);
1886 }
1887 if ((need == 2 && cp < 0x800) || (need == 3 && cp < 0x10000) ||
1888 (cp >= 0xD800 && cp <= 0xDFFF) || cp > 0x10FFFF)
1889 return FALSE;
1890 i += need + 1;
1891 }
1892 return TRUE;
1893}
static N_GUI_MOUSE_EVENT last
the last event the widget was told of, and how many
static int mode
static N_HTTP_RESP * text(int status, const char *body)
text response
static N_HTTP_SERVER * start(N_HTTP_SERVER_CONFIG *cfg, char *port, int base)
start a server on the first free port from base
volatile char key[227]
TRUE while the key is held.
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define _str(__PTR)
define true
Definition n_common.h:193
#define Reallocz(__ptr, __struct, __old_size, __size)
Realloc + zero new memory zone Handler to get errors.
Definition n_common.h:247
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
int ht_get_string(HASH_TABLE *table, const char *key, char **val)
get string at 'key' from 'table'
Definition n_hash.c:2228
structure of a hash table
Definition n_hash.h:138
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:228
Structure of a generic LIST container.
Definition n_list.h:59
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
Definition n_str.h:68
char * data
the string
Definition n_str.h:63
size_t length
total allocation (in bytes) of the data buffer, padding included
Definition n_str.h:65
int str_to_long_long(const char *s, long long int *i, const int base)
Helper for string to integer.
Definition n_str.c:710
N_STR * n_str_template_expand(const char *tmpl, HASH_TABLE *vars)
Expand double-brace tokens in a template using a hash table.
Definition n_str.c:1775
char * trim_nocopy(char *s)
trim and zero end the string, WARNING: keep and original pointer to delete the string correctly
Definition n_str.c:123
void free_nstr_ptr(void *ptr)
Free a N_STR pointer structure.
Definition n_str.c:70
N_STR * nstrprintf_cat_ex(N_STR **nstr_var, const char *format,...)
Function to allocate, format, and concatenate a string into an N_STR object.
Definition n_str.c:1711
N_STR * n_str_url_encode(const char *src)
Percent-encode a C string per RFC 3986 (unreserved set kept as-is).
Definition n_str.c:1819
size_t NSTRBYTE
N_STR base unit.
Definition n_str.h:58
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
int split_count(char **split_result)
Count split elements.
Definition n_str.c:1007
int str_natncasecmp(const char *a, size_t alen, const char *b, size_t blen)
Compare two byte runs in natural order, ignoring ASCII case.
Definition n_str.c:1524
int str_sanitize_ex(char *string, const NSTRBYTE string_len, const char *mask, const NSTRBYTE masklen, const char replacement)
clean a string by replacing evil characteres
Definition n_str.c:1595
int nstr_to_file(N_STR *str, char *filename)
Write a N_STR content into a file.
Definition n_str.c:431
int scan_dir_ex(const char *dir, const char *pattern, LIST *result, const int recurse, const int mode)
Scan a list of directory and return a list of char *file.
Definition n_str.c:1260
int str_to_long_long_ex(const char *s, NSTRBYTE start, NSTRBYTE end, long long int *i, const int base)
Helper for string[start to end] to long long integer.
Definition n_str.c:638
char * join(char **splitresult, const char *delim)
join the array into a string
Definition n_str.c:1044
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
Definition n_str.c:723
int strlo(const char *string, char *dest)
Lower case a string.
Definition n_str.c:868
N_STR * nstrcat_ex(N_STR **dest, void *src, NSTRBYTE size, int resize_flag)
Append data into N_STR using internal N_STR size and cursor position.
Definition n_str.c:1096
int write_and_fit(char **dest, NSTRBYTE *size, NSTRBYTE *written, const char *src)
Append src to *dest.
Definition n_str.c:1236
#define nstrprintf_cat(__nstr_var, __format,...)
Macro to quickly allocate and sprintf and cat to a N_STR.
Definition n_str.h:121
int strcpy_u(const char *from, char *to, NSTRBYTE to_size, char delim, NSTRBYTE *it)
Copy from start to dest until from[ iterator ] == delim.
Definition n_str.c:891
int str_to_int_ex(const char *s, NSTRBYTE start, NSTRBYTE end, int *i, const int base)
Helper for string[start to end] to integer.
Definition n_str.c:469
int str_to_int_nolog(const char *s, NSTRBYTE start, NSTRBYTE end, int *i, const int base, N_STR **infos)
Helper for string[start to end] to integer.
Definition n_str.c:517
int resize_nstr(N_STR *nstr, size_t size)
reallocate a nstr internal buffer.
Definition n_str.c:1628
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
Definition n_str.c:262
char * nfgets(char *buffer, NSTRBYTE size, FILE *stream)
try to fgets
Definition n_str.c:171
int skipu(const char *string, char toskip, NSTRBYTE *iterator, int inc)
skip until 'toskip' occurence is found from 'iterator' to the next 'toskip' value.
Definition n_str.c:801
#define WILDMAT_ABORT
Abort code to sped up pattern matching.
Definition n_str.h:72
int empty_nstr(N_STR *nstr)
empty a N_STR string
Definition n_str.c:192
int wildmatcase(register const char *text, register const char *p)
Written by Rich Salz rsalz at osf.org, refurbished by me.
Definition n_str.c:1389
#define WILDMAT_NEGATE_CLASS
What character marks an inverted character class?
Definition n_str.h:74
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:209
char * str_replace(const char *string, const char *substr, const char *replacement)
Replace every occurrence of substr by replacement in a copy of string.
Definition n_str.c:1460
char * trim(const char *s)
trim and put a \0 at the end, return new char *
Definition n_str.c:152
int skipw(const char *string, char toskip, NSTRBYTE *iterator, int inc)
skip while 'toskip' occurence is found from 'iterator' to the next non 'toskip' position.
Definition n_str.c:754
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:117
int str_natcasecmp(const char *a, const char *b)
Compare two strings in natural order, ignoring ASCII case.
Definition n_str.c:1582
N_STR * nstrcat_bytes_ex(N_STR **dest, void *data, NSTRBYTE size)
Append data into N_STR using internal N_STR size and cursor position.
Definition n_str.c:1161
int scan_dir(const char *dir, LIST *result, const int recurse)
Scan a list of directory and return a list of char *file.
Definition n_str.c:1247
int str_sanitize(char *string, const char *mask, const char replacement)
clean a string by replacing evil characteres
Definition n_str.c:1618
char ** split(const char *str, const char *delim, int empty)
split the strings into a an array of char *pointer , ended by a NULL one.
Definition n_str.c:927
int wildmat(register const char *text, register const char *p)
Written by Rich Salz rsalz at osf.org, refurbished by me.
Definition n_str.c:1325
int free_nstr_nolog(N_STR **ptr)
Free a N_STR structure and set the pointer to NULL.
Definition n_str.c:97
int str_to_long(const char *s, long int *i, const int base)
Helper for string to integer.
Definition n_str.c:695
int char_to_nstr_ex(const char *from, NSTRBYTE nboct, N_STR **to)
Convert a char into a N_STR, extended version.
Definition n_str.c:239
N_STR * char_to_nstr_nocopy(char *src)
Convert a char into a N_STR, direct use of linked source pointer.
Definition n_str.c:275
int _free_nstr(N_STR **ptr)
Free a N_STR structure and set the pointer to NULL.
Definition n_str.c:83
int write_and_fit_ex(char **dest, NSTRBYTE *size, NSTRBYTE *written, const char *src, NSTRBYTE src_size, NSTRBYTE additional_padding)
Append src_size bytes from src to *dest, updating size/written, growing the buffer if needed.
Definition n_str.c:1194
N_STR * file_to_nstr(char *filename)
Load a whole file into a N_STR.
Definition n_str.c:295
int free_split_result(char ***tab)
Free a split result allocated array.
Definition n_str.c:1023
N_STR * nstrprintf_ex(N_STR **nstr_var, const char *format,...)
Function to allocate and format a string into an N_STR object.
Definition n_str.c:1659
void free_nstr_ptr_nolog(void *ptr)
Free a N_STR pointer structure.
Definition n_str.c:110
int str_to_int(const char *s, int *i, const int base)
Helper for string to integer.
Definition n_str.c:560
int n_str_utf8_valid(const char *s, size_t len)
Test that [s, s+len) is well-formed UTF-8 (RFC 3629): no overlong form, no surrogate,...
Definition n_str.c:1855
int nstr_to_fd(N_STR *str, FILE *out, int lock)
Write a N_STR content into a file.
Definition n_str.c:378
int str_to_long_ex(const char *s, NSTRBYTE start, NSTRBYTE end, long int *i, const int base)
Helper for string[start to end] to long integer.
Definition n_str.c:579
int strup(const char *string, char *dest)
Upper case a string.
Definition n_str.c:847
A box including a string and his lenght.
Definition n_str.h:61
Common headers and low-level functions & define.
Hash functions and table.
Generic log system.
N_STR and string function declaration.