Ref/unref arguments in mpdm_cmp().
[mpdm.git] / mpdm_s.c
blobdc4b3106a1df01a6c022559f80b7b79eeca13999
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 mpdm_ref(v);
103 dst = mpdm_pokews(dst, dsize, ptr);
104 mpdm_unref(v);
107 return dst;
111 wchar_t *mpdm_mbstowcs(const char *str, int *s, int l)
112 /* converts an mbs to a wcs, but filling invalid chars
113 with question marks instead of just failing */
115 wchar_t *ptr = NULL;
116 char tmp[64]; /* really MB_CUR_MAX + 1 */
117 wchar_t wc;
118 int n, i, c, t = 0;
119 char *cstr;
121 /* allow NULL values for s */
122 if (s == NULL)
123 s = &t;
125 /* if there is a limit, duplicate and break the string */
126 if (l >= 0) {
127 cstr = strdup(str);
128 cstr[l] = '\0';
130 else
131 cstr = (char *) str;
133 /* try first a direct conversion with mbstowcs */
134 if ((*s = mbstowcs(NULL, cstr, 0)) != -1) {
135 /* direct conversion is possible; do it */
136 if ((ptr = malloc((*s + 1) * sizeof(wchar_t))) != NULL) {
137 mbstowcs(ptr, cstr, *s);
138 ptr[*s] = L'\0';
141 else {
142 /* zero everything */
143 *s = n = i = 0;
145 for (;;) {
146 /* no more characters to process? */
147 if ((c = cstr[n + i]) == '\0' && i == 0)
148 break;
150 tmp[i++] = c;
151 tmp[i] = '\0';
153 /* try to convert */
154 if (mbstowcs(&wc, tmp, 1) == (size_t) -1) {
155 /* can still be an incomplete multibyte char? */
156 if (c != '\0' && i <= (int) MB_CUR_MAX)
157 continue;
158 else {
159 /* too many failing bytes; skip 1 byte */
160 wc = L'?';
161 i = 1;
165 /* skip used bytes and back again */
166 n += i;
167 i = 0;
169 /* store new char */
170 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
171 break;
174 /* null terminate and count one less */
175 if (ptr != NULL) {
176 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
177 (*s)--;
181 /* free the duplicate */
182 if (cstr != str)
183 free(cstr);
185 return ptr;
189 char *mpdm_wcstombs(const wchar_t * str, int *s)
190 /* converts a wcs to an mbs, but filling invalid chars
191 with question marks instead of just failing */
193 char *ptr = NULL;
194 char tmp[64]; /* really MB_CUR_MAX + 1 */
195 int l, t = 0;
197 /* allow NULL values for s */
198 if (s == NULL)
199 s = &t;
201 /* try first a direct conversion with wcstombs */
202 if ((*s = wcstombs(NULL, str, 0)) != -1) {
203 /* direct conversion is possible; do it and return */
204 if ((ptr = malloc(*s + 1)) != NULL) {
205 wcstombs(ptr, str, *s);
206 ptr[*s] = '\0';
209 return ptr;
212 /* invalid encoding? convert characters one by one */
213 *s = 0;
215 while (*str) {
216 if ((l = wctomb(tmp, *str)) <= 0) {
217 /* if char couldn't be converted,
218 write a question mark instead */
219 l = wctomb(tmp, L'?');
222 tmp[l] = '\0';
223 if ((ptr = mpdm_poke(ptr, s, tmp, l, 1)) == NULL)
224 break;
226 str++;
229 /* null terminate and count one less */
230 if (ptr != NULL) {
231 ptr = mpdm_poke(ptr, s, "", 1, 1);
232 (*s)--;
235 return ptr;
239 mpdm_t mpdm_new_wcs(int flags, const wchar_t * str, int size, int cpy)
240 /* creates a new string value from a wcs */
242 wchar_t *ptr;
244 /* a size of -1 means 'calculate it' */
245 if (size == -1 && str != NULL)
246 size = wcslen(str);
248 /* create a copy? */
249 if (cpy) {
250 /* free() on destruction */
251 flags |= MPDM_FREE;
253 /* allocs */
254 if ((ptr = malloc((size + 1) * sizeof(wchar_t))) == NULL)
255 return NULL;
257 /* if no source, reset to zeroes; otherwise, copy */
258 if (str == NULL)
259 memset(ptr, '\0', size * sizeof(wchar_t));
260 else {
261 wcsncpy(ptr, str, size);
262 ptr[size] = L'\0';
265 else
266 ptr = (wchar_t *)str;
268 /* it's a string */
269 flags |= MPDM_STRING;
271 return mpdm_new(flags, ptr, size);
275 mpdm_t mpdm_new_mbstowcs(int flags, const char *str, int l)
276 /* creates a new string value from an mbs */
278 wchar_t *ptr;
279 int size;
281 if ((ptr = mpdm_mbstowcs(str, &size, l)) == NULL)
282 return NULL;
284 /* it's a string */
285 flags |= (MPDM_STRING | MPDM_FREE);
287 return mpdm_new(flags, ptr, size);
291 mpdm_t mpdm_new_wcstombs(int flags, const wchar_t * str)
292 /* creates a new mbs value from a wbs */
294 char *ptr;
295 int size;
297 ptr = mpdm_wcstombs(str, &size);
299 flags |= MPDM_FREE;
301 /* unset the string flag; mbs,s are not 'strings' */
302 flags &= ~MPDM_STRING;
304 return mpdm_new(flags, ptr, size);
308 mpdm_t mpdm_new_i(int ival)
309 /* creates a new string value from an integer */
311 mpdm_t v;
312 char tmp[32];
314 /* creates the visual representation */
315 snprintf(tmp, sizeof(tmp), "%d", ival);
317 v = MPDM_MBS(tmp);
319 return mpdm_set_ival(v, ival);
323 mpdm_t mpdm_new_r(double rval)
324 /* creates a new string value from a real number */
326 mpdm_t v;
327 char tmp[128];
329 /* creates the visual representation */
330 snprintf(tmp, sizeof(tmp), "%lf", rval);
332 /* manually strip useless zeroes */
333 if (strchr(tmp, '.') != NULL) {
334 char *ptr;
336 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
338 /* if it's over the ., strip it also */
339 if (*ptr != '.')
340 ptr++;
342 *ptr = '\0';
345 v = MPDM_MBS(tmp);
347 return mpdm_set_rval(v, rval);
351 /* interface */
354 * mpdm_string - Returns a printable representation of a value.
355 * @v: the value
357 * Returns a printable representation of a value. For strings, it's
358 * the value data itself; for any other type, a conversion to string
359 * is returned instead. This value should be used immediately, as it
360 * can be a pointer to a static buffer.
361 * [Strings]
363 wchar_t *mpdm_string(const mpdm_t v)
365 static wchar_t wtmp[32];
366 char tmp[32];
367 static wchar_t *ret;
369 /* if it's NULL, return a constant */
370 if (v == NULL)
371 ret = L"[NULL]";
372 else
373 /* if it's a string, return it */
374 if (v->flags & MPDM_STRING)
375 ret = (wchar_t *) v->data;
376 else {
377 /* otherwise, return a visual representation */
378 snprintf(tmp, sizeof(tmp), "%p", v);
379 mbstowcs(wtmp, tmp, sizeof(wtmp));
380 wtmp[(sizeof(wtmp) / sizeof(wchar_t)) - 1] = L'\0';
382 ret = wtmp;
385 return ret;
390 * mpdm_cmp - Compares two values.
391 * @v1: the first value
392 * @v2: the second value
394 * Compares two values. If both has the MPDM_STRING flag set,
395 * a comparison using wcscoll() is returned; if both are arrays,
396 * the size is compared first and, if they have the same number
397 * elements, each one is compared; otherwise, a simple pointer
398 * comparison is done.
399 * [Strings]
401 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
403 int r;
405 mpdm_ref(v1);
406 mpdm_ref(v2);
408 /* same values? */
409 if (v1 == v2)
410 r = 0;
411 else
412 /* is any value NULL? */
413 if (v1 == NULL)
414 r = -1;
415 else
416 if (v2 == NULL)
417 r = 1;
418 else
419 /* different values, but same content? (unlikely) */
420 if (v1->data == v2->data)
421 r = 0;
422 else
423 if (MPDM_IS_STRING(v1) && MPDM_IS_STRING(v2))
424 r = wcscoll((wchar_t *) v1->data, (wchar_t *) v2->data);
425 else
426 if (MPDM_IS_ARRAY(v1) && MPDM_IS_ARRAY(v2)) {
427 /* compare first the sizes */
428 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
429 int n;
431 /* they have the same size;
432 compare each pair of elements */
433 for (n = 0; n < mpdm_size(v1); n++) {
434 if ((r = mpdm_cmp(mpdm_aget(v1, n),
435 mpdm_aget(v2, n))) != 0)
436 break;
440 else
441 /* in any other case, compare just pointers */
442 r = (int) ((char *)v1->data - (char *)v2->data);
444 mpdm_unref(v2);
445 mpdm_unref(v1);
447 return r;
452 * mpdm_cmp_s - Compares two values (string version).
453 * @v1: the first value
454 * @v2: the second value
456 * Compares two values. Compares both values using wcscoll()
457 * if the first one is a string, or returns 1 otherwise.
459 int mpdm_cmp_s(const mpdm_t v1, const wchar_t *v2)
461 int r;
463 mpdm_ref(v1);
465 if (MPDM_IS_STRING(v1))
466 r = wcscoll((wchar_t *) v1->data, v2);
467 else
468 r = (int) ((wchar_t *)v1->data - v2);
470 mpdm_unref(v1);
472 return r;
477 * mpdm_splice - Creates a new string value from another.
478 * @v: the original value
479 * @i: the value to be inserted
480 * @offset: offset where the substring is to be inserted
481 * @del: number of characters to delete
483 * Creates a new string value from @v, deleting @del chars at @offset
484 * and substituting them by @i. If @del is 0, no deletion is done.
485 * both @offset and @del can be negative; if this is the case, it's
486 * assumed as counting from the end of @v. If @v is NULL, @i will become
487 * the new string, and both @offset and @del will be ignored. If @v is
488 * not NULL and @i is, no insertion process is done (only deletion, if
489 * applicable).
491 * Returns a two element array, with the new string in the first
492 * element and the deleted string in the second (with a NULL value
493 * if @del is 0).
494 * [Strings]
496 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i, int offset, int del)
498 mpdm_t w;
499 mpdm_t n = NULL;
500 mpdm_t d = NULL;
501 int os, ns, r;
502 int ins = 0;
503 wchar_t *ptr;
505 mpdm_ref(v);
506 mpdm_ref(i);
508 if (v != NULL) {
509 os = mpdm_size(v);
511 /* negative offsets start from the end */
512 if (offset < 0)
513 offset = os + 1 - offset;
515 /* never add further the end */
516 if (offset > os)
517 offset = os;
519 /* negative del counts as 'characters left' */
520 if (del < 0)
521 del = os + 1 - offset + del;
523 /* something to delete? */
524 if (del > 0) {
525 /* never delete further the end */
526 if (offset + del > os)
527 del = os - offset;
529 /* deleted string */
530 d = MPDM_NS(((wchar_t *) v->data) + offset, del);
532 else
533 del = 0;
535 /* something to insert? */
536 ins = mpdm_size(i);
538 /* new size and remainder */
539 ns = os + ins - del;
540 r = offset + del;
542 n = MPDM_NS(NULL, ns);
544 ptr = (wchar_t *)n->data;
546 /* copy the beginning */
547 if (offset > 0) {
548 wcsncpy(ptr, v->data, offset);
549 ptr += offset;
552 /* copy the text to be inserted */
553 if (ins > 0) {
554 wcsncpy(ptr, i->data, ins);
555 ptr += ins;
558 /* copy the remaining */
559 os -= r;
560 if (os > 0) {
561 wcsncpy(ptr, ((wchar_t *) v->data) + r, os);
562 ptr += os;
565 /* null terminate */
566 *ptr = L'\0';
568 else
569 n = i;
571 /* creates the output array */
572 w = MPDM_A(2);
574 mpdm_ref(w);
575 mpdm_aset(w, n, 0);
576 mpdm_aset(w, d, 1);
577 mpdm_unrefnd(w);
579 mpdm_unref(i);
580 mpdm_unref(v);
582 return w;
587 * mpdm_strcat_sn - Concatenates two strings (string with size version).
588 * @s1: the first string
589 * @s2: the second string
590 * @size: the size of the second string
592 * Returns a new string formed by the concatenation of @s1 and @s2.
593 * [Strings]
595 mpdm_t mpdm_strcat_sn(const mpdm_t s1, const wchar_t *s2, int size)
597 wchar_t *ptr = NULL;
598 int s = 0;
599 mpdm_t r;
601 if (s1 == NULL && s2 == NULL)
602 r = NULL;
603 else {
604 ptr = mpdm_pokev(ptr, &s, s1);
605 ptr = mpdm_pokewsn(ptr, &s, s2, size);
607 ptr = mpdm_poke(ptr, &s, L"", 1, sizeof(wchar_t));
608 r = MPDM_ENS(ptr, s - 1);
611 return r;
616 * mpdm_strcat_s - Concatenates two strings (string version).
617 * @s1: the first string
618 * @s2: the second string
620 * Returns a new string formed by the concatenation of @s1 and @s2.
621 * [Strings]
623 mpdm_t mpdm_strcat_s(const mpdm_t s1, const wchar_t *s2)
625 return mpdm_strcat_sn(s1, s2, s2 ? wcslen(s2) : 0);
630 * mpdm_strcat - Concatenates two strings.
631 * @s1: the first string
632 * @s2: the second string
634 * Returns a new string formed by the concatenation of @s1 and @s2.
635 * [Strings]
637 mpdm_t mpdm_strcat(const mpdm_t s1, const mpdm_t s2)
639 mpdm_t r;
641 mpdm_ref(s2);
642 r = mpdm_strcat_s(s1, s2 ? mpdm_string(s2) : NULL);
643 mpdm_unref(s2);
645 return r;
650 * mpdm_ival - Returns a value's data as an integer.
651 * @v: the value
653 * Returns a value's data as an integer. If the value is a string,
654 * it's converted via sscanf and returned; non-string values have all
655 * an ival of 0. The converted integer is cached, so costly string
656 * conversions are only done once. Values created with the MPDM_IVAL
657 * flag set have its ival cached from the beginning.
658 * [Strings]
659 * [Value Management]
661 int mpdm_ival(mpdm_t v)
663 int i = 0;
665 mpdm_ref(v);
667 if (v != NULL) {
668 /* if there is no cached integer, calculate it */
669 if (!(v->flags & MPDM_IVAL)) {
670 /* if it's a string, calculate it; other
671 values will have an ival of 0 */
672 if (v->flags & MPDM_STRING) {
673 char tmp[32];
674 char *fmt = "%i";
676 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
677 tmp[sizeof(tmp) - 1] = '\0';
679 /* workaround for mingw32: as it doesn't
680 correctly parse octal and hexadecimal
681 numbers, they are tried as special cases */
682 if (tmp[0] == '0') {
683 if (tmp[1] == 'b' || tmp[1] == 'B') {
684 /* binary number */
685 fmt = NULL;
686 char *ptr = &tmp[2];
688 while (*ptr == '0' || *ptr == '1') {
689 i <<= 1;
691 if (*ptr == '1')
692 i |= 1;
694 ptr++;
697 else
698 if (tmp[1] == 'x' || tmp[1] == 'X')
699 fmt = "%x";
700 else
701 fmt = "%o";
704 if (fmt != NULL)
705 sscanf(tmp, fmt, &i);
708 mpdm_set_ival(v, i);
711 i = v->ival;
714 mpdm_unref(v);
716 return i;
721 * mpdm_rval - Returns a value's data as a real number (double).
722 * @v: the value
724 * Returns a value's data as a real number (double float). If the value
725 * is a string, it's converted via sscanf and returned; non-string values
726 * have all an rval of 0. The converted double is cached, so costly string
727 * conversions are only done once. Values created with the MPDM_RVAL
728 * flag set have its rval cached from the beginning.
729 * [Strings]
730 * [Value Management]
732 double mpdm_rval(mpdm_t v)
734 double r = 0.0;
736 mpdm_ref(v);
738 if (v != NULL) {
739 /* if there is no cached double, calculate it */
740 if (!(v->flags & MPDM_RVAL)) {
741 /* if it's a string, calculate it; other
742 values will have an rval of 0.0 */
743 if (v->flags & MPDM_STRING) {
744 char tmp[128];
745 char *prev_locale;
747 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
748 tmp[sizeof(tmp) - 1] = '\0';
750 /* if the number starts with 0, it's
751 an octal or hexadecimal number; just
752 take the integer value and cast it */
753 if (tmp[0] == '0' && tmp[1] != '.')
754 r = (double) mpdm_ival(v);
755 else {
756 /* set locale to C for non locale-dependent
757 floating point conversion */
758 prev_locale = setlocale(LC_NUMERIC, "C");
760 /* read */
761 sscanf(tmp, "%lf", &r);
763 /* set previous locale */
764 setlocale(LC_NUMERIC, prev_locale);
768 mpdm_set_rval(v, r);
771 r = v->rval;
774 mpdm_unref(v);
776 return r;
781 * mpdm_gettext - Translates a string to the current language.
782 * @str: the string
784 * Translates the @str string to the current language.
786 * This function can still be used even if there is no real gettext
787 * support() by manually filling the __I18N__ hash.
789 * If the string is found in the current table, the translation is
790 * returned; otherwise, the same @str value is returned.
791 * [Strings]
792 * [Localization]
794 mpdm_t mpdm_gettext(const mpdm_t str)
796 mpdm_t v;
797 mpdm_t i18n = NULL;
799 /* gets the cache */
800 if ((i18n = mpdm_hget_s(mpdm_root(), L"__I18N__")) == NULL)
801 i18n = mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
803 mpdm_ref(str);
805 /* try first the cache */
806 if ((v = mpdm_hget(i18n, str)) == NULL) {
807 #ifdef CONFOPT_GETTEXT
808 char *s;
809 mpdm_t t;
811 /* convert to mbs */
812 t = mpdm_ref(MPDM_2MBS(str->data));
814 /* ask gettext for it */
815 s = gettext((char *) t->data);
817 if (s != t->data)
818 v = MPDM_MBS(s);
819 else
820 v = str;
822 mpdm_unref(t);
824 #else /* CONFOPT_GETTEXT */
826 v = str;
828 #endif /* CONFOPT_GETTEXT */
830 /* store in the cache */
831 mpdm_hset(i18n, str, v);
834 mpdm_unref(str);
836 return v;
841 * mpdm_gettext_domain - Sets domain and data directory for translations.
842 * @dom: the domain (application name)
843 * @data: directory contaning the .mo files
845 * Sets the domain (application name) and translation data for translating
846 * strings that will be returned by mpdm_gettext().@data must point to a
847 * directory containing the .mo (compiled .po) files.
849 * If there is no gettext support, returns 0, or 1 otherwise.
850 * [Strings]
851 * [Localization]
853 int mpdm_gettext_domain(const mpdm_t dom, const mpdm_t data)
855 int ret = 0;
857 mpdm_ref(dom);
858 mpdm_ref(data);
860 #ifdef CONFOPT_GETTEXT
862 mpdm_t dm;
863 mpdm_t dt;
865 /* convert both to mbs,s */
866 dm = mpdm_ref(MPDM_2MBS(dom->data));
867 dt = mpdm_ref(MPDM_2MBS(data->data));
869 /* bind and set domain */
870 bindtextdomain((char *) dm->data, (char *) dt->data);
871 textdomain((char *) dm->data);
873 mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
875 mpdm_unref(dt);
876 mpdm_unref(dm);
878 ret = 1;
880 #endif /* CONFOPT_GETTEXT */
882 #ifdef CONFOPT_WIN32
884 mpdm_t v;
886 if ((v = mpdm_hget_s(mpdm_root(), L"ENV")) != NULL &&
887 mpdm_hget_s(v, L"LANG") == NULL) {
888 wchar_t *wptr = L"en";
890 /* MS Windows crappy language constants... */
892 switch((GetSystemDefaultLangID() & 0x00ff)) {
893 case 0x01: wptr = L"ar"; break; /* arabic */
894 case 0x02: wptr = L"bg"; break; /* bulgarian */
895 case 0x03: wptr = L"ca"; break; /* catalan */
896 case 0x04: wptr = L"zh"; break; /* chinese */
897 case 0x05: wptr = L"cz"; break; /* czech */
898 case 0x06: wptr = L"da"; break; /* danish */
899 case 0x07: wptr = L"de"; break; /* german */
900 case 0x08: wptr = L"el"; break; /* greek */
901 case 0x09: wptr = L"en"; break; /* english */
902 case 0x0a: wptr = L"es"; break; /* spanish */
903 case 0x0b: wptr = L"fi"; break; /* finnish */
904 case 0x0c: wptr = L"fr"; break; /* french */
905 case 0x0d: wptr = L"he"; break; /* hebrew */
906 case 0x0e: wptr = L"hu"; break; /* hungarian */
907 case 0x0f: wptr = L"is"; break; /* icelandic */
908 case 0x10: wptr = L"it"; break; /* italian */
909 case 0x11: wptr = L"jp"; break; /* japanese */
910 case 0x12: wptr = L"ko"; break; /* korean */
911 case 0x13: wptr = L"nl"; break; /* dutch */
912 case 0x14: wptr = L"no"; break; /* norwegian */
913 case 0x15: wptr = L"po"; break; /* polish */
914 case 0x16: wptr = L"pt"; break; /* portuguese */
915 case 0x17: wptr = L"rm"; break; /* romansh (switzerland) */
916 case 0x18: wptr = L"ro"; break; /* romanian */
917 case 0x19: wptr = L"ru"; break; /* russian */
918 case 0x1a: wptr = L"sr"; break; /* serbian */
919 case 0x1b: wptr = L"sk"; break; /* slovak */
920 case 0x1c: wptr = L"sq"; break; /* albanian */
921 case 0x1d: wptr = L"sv"; break; /* swedish */
924 mpdm_hset_s(v, L"LANG", MPDM_S(wptr));
927 #endif /* CONFOPT_WIN32 */
929 mpdm_unref(data);
930 mpdm_unref(dom);
932 return ret;
936 #ifdef CONFOPT_WCWIDTH
938 int wcwidth(wchar_t);
940 int mpdm_wcwidth(wchar_t c)
942 return wcwidth(c);
945 #else /* CONFOPT_WCWIDTH */
947 #include "wcwidth.c"
949 int mpdm_wcwidth(wchar_t c)
951 return mk_wcwidth(c);
954 #endif /* CONFOPT_WCWIDTH */
958 * mpdm_sprintf - Formats a sprintf()-like string.
959 * @fmt: the string format
960 * @args: an array of values
962 * Formats a string using the sprintf() format taking the values from @args.
963 * [Strings]
965 mpdm_t mpdm_sprintf(const mpdm_t fmt, const mpdm_t args)
967 const wchar_t *i = fmt->data;
968 wchar_t *o = NULL;
969 int l = 0, n = 0;
970 wchar_t c;
972 mpdm_ref(fmt);
973 mpdm_ref(args);
975 /* loop all characters */
976 while ((c = *i++) != L'\0') {
977 int m = 0;
978 wchar_t *tptr = NULL;
979 wchar_t *wptr = NULL;
981 if (c == L'%') {
982 /* format directive */
983 char t_fmt[128];
984 char tmp[1024];
985 mpdm_t v;
986 char *ptr = NULL;
988 /* transfer the % */
989 t_fmt[m++] = '%';
991 /* transform the format to mbs */
992 while (*i != L'\0' &&
993 m < (int)(sizeof(t_fmt) - MB_CUR_MAX - 1) &&
994 wcschr(L"-.0123456789", *i) != NULL)
995 m += wctomb(&t_fmt[m], *i++);
997 /* transfer the directive */
998 m += wctomb(&t_fmt[m], *i++);
1000 t_fmt[m] = '\0';
1002 /* by default, copies the format */
1003 strcpy(tmp, t_fmt);
1005 /* pick next value */
1006 v = mpdm_aget(args, n++);
1008 switch (t_fmt[m - 1]) {
1009 case 'd':
1010 case 'i':
1011 case 'u':
1012 case 'x':
1013 case 'X':
1014 case 'o':
1016 /* integer value */
1017 snprintf(tmp, sizeof(tmp) - 1,
1018 t_fmt, mpdm_ival(v));
1019 break;
1021 case 'f':
1023 /* float (real) value */
1024 snprintf(tmp, sizeof(tmp) - 1,
1025 t_fmt, mpdm_rval(v));
1026 break;
1028 case 's':
1030 /* string value */
1031 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
1032 snprintf(tmp, sizeof(tmp) - 1, t_fmt, ptr);
1033 free(ptr);
1035 break;
1037 case 'c':
1039 /* char */
1040 m = 1;
1041 wptr = &c;
1042 c = mpdm_ival(v);
1043 break;
1045 case 'b':
1047 ptr = tmp;
1048 unsigned int mask;
1049 int p = 0;
1051 mask = 1 << ((sizeof(int) * 8) - 1);
1052 while (mask) {
1053 if (mask & (unsigned int) mpdm_ival(v)) {
1054 *ptr++ = '1';
1055 p = 1;
1057 else
1058 if (p)
1059 *ptr++ = '0';
1061 mask >>= 1;
1064 if (ptr == tmp)
1065 *ptr++ = '0';
1067 *ptr = '\0';
1068 break;
1070 case '%':
1072 /* percent sign */
1073 m = 1;
1074 wptr = &c;
1075 break;
1078 /* transfer */
1079 if (wptr == NULL)
1080 wptr = tptr = mpdm_mbstowcs(tmp, &m, -1);
1082 else {
1083 /* raw character */
1084 m = 1;
1085 wptr = &c;
1088 /* transfer */
1089 o = mpdm_poke(o, &l, wptr, m, sizeof(wchar_t));
1091 /* free the temporary buffer, if any */
1092 if (tptr != NULL)
1093 free(tptr);
1096 if (o == NULL)
1097 return NULL;
1099 /* null-terminate */
1100 o = mpdm_poke(o, &l, L"", 1, sizeof(wchar_t));
1102 mpdm_unref(args);
1103 mpdm_unref(fmt);
1105 return MPDM_ENS(o, l - 1);
1110 * mpdm_ulc - Converts a string to uppercase or lowecase.
1111 * @s: the string
1112 * @u: convert to uppercase (1) or to lowercase (0).
1114 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
1115 * [Strings]
1117 mpdm_t mpdm_ulc(const mpdm_t s, int u)
1119 mpdm_t r = NULL;
1120 wchar_t *optr;
1121 int i;
1123 mpdm_ref(s);
1125 i = mpdm_size(s);
1127 if ((optr = malloc((i + 1) * sizeof(wchar_t))) != NULL) {
1128 wchar_t *iptr = mpdm_string(s);
1129 int n;
1131 for (n = 0; n < i; n++)
1132 optr[n] = u ? towupper(iptr[n]) : towlower(iptr[n]);
1134 optr[n] = L'\0';
1135 r = MPDM_ENS(optr, i);
1138 mpdm_unref(s);
1140 return r;
1144 /* scanf working buffers */
1145 #define SCANF_BUF_SIZE 1024
1146 static wchar_t scanf_yset[SCANF_BUF_SIZE];
1147 static wchar_t scanf_nset[SCANF_BUF_SIZE];
1148 static wchar_t scanf_mark[SCANF_BUF_SIZE];
1150 struct {
1151 wchar_t cmd;
1152 wchar_t *yset;
1153 wchar_t *nset;
1154 } scanf_sets[] = {
1155 { L's', L"", L" \t" },
1156 { L'u', L"0123456789", L"" },
1157 { L'd', L"-0123456789", L"" },
1158 { L'i', L"-0123456789", L"" },
1159 { L'f', L"-0123456789.", L"" },
1160 { L'x', L"-0123456789xabcdefABCDEF", L"" },
1161 { L'\0', NULL, NULL },
1165 * mpdm_sscanf - Extracts data like sscanf().
1166 * @fmt: the string format
1167 * @str: the string to be parsed
1168 * @offset: the character offset to start scanning
1170 * Extracts data from a string using a special format pattern, very
1171 * much like the scanf() series of functions in the C library. Apart
1172 * from the standard percent-sign-commands (s, u, d, i, f, x,
1173 * n, [, with optional size and * to ignore), it implements S,
1174 * to match a string of characters upto what follows in the format
1175 * string. Also, the [ set of characters can include other % formats.
1177 * Returns an array with the extracted values. If %n is used, the
1178 * position in the scanned string is returned as the value.
1179 * [Strings]
1181 mpdm_t mpdm_sscanf(const mpdm_t fmt, const mpdm_t str, int offset)
1183 wchar_t *i = (wchar_t *)str->data;
1184 wchar_t *f = (wchar_t *)fmt->data;
1185 mpdm_t r;
1187 mpdm_ref(fmt);
1188 mpdm_ref(str);
1190 i += offset;
1191 r = MPDM_A(0);
1192 mpdm_ref(r);
1194 while (*f) {
1195 if (*f == L'%') {
1196 wchar_t *ptr = NULL;
1197 int size = 0;
1198 wchar_t cmd;
1199 int vsize = 0;
1200 int ignore = 0;
1201 int msize = 0;
1203 /* empty all buffers */
1204 scanf_yset[0] = scanf_nset[0] = scanf_mark[0] = L'\0';
1206 f++;
1208 /* an asterisk? don't return next value */
1209 if (*f == L'*') {
1210 ignore = 1;
1211 f++;
1214 /* does it have a size? */
1215 while (wcschr(L"0123456789", *f)) {
1216 vsize *= 10;
1217 vsize += *f - L'0';
1218 f++;
1221 /* if no size, set it to an arbitrary big limit */
1222 if (!vsize)
1223 vsize = 0xfffffff;
1225 /* now *f should contain a command */
1226 cmd = *f;
1227 f++;
1229 /* is it a verbatim percent sign? */
1230 if (cmd == L'%') {
1231 vsize = 1;
1232 ignore = 1;
1233 wcscpy(scanf_yset, L"%");
1235 else
1236 /* a position? */
1237 if (cmd == L'n') {
1238 vsize = 0;
1239 ignore = 1;
1240 mpdm_push(r, MPDM_I(i - (wchar_t *)str->data));
1242 else
1243 /* string upto a mark */
1244 if (cmd == L'S') {
1245 wchar_t *tmp = f;
1247 /* fill the mark upto another command */
1248 while (*tmp) {
1249 if (*tmp == L'%') {
1250 tmp++;
1252 /* is it an 'n'? ignore and go on */
1253 if (*tmp == L'n') {
1254 tmp++;
1255 continue;
1257 else
1258 if (*tmp == L'%')
1259 scanf_mark[msize++] = *tmp;
1260 else
1261 break;
1263 else
1264 scanf_mark[msize++] = *tmp;
1266 tmp++;
1269 scanf_mark[msize] = L'\0';
1271 else
1272 /* raw set */
1273 if (cmd == L'[') {
1274 int n = 0;
1275 wchar_t *set = scanf_yset;
1277 /* is it an inverse set? */
1278 if (*f == L'^') {
1279 set = scanf_nset;
1280 f++;
1283 /* first one is a ]? add it */
1284 if (*f == L']') {
1285 set[n++] = *f;
1286 f++;
1289 /* now build the set */
1290 for (; n < SCANF_BUF_SIZE - 1 && *f && *f != L']'; f++) {
1291 /* is it a range? */
1292 if (*f == L'-') {
1293 f++;
1295 /* start or end? hyphen itself */
1296 if (n == 0 || *f == L']')
1297 set[n++] = L'-';
1298 else {
1299 /* pick previous char */
1300 wchar_t c = set[n - 1];
1302 /* fill */
1303 while (n < SCANF_BUF_SIZE - 1 && c < *f)
1304 set[n++] = ++c;
1307 else
1308 /* is it another command? */
1309 if (*f == L'%') {
1310 int i;
1312 f++;
1313 for (i = 0; scanf_sets[i].cmd; i++) {
1314 if (*f == scanf_sets[i].cmd) {
1315 set[n] = L'\0';
1316 wcscat(set, scanf_sets[i].yset);
1317 n += wcslen(scanf_sets[i].yset);
1318 break;
1322 else
1323 set[n++] = *f;
1326 /* skip the ] */
1327 f++;
1329 set[n] = L'\0';
1331 else
1332 /* a standard set? */
1334 int n;
1336 for (n = 0; scanf_sets[n].cmd != L'\0'; n++) {
1337 if (cmd == scanf_sets[n].cmd) {
1338 wcscpy(scanf_yset, scanf_sets[n].yset);
1339 wcscpy(scanf_nset, scanf_sets[n].nset);
1340 break;
1345 /* now fill the dynamic string */
1346 while (vsize &&
1347 !wcschr(scanf_nset, *i) &&
1348 (scanf_yset[0] == L'\0' || wcschr(scanf_yset, *i)) &&
1349 (msize == 0 || wcsncmp(i, scanf_mark, msize) != 0)) {
1351 /* only add if not being ignored */
1352 if (!ignore)
1353 ptr = mpdm_poke(ptr, &size, i, 1, sizeof(wchar_t));
1355 i++;
1356 vsize--;
1359 if (!ignore && size) {
1360 /* null terminate and push */
1361 ptr = mpdm_poke(ptr, &size, L"", 1, sizeof(wchar_t));
1362 mpdm_push(r, MPDM_ENS(ptr, size - 1));
1365 else
1366 if (*f == L' ' || *f == L'\t') {
1367 /* if it's a blank, sync to next non-blank */
1368 f++;
1370 while (*i == L' ' || *i == L'\t')
1371 i++;
1373 else
1374 /* test for literals in the format string */
1375 if (*i == *f) {
1376 i++;
1377 f++;
1379 else
1380 break;
1383 mpdm_unref(str);
1384 mpdm_unref(fmt);
1386 mpdm_unrefnd(r);
1388 return r;