Ref/unref all args in mpdm_split().
[mpdm.git] / mpdm_s.c
blob361967f7431d9f50c82539f82feca5d1764858a2
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2010 Angel Ortega <angel@triptico.com>
6 mpdm_s.c - String management
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.triptico.com
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <wchar.h>
32 #include <locale.h>
33 #include <wctype.h>
35 #ifdef CONFOPT_GETTEXT
36 #include <libintl.h>
37 #endif
39 #ifdef CONFOPT_WIN32
40 #include <windows.h>
41 #endif
43 #include "mpdm.h"
46 /** code **/
48 void *mpdm_poke_o(void *dst, int *dsize, int *offset, const void *org, int osize, int esize)
50 if (org != NULL && osize) {
51 /* enough room? */
52 if (*offset + osize > *dsize) {
53 /* no; enlarge */
54 *dsize += osize;
56 dst = realloc(dst, *dsize * esize);
59 memcpy((char *)dst + (*offset * esize), org, osize * esize);
60 *offset += osize;
63 return dst;
67 void *mpdm_poke(void *dst, int *dsize, const void *org, int osize, int esize)
68 /* pokes (adds) org into dst, which is a dynamic string, making it grow */
70 int offset = *dsize;
72 return mpdm_poke_o(dst, dsize, &offset, org, osize, esize);
76 wchar_t *mpdm_pokewsn(wchar_t *dst, int *dsize, const wchar_t *str, int slen)
77 /* adds a wide string to dst using mpdm_poke() with size */
79 if (str)
80 dst = mpdm_poke(dst, dsize, str, slen, sizeof(wchar_t));
82 return dst;
86 wchar_t *mpdm_pokews(wchar_t *dst, int *dsize, const wchar_t *str)
87 /* adds a wide string to dst using mpdm_poke() */
89 if (str)
90 dst = mpdm_pokewsn(dst, dsize, str, wcslen(str));
92 return dst;
96 wchar_t *mpdm_pokev(wchar_t * dst, int *dsize, const mpdm_t v)
97 /* adds the string in v to dst using mpdm_poke() */
99 if (v != NULL) {
100 const wchar_t *ptr = mpdm_string(v);
102 dst = mpdm_pokews(dst, dsize, ptr);
105 return dst;
109 wchar_t *mpdm_mbstowcs(const char *str, int *s, int l)
110 /* converts an mbs to a wcs, but filling invalid chars
111 with question marks instead of just failing */
113 wchar_t *ptr = NULL;
114 char tmp[64]; /* really MB_CUR_MAX + 1 */
115 wchar_t wc;
116 int n, i, c, t = 0;
117 char *cstr;
119 /* allow NULL values for s */
120 if (s == NULL)
121 s = &t;
123 /* if there is a limit, duplicate and break the string */
124 if (l >= 0) {
125 cstr = strdup(str);
126 cstr[l] = '\0';
128 else
129 cstr = (char *) str;
131 /* try first a direct conversion with mbstowcs */
132 if ((*s = mbstowcs(NULL, cstr, 0)) != -1) {
133 /* direct conversion is possible; do it */
134 if ((ptr = malloc((*s + 1) * sizeof(wchar_t))) != NULL) {
135 mbstowcs(ptr, cstr, *s);
136 ptr[*s] = L'\0';
139 else {
140 /* zero everything */
141 *s = n = i = 0;
143 for (;;) {
144 /* no more characters to process? */
145 if ((c = cstr[n + i]) == '\0' && i == 0)
146 break;
148 tmp[i++] = c;
149 tmp[i] = '\0';
151 /* try to convert */
152 if (mbstowcs(&wc, tmp, 1) == (size_t) -1) {
153 /* can still be an incomplete multibyte char? */
154 if (c != '\0' && i <= (int) MB_CUR_MAX)
155 continue;
156 else {
157 /* too many failing bytes; skip 1 byte */
158 wc = L'?';
159 i = 1;
163 /* skip used bytes and back again */
164 n += i;
165 i = 0;
167 /* store new char */
168 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
169 break;
172 /* null terminate and count one less */
173 if (ptr != NULL) {
174 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
175 (*s)--;
179 /* free the duplicate */
180 if (cstr != str)
181 free(cstr);
183 return ptr;
187 char *mpdm_wcstombs(const wchar_t * str, int *s)
188 /* converts a wcs to an mbs, but filling invalid chars
189 with question marks instead of just failing */
191 char *ptr = NULL;
192 char tmp[64]; /* really MB_CUR_MAX + 1 */
193 int l, t = 0;
195 /* allow NULL values for s */
196 if (s == NULL)
197 s = &t;
199 /* try first a direct conversion with wcstombs */
200 if ((*s = wcstombs(NULL, str, 0)) != -1) {
201 /* direct conversion is possible; do it and return */
202 if ((ptr = malloc(*s + 1)) != NULL) {
203 wcstombs(ptr, str, *s);
204 ptr[*s] = '\0';
207 return ptr;
210 /* invalid encoding? convert characters one by one */
211 *s = 0;
213 while (*str) {
214 if ((l = wctomb(tmp, *str)) <= 0) {
215 /* if char couldn't be converted,
216 write a question mark instead */
217 l = wctomb(tmp, L'?');
220 tmp[l] = '\0';
221 if ((ptr = mpdm_poke(ptr, s, tmp, l, 1)) == NULL)
222 break;
224 str++;
227 /* null terminate and count one less */
228 if (ptr != NULL) {
229 ptr = mpdm_poke(ptr, s, "", 1, 1);
230 (*s)--;
233 return ptr;
237 mpdm_t mpdm_new_wcs(int flags, const wchar_t * str, int size, int cpy)
238 /* creates a new string value from a wcs */
240 wchar_t *ptr;
242 /* a size of -1 means 'calculate it' */
243 if (size == -1 && str != NULL)
244 size = wcslen(str);
246 /* create a copy? */
247 if (cpy) {
248 /* free() on destruction */
249 flags |= MPDM_FREE;
251 /* allocs */
252 if ((ptr = malloc((size + 1) * sizeof(wchar_t))) == NULL)
253 return NULL;
255 /* if no source, reset to zeroes; otherwise, copy */
256 if (str == NULL)
257 memset(ptr, '\0', size * sizeof(wchar_t));
258 else {
259 wcsncpy(ptr, str, size);
260 ptr[size] = L'\0';
263 else
264 ptr = (wchar_t *)str;
266 /* it's a string */
267 flags |= MPDM_STRING;
269 return mpdm_new(flags, ptr, size);
273 mpdm_t mpdm_new_mbstowcs(int flags, const char *str, int l)
274 /* creates a new string value from an mbs */
276 wchar_t *ptr;
277 int size;
279 if ((ptr = mpdm_mbstowcs(str, &size, l)) == NULL)
280 return NULL;
282 /* it's a string */
283 flags |= (MPDM_STRING | MPDM_FREE);
285 return mpdm_new(flags, ptr, size);
289 mpdm_t mpdm_new_wcstombs(int flags, const wchar_t * str)
290 /* creates a new mbs value from a wbs */
292 char *ptr;
293 int size;
295 ptr = mpdm_wcstombs(str, &size);
297 flags |= MPDM_FREE;
299 /* unset the string flag; mbs,s are not 'strings' */
300 flags &= ~MPDM_STRING;
302 return mpdm_new(flags, ptr, size);
306 mpdm_t mpdm_new_i(int ival)
307 /* creates a new string value from an integer */
309 mpdm_t v;
310 char tmp[32];
312 /* creates the visual representation */
313 snprintf(tmp, sizeof(tmp), "%d", ival);
315 v = MPDM_MBS(tmp);
317 return mpdm_set_ival(v, ival);
321 mpdm_t mpdm_new_r(double rval)
322 /* creates a new string value from a real number */
324 mpdm_t v;
325 char tmp[128];
327 /* creates the visual representation */
328 snprintf(tmp, sizeof(tmp), "%lf", rval);
330 /* manually strip useless zeroes */
331 if (strchr(tmp, '.') != NULL) {
332 char *ptr;
334 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
336 /* if it's over the ., strip it also */
337 if (*ptr != '.')
338 ptr++;
340 *ptr = '\0';
343 v = MPDM_MBS(tmp);
345 return mpdm_set_rval(v, rval);
349 /* interface */
352 * mpdm_string - Returns a printable representation of a value.
353 * @v: the value
355 * Returns a printable representation of a value. For strings, it's
356 * the value data itself; for any other type, a conversion to string
357 * is returned instead. This value should be used immediately, as it
358 * can be a pointer to a static buffer.
359 * [Strings]
361 wchar_t *mpdm_string(const mpdm_t v)
363 static wchar_t wtmp[32];
364 char tmp[32];
366 /* if it's NULL, return a constant */
367 if (v == NULL)
368 return L"[NULL]";
370 /* if it's a string, return it */
371 if (v->flags & MPDM_STRING)
372 return (wchar_t *) v->data;
374 /* otherwise, return a visual representation */
375 snprintf(tmp, sizeof(tmp), "%p", v);
376 mbstowcs(wtmp, tmp, sizeof(wtmp));
377 wtmp[(sizeof(wtmp) / sizeof(wchar_t)) - 1] = L'\0';
379 return wtmp;
384 * mpdm_cmp - Compares two values.
385 * @v1: the first value
386 * @v2: the second value
388 * Compares two values. If both has the MPDM_STRING flag set,
389 * a comparison using wcscoll() is returned; if both are arrays,
390 * the size is compared first and, if they have the same number
391 * elements, each one is compared; otherwise, a simple pointer
392 * comparison is done.
393 * [Strings]
395 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
397 int r;
399 /* same values? */
400 if (v1 == v2)
401 return 0;
403 /* is any value NULL? */
404 if (v1 == NULL)
405 return -1;
406 if (v2 == NULL)
407 return 1;
409 /* different values, but same content? (unlikely) */
410 if (v1->data == v2->data)
411 return 0;
413 if (MPDM_IS_STRING(v1) && MPDM_IS_STRING(v2))
414 r = wcscoll((wchar_t *) v1->data, (wchar_t *) v2->data);
415 else
416 if (MPDM_IS_ARRAY(v1) && MPDM_IS_ARRAY(v2)) {
417 /* compare first the sizes */
418 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
419 int n;
421 /* they have the same size;
422 compare each pair of elements */
423 for (n = 0; n < mpdm_size(v1); n++) {
424 if ((r = mpdm_cmp(mpdm_aget(v1, n),
425 mpdm_aget(v2, n))) != 0)
426 break;
430 else
431 /* in any other case, compare just pointers */
432 r = (int) ((char *)v1->data - (char *)v2->data);
434 return r;
439 * mpdm_cmp_s - Compares two values (string version).
440 * @v1: the first value
441 * @v2: the second value
443 * Compares two values. Compares both values using wcscoll()
444 * if the first one is a string, or returns 1 otherwise.
446 int mpdm_cmp_s(const mpdm_t v1, const wchar_t *v2)
448 int r;
450 if (MPDM_IS_STRING(v1))
451 r = wcscoll((wchar_t *) v1->data, v2);
452 else
453 r = (int) ((wchar_t *)v1->data - v2);
455 return r;
460 * mpdm_splice - Creates a new string value from another.
461 * @v: the original value
462 * @i: the value to be inserted
463 * @offset: offset where the substring is to be inserted
464 * @del: number of characters to delete
466 * Creates a new string value from @v, deleting @del chars at @offset
467 * and substituting them by @i. If @del is 0, no deletion is done.
468 * both @offset and @del can be negative; if this is the case, it's
469 * assumed as counting from the end of @v. If @v is NULL, @i will become
470 * the new string, and both @offset and @del will be ignored. If @v is
471 * not NULL and @i is, no insertion process is done (only deletion, if
472 * applicable).
474 * Returns a two element array, with the new string in the first
475 * element and the deleted string in the second (with a NULL value
476 * if @del is 0).
477 * [Strings]
479 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i, int offset, int del)
481 mpdm_t w;
482 mpdm_t n = NULL;
483 mpdm_t d = NULL;
484 int os, ns, r;
485 int ins = 0;
486 wchar_t *ptr;
488 if (v != NULL) {
489 os = mpdm_size(v);
491 /* negative offsets start from the end */
492 if (offset < 0)
493 offset = os + 1 - offset;
495 /* never add further the end */
496 if (offset > os)
497 offset = os;
499 /* negative del counts as 'characters left' */
500 if (del < 0)
501 del = os + 1 - offset + del;
503 /* something to delete? */
504 if (del > 0) {
505 /* never delete further the end */
506 if (offset + del > os)
507 del = os - offset;
509 /* deleted string */
510 d = MPDM_NS(((wchar_t *) v->data) + offset, del);
512 else
513 del = 0;
515 /* something to insert? */
516 ins = mpdm_size(i);
518 /* new size and remainder */
519 ns = os + ins - del;
520 r = offset + del;
522 if ((n = MPDM_NS(NULL, ns)) == NULL)
523 return NULL;
525 ptr = (wchar_t *)n->data;
527 /* copy the beginning */
528 if (offset > 0) {
529 wcsncpy(ptr, v->data, offset);
530 ptr += offset;
533 /* copy the text to be inserted */
534 if (ins > 0) {
535 wcsncpy(ptr, i->data, ins);
536 ptr += ins;
539 /* copy the remaining */
540 os -= r;
541 if (os > 0) {
542 wcsncpy(ptr, ((wchar_t *) v->data) + r, os);
543 ptr += os;
546 /* null terminate */
547 *ptr = L'\0';
549 else
550 n = i;
552 /* creates the output array */
553 w = MPDM_A(2);
555 mpdm_aset(w, n, 0);
556 mpdm_aset(w, d, 1);
558 return w;
563 * mpdm_strcat_sn - Concatenates two strings (string with size version).
564 * @s1: the first string
565 * @s2: the second string
566 * @size: the size of the second string
568 * Returns a new string formed by the concatenation of @s1 and @s2.
569 * [Strings]
571 mpdm_t mpdm_strcat_sn(const mpdm_t s1, const wchar_t *s2, int size)
573 wchar_t *ptr = NULL;
574 int s = 0;
576 if (s1 == NULL && s2 == NULL)
577 return NULL;
579 ptr = mpdm_pokev(ptr, &s, s1);
580 ptr = mpdm_pokewsn(ptr, &s, s2, size);
582 /* if no characters were added, returns an empty string */
583 if (ptr == NULL)
584 return MPDM_LS(L"");
586 ptr = mpdm_poke(ptr, &s, L"", 1, sizeof(wchar_t));
587 return MPDM_ENS(ptr, s - 1);
592 * mpdm_strcat_s - Concatenates two strings (string version).
593 * @s1: the first string
594 * @s2: the second string
596 * Returns a new string formed by the concatenation of @s1 and @s2.
597 * [Strings]
599 mpdm_t mpdm_strcat_s(const mpdm_t s1, const wchar_t *s2)
601 return mpdm_strcat_sn(s1, s2, s2 ? wcslen(s2) : 0);
606 * mpdm_strcat - Concatenates two strings.
607 * @s1: the first string
608 * @s2: the second string
610 * Returns a new string formed by the concatenation of @s1 and @s2.
611 * [Strings]
613 mpdm_t mpdm_strcat(const mpdm_t s1, const mpdm_t s2)
615 return mpdm_strcat_s(s1, s2 ? mpdm_string(s2) : NULL);
620 * mpdm_ival - Returns a value's data as an integer.
621 * @v: the value
623 * Returns a value's data as an integer. If the value is a string,
624 * it's converted via sscanf and returned; non-string values have all
625 * an ival of 0. The converted integer is cached, so costly string
626 * conversions are only done once. Values created with the MPDM_IVAL
627 * flag set have its ival cached from the beginning.
628 * [Strings]
629 * [Value Management]
631 int mpdm_ival(mpdm_t v)
633 if (v == NULL)
634 return 0;
636 /* if there is no cached integer, calculate it */
637 if (!(v->flags & MPDM_IVAL)) {
638 int i = 0;
640 /* if it's a string, calculate it; other
641 values will have an ival of 0 */
642 if (v->flags & MPDM_STRING) {
643 char tmp[32];
644 char *fmt = "%i";
646 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
647 tmp[sizeof(tmp) - 1] = '\0';
649 /* workaround for mingw32: as it doesn't
650 correctly parse octal and hexadecimal
651 numbers, they are tried as special cases */
652 if (tmp[0] == '0') {
653 if (tmp[1] == 'b' || tmp[1] == 'B') {
654 /* binary number */
655 fmt = NULL;
656 char *ptr = &tmp[2];
658 while (*ptr == '0' || *ptr == '1') {
659 i <<= 1;
661 if (*ptr == '1')
662 i |= 1;
664 ptr++;
667 else
668 if (tmp[1] == 'x' || tmp[1] == 'X')
669 fmt = "%x";
670 else
671 fmt = "%o";
674 if (fmt != NULL)
675 sscanf(tmp, fmt, &i);
678 mpdm_set_ival(v, i);
681 return v->ival;
686 * mpdm_rval - Returns a value's data as a real number (double).
687 * @v: the value
689 * Returns a value's data as a real number (double float). If the value
690 * is a string, it's converted via sscanf and returned; non-string values
691 * have all an rval of 0. The converted double is cached, so costly string
692 * conversions are only done once. Values created with the MPDM_RVAL
693 * flag set have its rval cached from the beginning.
694 * [Strings]
695 * [Value Management]
697 double mpdm_rval(mpdm_t v)
699 if (v == NULL)
700 return 0;
702 /* if there is no cached double, calculate it */
703 if (!(v->flags & MPDM_RVAL)) {
704 double r = 0.0;
706 /* if it's a string, calculate it; other
707 values will have an rval of 0.0 */
708 if (v->flags & MPDM_STRING) {
709 char tmp[128];
710 char *prev_locale;
712 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
713 tmp[sizeof(tmp) - 1] = '\0';
715 /* if the number starts with 0, it's
716 an octal or hexadecimal number; just
717 take the integer value and cast it */
718 if (tmp[0] == '0' && tmp[1] != '.')
719 r = (double) mpdm_ival(v);
720 else {
721 /* set locale to C for non locale-dependent
722 floating point conversion */
723 prev_locale = setlocale(LC_NUMERIC, "C");
725 /* read */
726 sscanf(tmp, "%lf", &r);
728 /* set previous locale */
729 setlocale(LC_NUMERIC, prev_locale);
733 mpdm_set_rval(v, r);
736 return v->rval;
741 * mpdm_gettext - Translates a string to the current language.
742 * @str: the string
744 * Translates the @str string to the current language.
746 * This function can still be used even if there is no real gettext
747 * support() by manually filling the __I18N__ hash.
749 * If the string is found in the current table, the translation is
750 * returned; otherwise, the same @str value is returned.
751 * [Strings]
752 * [Localization]
754 mpdm_t mpdm_gettext(const mpdm_t str)
756 mpdm_t v;
757 mpdm_t i18n = NULL;
759 /* gets the cache, if any */
760 if ((i18n = mpdm_hget_s(mpdm_root(), L"__I18N__")) == NULL)
761 return str;
763 /* try first the cache */
764 if ((v = mpdm_hget(i18n, str)) == NULL) {
765 #ifdef CONFOPT_GETTEXT
766 char *s;
767 mpdm_t t;
769 /* convert to mbs */
770 t = mpdm_ref(MPDM_2MBS(str->data));
772 /* ask gettext for it */
773 s = gettext((char *) t->data);
775 /* create new value only if it's different */
776 if (s != t->data) {
777 v = MPDM_MBS(s);
779 /* store in the cache */
780 mpdm_hset(i18n, str, v);
782 else
783 v = str;
785 mpdm_unref(t);
787 #else /* CONFOPT_GETTEXT */
789 v = str;
791 #endif /* CONFOPT_GETTEXT */
794 return v;
799 * mpdm_gettext_domain - Sets domain and data directory for translations.
800 * @dom: the domain (application name)
801 * @data: directory contaning the .mo files
803 * Sets the domain (application name) and translation data for translating
804 * strings that will be returned by mpdm_gettext().@data must point to a
805 * directory containing the .mo (compiled .po) files.
807 * If there is no gettext support, returns 0, or 1 otherwise.
808 * [Strings]
809 * [Localization]
811 int mpdm_gettext_domain(const mpdm_t dom, const mpdm_t data)
813 int ret = 0;
815 #ifdef CONFOPT_GETTEXT
817 mpdm_t dm;
818 mpdm_t dt;
820 /* convert both to mbs,s */
821 dm = mpdm_ref(MPDM_2MBS(dom->data));
822 dt = mpdm_ref(MPDM_2MBS(data->data));
824 /* bind and set domain */
825 bindtextdomain((char *) dm->data, (char *) dt->data);
826 textdomain((char *) dm->data);
828 mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
830 mpdm_unref(dt);
831 mpdm_unref(dm);
833 ret = 1;
835 #endif /* CONFOPT_GETTEXT */
837 #ifdef CONFOPT_WIN32
839 mpdm_t v;
841 if ((v = mpdm_hget_s(mpdm_root(), L"ENV")) != NULL &&
842 mpdm_hget_s(v, L"LANG") == NULL) {
843 wchar_t *wptr = L"en";
845 /* MS Windows crappy language constants... */
847 switch((GetSystemDefaultLangID() & 0x00ff)) {
848 case 0x01: wptr = L"ar"; break; /* arabic */
849 case 0x02: wptr = L"bg"; break; /* bulgarian */
850 case 0x03: wptr = L"ca"; break; /* catalan */
851 case 0x04: wptr = L"zh"; break; /* chinese */
852 case 0x05: wptr = L"cz"; break; /* czech */
853 case 0x06: wptr = L"da"; break; /* danish */
854 case 0x07: wptr = L"de"; break; /* german */
855 case 0x08: wptr = L"el"; break; /* greek */
856 case 0x09: wptr = L"en"; break; /* english */
857 case 0x0a: wptr = L"es"; break; /* spanish */
858 case 0x0b: wptr = L"fi"; break; /* finnish */
859 case 0x0c: wptr = L"fr"; break; /* french */
860 case 0x0d: wptr = L"he"; break; /* hebrew */
861 case 0x0e: wptr = L"hu"; break; /* hungarian */
862 case 0x0f: wptr = L"is"; break; /* icelandic */
863 case 0x10: wptr = L"it"; break; /* italian */
864 case 0x11: wptr = L"jp"; break; /* japanese */
865 case 0x12: wptr = L"ko"; break; /* korean */
866 case 0x13: wptr = L"nl"; break; /* dutch */
867 case 0x14: wptr = L"no"; break; /* norwegian */
868 case 0x15: wptr = L"po"; break; /* polish */
869 case 0x16: wptr = L"pt"; break; /* portuguese */
870 case 0x17: wptr = L"rm"; break; /* romansh (switzerland) */
871 case 0x18: wptr = L"ro"; break; /* romanian */
872 case 0x19: wptr = L"ru"; break; /* russian */
873 case 0x1a: wptr = L"sr"; break; /* serbian */
874 case 0x1b: wptr = L"sk"; break; /* slovak */
875 case 0x1c: wptr = L"sq"; break; /* albanian */
876 case 0x1d: wptr = L"sv"; break; /* swedish */
879 mpdm_hset_s(v, L"LANG", MPDM_S(wptr));
882 #endif /* CONFOPT_WIN32 */
884 return ret;
888 #ifdef CONFOPT_WCWIDTH
890 int wcwidth(wchar_t);
892 int mpdm_wcwidth(wchar_t c)
894 return wcwidth(c);
897 #else /* CONFOPT_WCWIDTH */
899 #include "wcwidth.c"
901 int mpdm_wcwidth(wchar_t c)
903 return mk_wcwidth(c);
906 #endif /* CONFOPT_WCWIDTH */
910 * mpdm_sprintf - Formats a sprintf()-like string.
911 * @fmt: the string format
912 * @args: an array of values
914 * Formats a string using the sprintf() format taking the values from @args.
915 * [Strings]
917 mpdm_t mpdm_sprintf(const mpdm_t fmt, const mpdm_t args)
919 const wchar_t *i = fmt->data;
920 wchar_t *o = NULL;
921 int l = 0, n = 0;
922 wchar_t c;
924 /* loop all characters */
925 while ((c = *i++) != L'\0') {
926 int m = 0;
927 wchar_t *tptr = NULL;
928 wchar_t *wptr = NULL;
930 if (c == L'%') {
931 /* format directive */
932 char t_fmt[128];
933 char tmp[1024];
934 mpdm_t v;
935 char *ptr = NULL;
937 /* transfer the % */
938 t_fmt[m++] = '%';
940 /* transform the format to mbs */
941 while (*i != L'\0' &&
942 m < (int)(sizeof(t_fmt) - MB_CUR_MAX - 1) &&
943 wcschr(L"-.0123456789", *i) != NULL)
944 m += wctomb(&t_fmt[m], *i++);
946 /* transfer the directive */
947 m += wctomb(&t_fmt[m], *i++);
949 t_fmt[m] = '\0';
951 /* by default, copies the format */
952 strcpy(tmp, t_fmt);
954 /* pick next value */
955 v = mpdm_aget(args, n++);
957 switch (t_fmt[m - 1]) {
958 case 'd':
959 case 'i':
960 case 'u':
961 case 'x':
962 case 'X':
963 case 'o':
965 /* integer value */
966 snprintf(tmp, sizeof(tmp) - 1,
967 t_fmt, mpdm_ival(v));
968 break;
970 case 'f':
972 /* float (real) value */
973 snprintf(tmp, sizeof(tmp) - 1,
974 t_fmt, mpdm_rval(v));
975 break;
977 case 's':
979 /* string value */
980 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
981 snprintf(tmp, sizeof(tmp) - 1, t_fmt, ptr);
982 free(ptr);
984 break;
986 case 'c':
988 /* char */
989 m = 1;
990 wptr = &c;
991 c = mpdm_ival(v);
992 break;
994 case 'b':
996 ptr = tmp;
997 unsigned int mask;
998 int p = 0;
1000 mask = 1 << ((sizeof(int) * 8) - 1);
1001 while (mask) {
1002 if (mask & (unsigned int) mpdm_ival(v)) {
1003 *ptr++ = '1';
1004 p = 1;
1006 else
1007 if (p)
1008 *ptr++ = '0';
1010 mask >>= 1;
1013 if (ptr == tmp)
1014 *ptr++ = '0';
1016 *ptr = '\0';
1017 break;
1019 case '%':
1021 /* percent sign */
1022 m = 1;
1023 wptr = &c;
1024 break;
1027 /* transfer */
1028 if (wptr == NULL)
1029 wptr = tptr = mpdm_mbstowcs(tmp, &m, -1);
1031 else {
1032 /* raw character */
1033 m = 1;
1034 wptr = &c;
1037 /* transfer */
1038 o = mpdm_poke(o, &l, wptr, m, sizeof(wchar_t));
1040 /* free the temporary buffer, if any */
1041 if (tptr != NULL)
1042 free(tptr);
1045 if (o == NULL)
1046 return NULL;
1048 /* null-terminate */
1049 o = mpdm_poke(o, &l, L"", 1, sizeof(wchar_t));
1051 return MPDM_ENS(o, l - 1);
1056 * mpdm_ulc - Converts a string to uppercase or lowecase.
1057 * @s: the string
1058 * @u: convert to uppercase (1) or to lowercase (0).
1060 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
1061 * [Strings]
1063 mpdm_t mpdm_ulc(const mpdm_t s, int u)
1065 mpdm_t r = NULL;
1066 wchar_t *optr;
1067 int i = mpdm_size(s);
1069 if ((optr = malloc((i + 1) * sizeof(wchar_t))) != NULL) {
1070 wchar_t *iptr = mpdm_string(s);
1071 int n;
1073 for (n = 0; n < i; n++)
1074 optr[n] = u ? towupper(iptr[n]) : towlower(iptr[n]);
1076 optr[n] = L'\0';
1077 r = MPDM_ENS(optr, i);
1080 return r;
1084 /* scanf working buffers */
1085 #define SCANF_BUF_SIZE 1024
1086 static wchar_t scanf_yset[SCANF_BUF_SIZE];
1087 static wchar_t scanf_nset[SCANF_BUF_SIZE];
1088 static wchar_t scanf_mark[SCANF_BUF_SIZE];
1090 struct {
1091 wchar_t cmd;
1092 wchar_t *yset;
1093 wchar_t *nset;
1094 } scanf_sets[] = {
1095 { L's', L"", L" \t" },
1096 { L'u', L"0123456789", L"" },
1097 { L'd', L"-0123456789", L"" },
1098 { L'i', L"-0123456789", L"" },
1099 { L'f', L"-0123456789.", L"" },
1100 { L'x', L"-0123456789xabcdefABCDEF", L"" },
1101 { L'\0', NULL, NULL },
1105 * mpdm_sscanf - Extracts data like sscanf().
1106 * @fmt: the string format
1107 * @str: the string to be parsed
1108 * @offset: the character offset to start scanning
1110 * Extracts data from a string using a special format pattern, very
1111 * much like the scanf() series of functions in the C library. Apart
1112 * from the standard percent-sign-commands (s, u, d, i, f, x,
1113 * n, [, with optional size and * to ignore), it implements S,
1114 * to match a string of characters upto what follows in the format
1115 * string. Also, the [ set of characters can include other % formats.
1117 * Returns an array with the extracted values. If %n is used, the
1118 * position in the scanned string is returned as the value.
1119 * [Strings]
1121 mpdm_t mpdm_sscanf(const mpdm_t fmt, const mpdm_t str, int offset)
1123 wchar_t *i = (wchar_t *)str->data;
1124 wchar_t *f = (wchar_t *)fmt->data;
1125 mpdm_t r;
1127 i += offset;
1128 r = MPDM_A(0);
1130 while (*f) {
1131 if (*f == L'%') {
1132 wchar_t *ptr = NULL;
1133 int size = 0;
1134 wchar_t cmd;
1135 int vsize = 0;
1136 int ignore = 0;
1137 int msize = 0;
1139 /* empty all buffers */
1140 scanf_yset[0] = scanf_nset[0] = scanf_mark[0] = L'\0';
1142 f++;
1144 /* an asterisk? don't return next value */
1145 if (*f == L'*') {
1146 ignore = 1;
1147 f++;
1150 /* does it have a size? */
1151 while (wcschr(L"0123456789", *f)) {
1152 vsize *= 10;
1153 vsize += *f - L'0';
1154 f++;
1157 /* if no size, set it to an arbitrary big limit */
1158 if (!vsize)
1159 vsize = 0xfffffff;
1161 /* now *f should contain a command */
1162 cmd = *f;
1163 f++;
1165 /* is it a verbatim percent sign? */
1166 if (cmd == L'%') {
1167 vsize = 1;
1168 ignore = 1;
1169 wcscpy(scanf_yset, L"%");
1171 else
1172 /* a position? */
1173 if (cmd == L'n') {
1174 vsize = 0;
1175 ignore = 1;
1176 mpdm_push(r, MPDM_I(i - (wchar_t *)str->data));
1178 else
1179 /* string upto a mark */
1180 if (cmd == L'S') {
1181 wchar_t *tmp = f;
1183 /* fill the mark upto another command */
1184 while (*tmp) {
1185 if (*tmp == L'%') {
1186 tmp++;
1188 /* is it an 'n'? ignore and go on */
1189 if (*tmp == L'n') {
1190 tmp++;
1191 continue;
1193 else
1194 if (*tmp == L'%')
1195 scanf_mark[msize++] = *tmp;
1196 else
1197 break;
1199 else
1200 scanf_mark[msize++] = *tmp;
1202 tmp++;
1205 scanf_mark[msize] = L'\0';
1207 else
1208 /* raw set */
1209 if (cmd == L'[') {
1210 int n = 0;
1211 wchar_t *set = scanf_yset;
1213 /* is it an inverse set? */
1214 if (*f == L'^') {
1215 set = scanf_nset;
1216 f++;
1219 /* first one is a ]? add it */
1220 if (*f == L']') {
1221 set[n++] = *f;
1222 f++;
1225 /* now build the set */
1226 for (; n < SCANF_BUF_SIZE - 1 && *f && *f != L']'; f++) {
1227 /* is it a range? */
1228 if (*f == L'-') {
1229 f++;
1231 /* start or end? hyphen itself */
1232 if (n == 0 || *f == L']')
1233 set[n++] = L'-';
1234 else {
1235 /* pick previous char */
1236 wchar_t c = set[n - 1];
1238 /* fill */
1239 while (n < SCANF_BUF_SIZE - 1 && c < *f)
1240 set[n++] = ++c;
1243 else
1244 /* is it another command? */
1245 if (*f == L'%') {
1246 int i;
1248 f++;
1249 for (i = 0; scanf_sets[i].cmd; i++) {
1250 if (*f == scanf_sets[i].cmd) {
1251 set[n] = L'\0';
1252 wcscat(set, scanf_sets[i].yset);
1253 n += wcslen(scanf_sets[i].yset);
1254 break;
1258 else
1259 set[n++] = *f;
1262 /* skip the ] */
1263 f++;
1265 set[n] = L'\0';
1267 else
1268 /* a standard set? */
1270 int n;
1272 for (n = 0; scanf_sets[n].cmd != L'\0'; n++) {
1273 if (cmd == scanf_sets[n].cmd) {
1274 wcscpy(scanf_yset, scanf_sets[n].yset);
1275 wcscpy(scanf_nset, scanf_sets[n].nset);
1276 break;
1281 /* now fill the dynamic string */
1282 while (vsize &&
1283 !wcschr(scanf_nset, *i) &&
1284 (scanf_yset[0] == L'\0' || wcschr(scanf_yset, *i)) &&
1285 (msize == 0 || wcsncmp(i, scanf_mark, msize) != 0)) {
1287 /* only add if not being ignored */
1288 if (!ignore)
1289 ptr = mpdm_poke(ptr, &size, i, 1, sizeof(wchar_t));
1291 i++;
1292 vsize--;
1295 if (!ignore && size) {
1296 /* null terminate and push */
1297 ptr = mpdm_poke(ptr, &size, L"", 1, sizeof(wchar_t));
1298 mpdm_push(r, MPDM_ENS(ptr, size - 1));
1301 else
1302 if (*f == L' ' || *f == L'\t') {
1303 /* if it's a blank, sync to next non-blank */
1304 f++;
1306 while (*i == L' ' || *i == L'\t')
1307 i++;
1309 else
1310 /* test for literals in the format string */
1311 if (*i == *f) {
1312 i++;
1313 f++;
1315 else
1316 break;
1319 return r;