qLibc
qstring.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2026 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qstring.c String APIs.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdbool.h>
36#include <string.h>
37#include <stdarg.h>
38#include <ctype.h>
39#include <unistd.h>
40#include <sys/time.h>
41#include "qinternal.h"
42#include "utilities/qencode.h"
43#include "utilities/qhash.h"
44#include "utilities/qstring.h"
45
46/**
47 * Remove whitespace, including CR and LF, from both ends of a string.
48 *
49 * @param str source string
50 *
51 * @return pointer to `str` on success, or NULL on failure.
52 *
53 * @note This function modifies the source string in place.
54 */
55char *qstrtrim(char *str) {
56 if (str == NULL)
57 return NULL;
58
59 char *ss, *se;
60 for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
61 ss++)
62 ;
63 for (se = ss; *se != '\0'; se++)
64 ;
65 for (se--;
66 se >= ss
67 && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
68 se--)
69 ;
70 se++;
71 *se = '\0';
72
73 if (ss > str) {
74 size_t len = (se - ss) + 1;
75 memmove(str, ss, len);
76 }
77
78 return str;
79}
80
81/**
82 * Remove leading whitespace from a string.
83 *
84 * @param str source string
85 *
86 * @return pointer to `str` on success, or NULL on failure.
87 *
88 * @note This function modifies the source string in place.
89 */
90char *qstrtrim_head(char *str) {
91 if (str == NULL)
92 return NULL;
93
94 char *ss;
95 for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
96 ss++)
97 ;
98
99 if (ss > str) {
100 size_t len = strlen(ss) + 1;
101 memmove(str, ss, len);
102 }
103
104 return str;
105}
106
107/**
108 * Remove trailing whitespace, including CR and LF, from a string.
109 *
110 * @param str source string
111 *
112 * @return pointer to `str` on success, or NULL on failure.
113 *
114 * @note This function modifies the source string in place.
115 */
116char *qstrtrim_tail(char *str) {
117 if (str == NULL)
118 return NULL;
119
120 char *se;
121 for (se = str + strlen(str) - 1;
122 se >= str
123 && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
124 se--)
125 ;
126 se++;
127 *se = '\0';
128
129 return str;
130}
131
132/**
133 * Remove matching characters from the start and end of a string.
134 *
135 * @param str source string
136 * @param head leading character
137 * @param tail trailing character
138 *
139 * @return pointer to `str` on success, or NULL on failure.
140 *
141 * @note This function modifies the source string in place.
142 *
143 * @code
144 * char *str = strdup(" \"hello world\" ");
145 * qstrtrim(str); // remove whitespace
146 * qstrunchar(str, '"', '"'); // remove quotes
147 * @endcode
148 */
149char *qstrunchar(char *str, char head, char tail) {
150 if (str == NULL)
151 return NULL;
152
153 int len = strlen(str);
154 if (len >= 2 && str[0] == head && str[len - 1] == tail) {
155 memmove(str, str + 1, len - 2);
156 str[len - 2] = '\0';
157 } else {
158 return NULL;
159 }
160
161 return str;
162}
163
164/**
165 * Replace tokens or strings in a source string using the given mode.
166 *
167 * @param mode replace mode
168 * @param srcstr source string
169 * @param tokstr token or string to match
170 * @param word replacement string
171 *
172 * @return pointer to the result string on success, or NULL on failure.
173 *
174 * @note
175 * The mode has two characters.
176 *
177 * The first character selects how matching works:
178 * - `t` matches each character in `tokstr` as a token.
179 * - `s` matches `tokstr` as a full string.
180 *
181 * The second character selects where the result is stored:
182 * - `n` returns a newly allocated string.
183 * - `r` writes the result back into `srcstr`.
184 *
185 * When `r` is used, `srcstr` must have enough space for the result, and it
186 * must point to writable memory.
187 *
188 * Supported modes:
189 * - `tn` : token replace, return a new string
190 * - `tr` : token replace, update `srcstr`
191 * - `sn` : string replace, return a new string
192 * - `sr` : string replace, update `srcstr`
193 *
194 * @code
195 * char srcstr[256], *retstr;
196 * char mode[4][2+1] = {"tn", "tr", "sn", "sr"};
197 *
198 * for (i = 0; i < 4; i++) {
199 * strcpy(srcstr, "Welcome to The qDecoder Project.");
200 *
201 * printf("before %s : srcstr = %s\n", mode[i], srcstr);
202 * retstr = qstrreplace(mode[i], srcstr, "The", "_");
203 * printf("after %s : srcstr = %s\n", mode[i], srcstr);
204 * printf(" retstr = %s\n\n", retstr);
205 * if (mode[i][1] == 'n') free(retstr);
206 * }
207 * @endcode
208 */
209char *qstrreplace(const char *mode, char *srcstr, const char *tokstr,
210 const char *word) {
211 if (mode == NULL || strlen(mode) != 2|| srcstr == NULL || tokstr == NULL
212 || word == NULL) {
213 DEBUG("Unknown mode \"%s\".", mode);
214 return NULL;
215 }
216
217 char *newstr, *newp, *srcp, *tokenp, *retp;
218 newstr = newp = srcp = tokenp = retp = NULL;
219
220 char method = mode[0], memuse = mode[1];
221 int maxstrlen, tokstrlen;
222
223 /* Put replaced string into allocated 'newstr' */
224 if (method == 't') { /* Token replace */
225 maxstrlen = strlen(srcstr) * ((strlen(word) > 0) ? strlen(word) : 1);
226 newstr = (char *) malloc(maxstrlen + 1);
227 if (newstr == NULL)
228 return NULL;
229
230 for (srcp = (char *) srcstr, newp = newstr; *srcp; srcp++) {
231 for (tokenp = (char *) tokstr; *tokenp; tokenp++) {
232 if (*srcp == *tokenp) {
233 char *wordp;
234 for (wordp = (char *) word; *wordp; wordp++) {
235 *newp++ = *wordp;
236 }
237 break;
238 }
239 }
240 if (!*tokenp)
241 *newp++ = *srcp;
242 }
243 *newp = '\0';
244 } else if (method == 's') { /* String replace */
245 if (strlen(word) > strlen(tokstr)) {
246 maxstrlen = ((strlen(srcstr) / strlen(tokstr)) * strlen(word))
247 + (strlen(srcstr) % strlen(tokstr));
248 } else {
249 maxstrlen = strlen(srcstr);
250 }
251 newstr = (char *) malloc(maxstrlen + 1);
252 if (newstr == NULL)
253 return NULL;
254
255 tokstrlen = strlen(tokstr);
256
257 for (srcp = srcstr, newp = newstr; *srcp; srcp++) {
258 if (!strncmp(srcp, tokstr, tokstrlen)) {
259 char *wordp;
260 for (wordp = (char *) word; *wordp; wordp++)
261 *newp++ = *wordp;
262 srcp += tokstrlen - 1;
263 } else
264 *newp++ = *srcp;
265 }
266 *newp = '\0';
267 } else {
268 DEBUG("Unknown mode \"%s\".", mode);
269 return NULL;
270 }
271
272 /* decide whether newing the memory or replacing into exist one */
273 if (memuse == 'n')
274 retp = newstr;
275 else if (memuse == 'r') {
276 strcpy(srcstr, newstr);
277 free(newstr);
278 retp = srcstr;
279 } else {
280 DEBUG("Unknown mode \"%s\".", mode);
281 free(newstr);
282 return NULL;
283 }
284
285 return retp;
286}
287
288/**
289 * Copy src string to dst. The dst string array will be always terminated by
290 * NULL character. Also allows overlap between src and dst.
291 *
292 * @param dst pointer to the string to be copied
293 * @param size the size of dst character arrary
294 * @param src pointer to source string
295 *
296 * @return always returns pointer to dst
297 */
298char *qstrcpy(char *dst, size_t size, const char *src) {
299 if (dst == NULL || size == 0 || src == NULL)
300 return dst;
301
302 size_t nbytes = strlen(src);
303 return qstrncpy(dst, size, src, nbytes);
304}
305
306/**
307 * Copy src string to dst no more than n bytes. The dst string array will be
308 * always terminated by NULL character. Also allows overlap between src and dst.
309 *
310 * @param dst pointer to the string to be copied
311 * @param size the size of dst character arrary
312 * @param src pointer to source string
313 * @param nbytes number of bytes to copy
314 *
315 * @return always returns pointer to dst
316 */
317char *qstrncpy(char *dst, size_t size, const char *src, size_t nbytes) {
318 if (dst == NULL || size == 0 || src == NULL)
319 return dst;
320
321 if (nbytes >= size)
322 nbytes = size - 1;
323 memmove((void *) dst, (void *) src, nbytes);
324 dst[nbytes] = '\0';
325
326 return dst;
327}
328
329/**
330 * Duplicate a formatted string.
331 *
332 * @param format string format
333 *
334 * @return newly allocated string on success, or NULL on failure.
335 */
336char *qstrdupf(const char *format, ...) {
337 char *str;
338 DYNAMIC_VSPRINTF(str, format);
339 if (str == NULL)
340 return NULL;
341
342 char *dup = strdup(str);
343 free(str);
344
345 return dup;
346}
347
348/**
349 * Duplicate a substring between two markers.
350 *
351 * @param str source string
352 * @param start start marker
353 * @param end end marker
354 *
355 * @return newly allocated string on success, or NULL on failure.
356 */
357char *qstrdup_between(const char *str, const char *start, const char *end) {
358 char *s;
359 if ((s = strstr(str, start)) == NULL)
360 return NULL;
361 s += strlen(start);
362
363 char *e;
364 if ((e = strstr(s, end)) == NULL)
365 return NULL;
366
367 int len = e - s;
368
369 char *buf = (char *) malloc(sizeof(char) * (len + 1));
370 if (buf == NULL)
371 return NULL;
372
373 strncpy(buf, s, len);
374 buf[len] = '\0';
375 return buf;
376}
377
378/**
379 * Duplicate a block of memory.
380 *
381 * @param data source data
382 * @param size data size
383 *
384 * @return pointer to newly allocated data on success, or NULL on failure.
385 */
386void *qmemdup(const void *data, size_t size) {
387 if (data == NULL || size == 0) {
388 return NULL;
389 }
390
391 void *newdata = malloc(size);
392 if (newdata == NULL) {
393 return NULL;
394 }
395
396 memcpy(newdata, data, size);
397 return newdata;
398}
399
400/**
401 * Append formatted text to the end of a string.
402 *
403 * @param str destination string
404 * @param format string format to append
405 *
406 * @return pointer to `str` on success, or NULL on failure.
407 */
408char *qstrcatf(char *str, const char *format, ...) {
409 char *buf;
410 DYNAMIC_VSPRINTF(buf, format);
411 if (buf == NULL)
412 return NULL;
413
414 char *ret = strcat(str, buf);
415 free(buf);
416 return ret;
417}
418
419/**
420 * Read one line from a string.
421 *
422 * @param buf buffer pointer
423 * @param size buffer size
424 * @param offset pointer to the current position in the source string
425 *
426 * @return pointer to `buf` on success, or NULL on EOF.
427 *
428 * @note
429 * CR and LF will not be stored.
430 *
431 * @code
432 * char *text="Hello\nWorld";
433 *
434 * char *offset = text;
435 * char buf[1024];
436 * while(qstrgets(buf, sizeof(buf), &offset) == NULL) {
437 * printf("%s\n", buf);
438 * }
439 * @endcode
440 */
441char *qstrgets(char *buf, size_t size, char **offset) {
442 if (offset == NULL || *offset == NULL || **offset == '\0')
443 return NULL;
444
445 size_t i;
446 char *from = *offset;
447 char *to = buf;
448 for (i = 0; *from != '\0' && i < (size - 1); i++, from++) {
449 if (*from == '\r')
450 continue;
451 if (*from == '\n') {
452 from++;
453 break;
454 }
455 *to = *from;
456 to++;
457 }
458 *to = '\0';
459 *offset = from;
460
461 return buf;
462}
463
464/**
465 * Reverse the order of characters in the string
466 *
467 * @param str pointer to source string
468 *
469 * @return always returns pointer to str
470 *
471 * @note This function modifies str directly.
472 */
473char *qstrrev(char *str) {
474 if (str == NULL)
475 return str;
476
477 char *p1, *p2;
478 for (p1 = str, p2 = str + (strlen(str) - 1); p2 > p1; p1++, p2--) {
479 char t = *p1;
480 *p1 = *p2;
481 *p2 = t;
482 }
483
484 return str;
485}
486
487/**
488 * Convert character to bigger character.
489 *
490 * @param str pointer to source string
491 *
492 * @return always returns pointer to str
493 *
494 * @note This function modifies str directly.
495 */
496char *qstrupper(char *str) {
497 char *cp;
498
499 if (!str)
500 return NULL;
501 for (cp = str; *cp; cp++)
502 if (*cp >= 'a' && *cp <= 'z')
503 *cp -= 32;
504 return str;
505}
506
507/**
508 * Convert character to lower character.
509 *
510 * @param str pointer to source string
511 *
512 * @return always returns pointer to str
513 *
514 * @note This function modifies str directly.
515 */
516char *qstrlower(char *str) {
517 char *cp;
518
519 if (!str)
520 return NULL;
521 for (cp = str; *cp; cp++)
522 if (*cp >= 'A' && *cp <= 'Z')
523 *cp += 32;
524 return str;
525}
526
527/**
528 * Split a string into tokens.
529 *
530 * @param str source string
531 * @param delimiters string that specifies the delimiter characters
532 * @param retstop stop delimiter character is stored here. This can be
533 * NULL if you do not need it.
534 * @param offset integer pointer used to store the last position.
535 * It must be reset to 0 before the first call.
536 *
537 * @return pointer to the first byte of a token on success, or NULL on failure.
538 *
539 * @code
540 * char *str = strdup("Hello,world|Thank,you");
541 * char *token;
542 * int offset = 0;
543 * while((token = qstrtok(str, "|,", NULL, &offset)) != NULL) {
544 * printf("%s\n", token);
545 * }
546 * @endcode
547 *
548 * @note
549 * This may modify str argument.
550 * The major difference between qstrtok() and standard strtok() is that
551 * qstrtok() can returns empty string tokens. If the str is "a:b::d", qstrtok()
552 * returns "a", "b", "", "d". But strtok() returns "a","b","d".
553 */
554char *qstrtok(char *str, const char *delimiters, char *retstop, int *offset) {
555 char *tokensp, *tokenep;
556
557 tokensp = tokenep = (char *) (str + *offset);
558 int numdel = strlen(delimiters);
559 for (; *tokenep; tokenep++) {
560 int j;
561 for (j = 0; j < numdel; j++) {
562 if (*tokenep == delimiters[j]) {
563 if (retstop != NULL)
564 *retstop = delimiters[j];
565 *tokenep = '\0';
566 tokenep++;
567 *offset = tokenep - str;
568 return tokensp;
569 }
570 }
571 }
572
573 if (retstop != NULL)
574 *retstop = '\0';
575 if (tokensp != tokenep) {
576 *offset = tokenep - str;
577 return tokensp;
578 }
579 return NULL;
580}
581
582/**
583 * Tokenize a string.
584 *
585 * @param str source string
586 * @param delimiters string that specifies a set of delimiters that may
587 * surround the token being extracted
588 *
589 * @return qlist_t pointer on success, or NULL on failure.
590 *
591 * @code
592 * qlist_t *tokens = qstr_tokenizer("a:b:c", ":");
593 * char *str;
594 * while((str = tokens->popfirst(tokens, NULL)) != NULL) {
595 * printf("%s\n", str);
596 * }
597 * tokens->free(tokens);
598 * @endcode
599 */
600qlist_t *qstrtokenizer(const char *str, const char *delimiters) {
601 qlist_t *list = qlist(0);
602 if (list == NULL)
603 return NULL;
604
605 int i;
606 char *dupstr = strdup(str);
607 char *token;
608 int offset = 0;
609 for (i = 1, token = qstrtok(dupstr, delimiters, NULL, &offset);
610 token != NULL;
611 token = qstrtok(dupstr, delimiters, NULL, &offset), i++) {
612 list->addlast(list, token, strlen(token) + 1);
613 }
614 free(dupstr);
615
616 return list;
617}
618
619/**
620 * Generate a unique ID.
621 *
622 * @param seed additional seed string. This can be NULL.
623 *
624 * @return newly allocated string.
625 *
626 * @note
627 * The length of returned string is 32+1 bytes long including terminating NULL
628 * character. It's a good idea to call srand() once before calling this because
629 * it uses rand().
630 */
631char *qstrunique(const char *seed) {
632 long usec;
633#ifdef _WIN32
634 FILETIME ft;
635 GetSystemTimeAsFileTime(&ft);
636 usec = ft.dwLowDateTime % 1000000;
637#else
638 struct timeval tv;
639 gettimeofday(&tv, NULL);
640 usec = tv.tv_usec;
641#endif
642
643 char uniquestr[128];
644 snprintf(uniquestr, sizeof(uniquestr), "%u%d%lu%ld%s", getpid(), rand(),
645 (unsigned long)time(NULL), usec, (seed != NULL) ? seed : "");
646
647 unsigned char md5hash[16];
648 qhashmd5(uniquestr, strlen(uniquestr), md5hash);
649 char *md5ascii = qhex_encode(md5hash, 16);
650
651 return md5ascii;
652}
653
654/**
655 * Convert an integer to a comma-separated string.
656 *
657 * @param number integer
658 *
659 * @return newly allocated string on success, or NULL on failure.
660 */
661char *qstr_comma_number(int number) {
662 char *str, *strp;
663
664 str = strp = (char *) malloc(sizeof(char) * (14 + 1));
665 if (str == NULL)
666 return NULL;
667
668 char buf[10 + 1], *bufp;
669 snprintf(buf, sizeof(buf), "%d", abs(number));
670
671 if (number < 0)
672 *strp++ = '-';
673 for (bufp = buf; *bufp != '\0'; strp++, bufp++) {
674 *strp = *bufp;
675 if ((strlen(bufp)) % 3 == 1 && *(bufp + 1) != '\0')
676 *(++strp) = ',';
677 }
678 *strp = '\0';
679
680 return str;
681}
682
683/**
684 * Test whether a string matches a character rule.
685 *
686 * @param testfunc test function for each character
687 * @param str string to test
688 *
689 * @return true if all characters match, otherwise false.
690 *
691 * @code
692 * if(qstrtest(isalnum, "hello1234") == true) {
693 * printf("It is alpha-numeric string.");
694 * }
695 *
696 * if(qstrtest(isdigit, "0123456789") == true) {
697 * printf("It is alpha-numeric string.");
698 * }
699 * @endcode
700 *
701 * @note
702 * You can use the standard functions below without writing your own version.
703 * Make sure <ctype.h> header should be included before using any of these
704 * functions.
705 * isalnum - checks for an alphanumeric character.
706 * isalpha - checks for an alphabetic character.
707 * isascii - checks whether c is a 7-bit unsigned char value that fits into
708 * the ASCII character set.
709 * isblank - checks for a blank character; that is, a space or a tab.
710 * iscntrl - checks for a control character.
711 * isdigit - checks for a digit (0 through 9).
712 * isgraph - checks for any printable character except space.
713 * islower - checks for a lower-case character.
714 * isprint - checks for any printable character including space.
715 * ispunct - checks for any printable character which is not a space or an
716 * alphanumeric character.
717 * isspace - checks for white-space characters.
718 * isupper - checks for an uppercase letter.
719 * isxdigit - checks for a hexadecimal digits.
720 * See `man isalnum` for more details about these functions.
721 */
722bool qstrtest(int (*testfunc)(int), const char *str) {
723 for (; *str; str++) {
724 if (testfunc(*str) == 0)
725 return false;
726 }
727 return true;
728}
729
730/**
731 * Test for an email-address formatted string
732 *
733 * @param email email-address formatted string
734 *
735 * @return true on success, otherwise false
736 */
737bool qstr_is_email(const char *email) {
738 int i, alpa, dot, gol;
739
740 if (email == NULL)
741 return false;
742
743 for (i = alpa = dot = gol = 0; email[i] != '\0'; i++) {
744 switch (email[i]) {
745 case '@': {
746 if (alpa == 0)
747 return false;
748 if (gol > 0)
749 return false;
750 gol++;
751 break;
752 }
753 case '.': {
754 if ((i > 0) && (email[i - 1] == '@'))
755 return false;
756 if ((gol > 0) && (email[i - 1] == '.'))
757 return false;
758 dot++;
759 break;
760 }
761 default: {
762 alpa++;
763 if ((email[i] >= '0') && (email[i] <= '9'))
764 break;
765 else if ((email[i] >= 'A') && (email[i] <= 'Z'))
766 break;
767 else if ((email[i] >= 'a') && (email[i] <= 'z'))
768 break;
769 else if ((email[i] == '-') || (email[i] == '_'))
770 break;
771 else
772 return false;
773 }
774 }
775 }
776
777 if ((alpa <= 3) || (gol == 0) || (dot == 0))
778 return false;
779 return true;
780}
781
782/**
783 * Test for an IPv4 address string
784 *
785 * @param url IPv4 address string
786 *
787 * @return true on success, otherwise false
788 *
789 * @code
790 * if(qstr_is_ip4addr("1.2.3.4") == true) {
791 * printf("It is IPv4 address string.");
792 * }
793 * @endcode
794 */
795bool qstr_is_ip4addr(const char *str) {
796 char *dupstr = strdup(str);
797
798 char *s1, *s2;
799 int periodcnt;
800 for (s1 = dupstr, periodcnt = 0; (s2 = strchr(s1, '.')) != NULL;
801 s1 = s2 + 1, periodcnt++) {
802 *s2 = '\0';
803
804 int n;
805 if (qstrtest(isdigit, s1) == false || (n = atoi(s1)) <= 0 || n >= 256) {
806 free(dupstr);
807 return false;
808 }
809 }
810
811 free(dupstr);
812 if (periodcnt != 3)
813 return false;
814 return true;
815}
816
817#ifdef __linux__
818#include <iconv.h>
819#endif
820
821/**
822 * Convert character encoding.
823 *
824 * @param str source string. This can be NULL.
825 * @param fromcode source encoding
826 * @param tocode target encoding
827 * @param mag size multiplier between `fromcode` and `tocode`
828 *
829 * @return newly allocated converted string on success, or NULL on failure.
830 *
831 * @code
832 * qCharEncode("KOREAN_EUCKR_STRING", "EUC-KR", "UTF-8", 1.5);
833 * @endcode
834 */
835char *qstr_conv_encoding(const char *str, const char *fromcode,
836 const char *tocode, float mag) {
837#ifdef __linux__
838 if (str == NULL)
839 return NULL;
840
841 char *fromstr = (char *) str;
842 size_t fromsize = strlen(fromstr) + 1;
843
844 size_t tosize = sizeof(char) * ((mag * (fromsize - 1)) + 1);
845 char *tostr = (char *) malloc(tosize);
846 if (tostr == NULL)
847 return NULL;
848
849 char *tostr1 = tostr;
850
851 iconv_t it = iconv_open(tocode, fromcode);
852 if (it < 0) {
853 DEBUG("iconv_open() failed.");
854 return NULL;
855 }
856
857 int ret = iconv(it, &fromstr, &fromsize, &tostr, &tosize);
858 iconv_close(it);
859
860 if (ret < 0) {
861 DEBUG("iconv() failed.");
862 free(tostr1);
863 return NULL;
864 }
865
866 return tostr1;
867#else
868 return NULL;
869#endif
870}
char * qhex_encode(const void *bin, size_t size)
Encode data as hexadecimal digits.
Definition qencode.c:385
bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
Calculate 128-bit(16-bytes) MD5 hash.
Definition qhash.c:67
static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
qhttpclient->head(): Sends a HEAD request.
qlist_t * qlist(int options)
Create new qlist_t linked-list container.
Definition qlist.c:124
char * qstrrev(char *str)
Reverse the order of characters in the string.
Definition qstring.c:473
char * qstrtok(char *str, const char *delimiters, char *retstop, int *offset)
Split a string into tokens.
Definition qstring.c:554
char * qstrcpy(char *dst, size_t size, const char *src)
Copy src string to dst.
Definition qstring.c:298
char * qstrunique(const char *seed)
Generate a unique ID.
Definition qstring.c:631
qlist_t * qstrtokenizer(const char *str, const char *delimiters)
Tokenize a string.
Definition qstring.c:600
char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
Copy src string to dst no more than n bytes.
Definition qstring.c:317
char * qstrtrim(char *str)
Remove whitespace, including CR and LF, from both ends of a string.
Definition qstring.c:55
bool qstrtest(int(*testfunc)(int), const char *str)
Test whether a string matches a character rule.
Definition qstring.c:722
char * qstrtrim_head(char *str)
Remove leading whitespace from a string.
Definition qstring.c:90
char * qstrcatf(char *str, const char *format,...)
Append formatted text to the end of a string.
Definition qstring.c:408
char * qstrunchar(char *str, char head, char tail)
Remove matching characters from the start and end of a string.
Definition qstring.c:149
char * qstrdup_between(const char *str, const char *start, const char *end)
Duplicate a substring between two markers.
Definition qstring.c:357
bool qstr_is_ip4addr(const char *str)
Test for an IPv4 address string.
Definition qstring.c:795
char * qstrdupf(const char *format,...)
Duplicate a formatted string.
Definition qstring.c:336
void * qmemdup(const void *data, size_t size)
Duplicate a block of memory.
Definition qstring.c:386
char * qstrtrim_tail(char *str)
Remove trailing whitespace, including CR and LF, from a string.
Definition qstring.c:116
char * qstrgets(char *buf, size_t size, char **offset)
Read one line from a string.
Definition qstring.c:441
char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
Replace tokens or strings in a source string using the given mode.
Definition qstring.c:209
char * qstrupper(char *str)
Convert character to bigger character.
Definition qstring.c:496
char * qstrlower(char *str)
Convert character to lower character.
Definition qstring.c:516
char * qstr_comma_number(int number)
Convert an integer to a comma-separated string.
Definition qstring.c:661
char * qstr_conv_encoding(const char *str, const char *fromcode, const char *tocode, float mag)
Convert character encoding.
Definition qstring.c:835
bool qstr_is_email(const char *email)
Test for an email-address formatted string.
Definition qstring.c:737