Fixed mp-doccer detection broken in previous patch.
[mpdm.git] / mpdm_s.c
blob97ece82ceb45e869bacc4aa5c51093faca7fb587
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2009 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 /*******************
47 Data
48 ********************/
50 /*******************
51 Code
52 ********************/
54 void *mpdm_poke(void *dst, int *dsize, const void *org, int osize, int esize)
55 /* pokes (adds) org into dst, which is a dynamic string, making it grow */
57 if (org != NULL && osize) {
58 /* makes room for the new string */
59 if ((dst = realloc(dst, (*dsize + osize) * esize)) != NULL) {
60 /* copies it */
61 memcpy((char *)dst + (*dsize * esize), org, osize * esize);
63 /* adds to final size */
64 *dsize += osize;
68 return dst;
72 wchar_t *mpdm_pokews(wchar_t *dst, int *dsize, const wchar_t *str)
73 /* adds a wide string to dst using mpdm_poke() */
75 return mpdm_poke(dst, dsize, str, wcslen(str), sizeof(wchar_t));
79 wchar_t *mpdm_pokev(wchar_t * dst, int *dsize, const mpdm_t v)
80 /* adds the string in v to dst using mpdm_poke() */
82 if (v != NULL) {
83 const wchar_t *ptr = mpdm_string(v);
85 dst = mpdm_pokews(dst, dsize, ptr);
88 return dst;
92 wchar_t *mpdm_mbstowcs(const char *str, int *s, int l)
93 /* converts an mbs to a wcs, but filling invalid chars
94 with question marks instead of just failing */
96 wchar_t *ptr = NULL;
97 char tmp[64]; /* really MB_CUR_MAX + 1 */
98 wchar_t wc;
99 int n, i, c, t = 0;
100 char *cstr;
102 /* allow NULL values for s */
103 if (s == NULL)
104 s = &t;
106 /* if there is a limit, duplicate and break the string */
107 if (l >= 0) {
108 cstr = strdup(str);
109 cstr[l] = '\0';
111 else
112 cstr = (char *) str;
114 /* try first a direct conversion with mbstowcs */
115 if ((*s = mbstowcs(NULL, cstr, 0)) != -1) {
116 /* direct conversion is possible; do it */
117 if ((ptr = malloc((*s + 1) * sizeof(wchar_t))) != NULL) {
118 mbstowcs(ptr, cstr, *s);
119 ptr[*s] = L'\0';
122 else {
123 /* zero everything */
124 *s = n = i = 0;
126 for (;;) {
127 /* no more characters to process? */
128 if ((c = cstr[n + i]) == '\0' && i == 0)
129 break;
131 tmp[i++] = c;
132 tmp[i] = '\0';
134 /* try to convert */
135 if (mbstowcs(&wc, tmp, 1) == (size_t) -1) {
136 /* can still be an incomplete multibyte char? */
137 if (c != '\0' && i <= (int) MB_CUR_MAX)
138 continue;
139 else {
140 /* too many failing bytes; skip 1 byte */
141 wc = L'?';
142 i = 1;
146 /* skip used bytes and back again */
147 n += i;
148 i = 0;
150 /* store new char */
151 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
152 break;
155 /* null terminate and count one less */
156 if (ptr != NULL) {
157 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
158 (*s)--;
162 /* free the duplicate */
163 if (cstr != str)
164 free(cstr);
166 return ptr;
170 char *mpdm_wcstombs(const wchar_t * str, int *s)
171 /* converts a wcs to an mbs, but filling invalid chars
172 with question marks instead of just failing */
174 char *ptr = NULL;
175 char tmp[64]; /* really MB_CUR_MAX + 1 */
176 int l, t = 0;
178 /* allow NULL values for s */
179 if (s == NULL)
180 s = &t;
182 /* try first a direct conversion with wcstombs */
183 if ((*s = wcstombs(NULL, str, 0)) != -1) {
184 /* direct conversion is possible; do it and return */
185 if ((ptr = malloc(*s + 1)) != NULL) {
186 wcstombs(ptr, str, *s);
187 ptr[*s] = '\0';
190 return ptr;
193 /* invalid encoding? convert characters one by one */
194 *s = 0;
196 while (*str) {
197 if ((l = wctomb(tmp, *str)) <= 0) {
198 /* if char couldn't be converted,
199 write a question mark instead */
200 l = wctomb(tmp, L'?');
203 tmp[l] = '\0';
204 if ((ptr = mpdm_poke(ptr, s, tmp, l, 1)) == NULL)
205 break;
207 str++;
210 /* null terminate and count one less */
211 if (ptr != NULL) {
212 ptr = mpdm_poke(ptr, s, "", 1, 1);
213 (*s)--;
216 return ptr;
220 mpdm_t mpdm_new_wcs(int flags, const wchar_t * str, int size, int cpy)
221 /* creates a new string value from a wcs */
223 wchar_t *ptr;
225 /* a size of -1 means 'calculate it' */
226 if (size == -1 && str != NULL)
227 size = wcslen(str);
229 /* create a copy? */
230 if (cpy) {
231 /* free() on destruction */
232 flags |= MPDM_FREE;
234 /* allocs */
235 if ((ptr = malloc((size + 1) * sizeof(wchar_t))) == NULL)
236 return NULL;
238 /* if no source, reset to zeroes; otherwise, copy */
239 if (str == NULL)
240 memset(ptr, '\0', size * sizeof(wchar_t));
241 else {
242 wcsncpy(ptr, str, size);
243 ptr[size] = L'\0';
246 else
247 ptr = (wchar_t *)str;
249 /* it's a string */
250 flags |= MPDM_STRING;
252 return mpdm_new(flags, ptr, size);
256 mpdm_t mpdm_new_mbstowcs(int flags, const char *str, int l)
257 /* creates a new string value from an mbs */
259 wchar_t *ptr;
260 int size;
262 if ((ptr = mpdm_mbstowcs(str, &size, l)) == NULL)
263 return NULL;
265 /* it's a string */
266 flags |= (MPDM_STRING | MPDM_FREE);
268 return mpdm_new(flags, ptr, size);
272 mpdm_t mpdm_new_wcstombs(int flags, const wchar_t * str)
273 /* creates a new mbs value from a wbs */
275 char *ptr;
276 int size;
278 ptr = mpdm_wcstombs(str, &size);
280 flags |= MPDM_FREE;
282 /* unset the string flag; mbs,s are not 'strings' */
283 flags &= ~MPDM_STRING;
285 return mpdm_new(flags, ptr, size);
289 mpdm_t mpdm_new_i(int ival)
290 /* creates a new string value from an integer */
292 mpdm_t v;
293 char tmp[32];
295 /* creates the visual representation */
296 snprintf(tmp, sizeof(tmp), "%d", ival);
298 v = MPDM_MBS(tmp);
300 return mpdm_set_ival(v, ival);
304 mpdm_t mpdm_new_r(double rval)
305 /* creates a new string value from a real number */
307 mpdm_t v;
308 char tmp[128];
310 /* creates the visual representation */
311 snprintf(tmp, sizeof(tmp), "%lf", rval);
313 /* manually strip useless zeroes */
314 if (strchr(tmp, '.') != NULL) {
315 char *ptr;
317 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
319 /* if it's over the ., strip it also */
320 if (*ptr != '.')
321 ptr++;
323 *ptr = '\0';
326 v = MPDM_MBS(tmp);
328 return mpdm_set_rval(v, rval);
332 /* interface */
335 * mpdm_string - Returns a printable representation of a value.
336 * @v: the value
338 * Returns a printable representation of a value. For strings, it's
339 * the value data itself; for any other type, a conversion to string
340 * is returned instead. This value should be used immediately, as it
341 * can be a pointer to a static buffer.
342 * [Strings]
344 wchar_t *mpdm_string(const mpdm_t v)
346 static wchar_t wtmp[32];
347 char tmp[32];
349 /* if it's NULL, return a constant */
350 if (v == NULL)
351 return L"[NULL]";
353 /* if it's a string, return it */
354 if (v->flags & MPDM_STRING)
355 return (wchar_t *) v->data;
357 /* otherwise, return a visual representation */
358 snprintf(tmp, sizeof(tmp), "%p", v);
359 mbstowcs(wtmp, tmp, sizeof(wtmp));
360 wtmp[(sizeof(wtmp) / sizeof(wchar_t)) - 1] = L'\0';
362 return wtmp;
367 * mpdm_cmp - Compares two values.
368 * @v1: the first value
369 * @v2: the second value
371 * Compares two values. If both has the MPDM_STRING flag set,
372 * a comparison using wcscmp() is returned; if both are arrays,
373 * the size is compared first and, if they have the same number
374 * elements, each one is compared; otherwise, a simple pointer
375 * comparison is done.
376 * [Strings]
378 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
380 int r;
382 /* same values? */
383 if (v1 == v2)
384 return 0;
386 /* is any value NULL? */
387 if (v1 == NULL)
388 return -1;
389 if (v2 == NULL)
390 return 1;
392 /* different values, but same content? (unlikely) */
393 if (v1->data == v2->data)
394 return 0;
396 if (MPDM_IS_STRING(v1) && MPDM_IS_STRING(v2))
397 r = wcscoll((wchar_t *) v1->data, (wchar_t *) v2->data);
398 else
399 if (MPDM_IS_ARRAY(v1) && MPDM_IS_ARRAY(v2)) {
400 /* compare first the sizes */
401 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
402 int n;
404 /* they have the same size;
405 compare each pair of elements */
406 for (n = 0; n < mpdm_size(v1); n++) {
407 if ((r = mpdm_cmp(mpdm_aget(v1, n),
408 mpdm_aget(v2, n))) != 0)
409 break;
413 else
414 /* in any other case, compare just pointers */
415 r = (int) ((char *)v1->data - (char *)v2->data);
417 return r;
422 * mpdm_splice - Creates a new string value from another.
423 * @v: the original value
424 * @i: the value to be inserted
425 * @offset: offset where the substring is to be inserted
426 * @del: number of characters to delete
428 * Creates a new string value from @v, deleting @del chars at @offset
429 * and substituting them by @i. If @del is 0, no deletion is done.
430 * both @offset and @del can be negative; if this is the case, it's
431 * assumed as counting from the end of @v. If @v is NULL, @i will become
432 * the new string, and both @offset and @del will be ignored. If @v is
433 * not NULL and @i is, no insertion process is done (only deletion, if
434 * applicable).
436 * Returns a two element array, with the new string in the first
437 * element and the deleted string in the second (with a NULL value
438 * if @del is 0).
439 * [Strings]
441 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i, int offset, int del)
443 mpdm_t w;
444 mpdm_t n = NULL;
445 mpdm_t d = NULL;
446 int os, ns, r;
447 int ins = 0;
448 wchar_t *ptr;
450 if (v != NULL) {
451 os = mpdm_size(v);
453 /* negative offsets start from the end */
454 if (offset < 0)
455 offset = os + 1 - offset;
457 /* never add further the end */
458 if (offset > os)
459 offset = os;
461 /* negative del counts as 'characters left' */
462 if (del < 0)
463 del = os + 1 - offset + del;
465 /* something to delete? */
466 if (del > 0) {
467 /* never delete further the end */
468 if (offset + del > os)
469 del = os - offset;
471 /* deleted string */
472 d = MPDM_NS(((wchar_t *) v->data) + offset, del);
474 else
475 del = 0;
477 /* something to insert? */
478 ins = mpdm_size(i);
480 /* new size and remainder */
481 ns = os + ins - del;
482 r = offset + del;
484 if ((n = MPDM_NS(NULL, ns)) == NULL)
485 return NULL;
487 ptr = (wchar_t *)n->data;
489 /* copy the beginning */
490 if (offset > 0) {
491 wcsncpy(ptr, v->data, offset);
492 ptr += offset;
495 /* copy the text to be inserted */
496 if (ins > 0) {
497 wcsncpy(ptr, i->data, ins);
498 ptr += ins;
501 /* copy the remaining */
502 os -= r;
503 if (os > 0) {
504 wcsncpy(ptr, ((wchar_t *) v->data) + r, os);
505 ptr += os;
508 /* null terminate */
509 *ptr = L'\0';
511 else
512 n = i;
514 /* creates the output array */
515 w = MPDM_A(2);
517 mpdm_aset(w, n, 0);
518 mpdm_aset(w, d, 1);
520 return w;
525 * mpdm_strcat - Concatenates two strings.
526 * @s1: the first string
527 * @s2: the second string
529 * Returns a new string formed by the concatenation of @s1 and @s2.
530 * [Strings]
532 mpdm_t mpdm_strcat(const mpdm_t s1, const mpdm_t s2)
534 wchar_t *ptr = NULL;
535 int s = 0;
537 if (s1 == NULL && s2 == NULL)
538 return NULL;
540 ptr = mpdm_pokev(ptr, &s, s1);
541 ptr = mpdm_pokev(ptr, &s, s2);
543 /* if no characters were added, returns an empty string */
544 if (ptr == NULL)
545 return MPDM_LS(L"");
547 ptr = mpdm_poke(ptr, &s, L"", 1, sizeof(wchar_t));
548 return MPDM_ENS(ptr, s - 1);
553 * mpdm_ival - Returns a value's data as an integer.
554 * @v: the value
556 * Returns a value's data as an integer. If the value is a string,
557 * it's converted via sscanf and returned; non-string values have all
558 * an ival of 0. The converted integer is cached, so costly string
559 * conversions are only done once. Values created with the MPDM_IVAL
560 * flag set have its ival cached from the beginning.
561 * [Strings]
562 * [Value Management]
564 int mpdm_ival(mpdm_t v)
566 if (v == NULL)
567 return 0;
569 /* if there is no cached integer, calculate it */
570 if (!(v->flags & MPDM_IVAL)) {
571 int i = 0;
573 /* if it's a string, calculate it; other
574 values will have an ival of 0 */
575 if (v->flags & MPDM_STRING) {
576 char tmp[32];
577 char *fmt = "%i";
579 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
580 tmp[sizeof(tmp) - 1] = '\0';
582 /* workaround for mingw32: as it doesn't
583 correctly parse octal and hexadecimal
584 numbers, they are tried as special cases */
585 if (tmp[0] == '0') {
586 if (tmp[1] == 'b' || tmp[1] == 'B') {
587 /* binary number */
588 fmt = NULL;
589 char *ptr = &tmp[2];
591 while (*ptr == '0' || *ptr == '1') {
592 i <<= 1;
594 if (*ptr == '1')
595 i |= 1;
597 ptr++;
600 else
601 if (tmp[1] == 'x' || tmp[1] == 'X')
602 fmt = "%x";
603 else
604 fmt = "%o";
607 if (fmt != NULL)
608 sscanf(tmp, fmt, &i);
611 mpdm_set_ival(v, i);
614 return v->ival;
619 * mpdm_rval - Returns a value's data as a real number (double).
620 * @v: the value
622 * Returns a value's data as a real number (double float). If the value
623 * is a string, it's converted via sscanf and returned; non-string values
624 * have all an rval of 0. The converted double is cached, so costly string
625 * conversions are only done once. Values created with the MPDM_RVAL
626 * flag set have its rval cached from the beginning.
627 * [Strings]
628 * [Value Management]
630 double mpdm_rval(mpdm_t v)
632 if (v == NULL)
633 return 0;
635 /* if there is no cached double, calculate it */
636 if (!(v->flags & MPDM_RVAL)) {
637 double r = 0.0;
639 /* if it's a string, calculate it; other
640 values will have an rval of 0.0 */
641 if (v->flags & MPDM_STRING) {
642 char tmp[128];
643 char *prev_locale;
645 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
646 tmp[sizeof(tmp) - 1] = '\0';
648 /* if the number starts with 0, it's
649 an octal or hexadecimal number; just
650 take the integer value and cast it */
651 if (tmp[0] == '0' && tmp[1] != '.')
652 r = (double) mpdm_ival(v);
653 else {
654 /* set locale to C for non locale-dependent
655 floating point conversion */
656 prev_locale = setlocale(LC_NUMERIC, "C");
658 /* read */
659 sscanf(tmp, "%lf", &r);
661 /* set previous locale */
662 setlocale(LC_NUMERIC, prev_locale);
666 mpdm_set_rval(v, r);
669 return v->rval;
674 * mpdm_gettext - Translates a string to the current language.
675 * @str: the string
677 * Translates the @str string to the current language.
679 * This function can still be used even if there is no real gettext
680 * support() by manually filling the __I18N__ hash.
682 * If the string is found in the current table, the translation is
683 * returned; otherwise, the same @str value is returned.
684 * [Strings]
685 * [Localization]
687 mpdm_t mpdm_gettext(const mpdm_t str)
689 mpdm_t v;
690 mpdm_t i18n = NULL;
692 /* gets the cache, if any */
693 if ((i18n = mpdm_hget_s(mpdm_root(), L"__I18N__")) == NULL)
694 return str;
696 /* try first the cache */
697 if ((v = mpdm_hget(i18n, str)) == NULL) {
698 #ifdef CONFOPT_GETTEXT
699 char *s;
701 /* convert to mbs */
702 v = MPDM_2MBS(str->data);
704 /* ask gettext for it */
705 s = gettext((char *) v->data);
707 /* create new value only if it's different */
708 if (s != v->data) {
709 v = MPDM_MBS(s);
711 /* store in the cache */
712 mpdm_hset(i18n, str, v);
714 else
715 #endif /* CONFOPT_GETTEXT */
717 v = str;
720 return v;
725 * mpdm_gettext_domain - Sets domain and data directory for translations.
726 * @dom: the domain (application name)
727 * @data: directory contaning the .mo files
729 * Sets the domain (application name) and translation data for translating
730 * strings that will be returned by mpdm_gettext().@data must point to a
731 * directory containing the .mo (compiled .po) files.
733 * If there is no gettext support, returns 0, or 1 otherwise.
734 * [Strings]
735 * [Localization]
737 int mpdm_gettext_domain(const mpdm_t dom, const mpdm_t data)
739 int ret = 0;
741 #ifdef CONFOPT_GETTEXT
743 mpdm_t dm;
744 mpdm_t dt;
746 /* convert both to mbs,s */
747 dm = MPDM_2MBS(dom->data);
748 dt = MPDM_2MBS(data->data);
750 /* bind and set domain */
751 bindtextdomain((char *) dm->data, (char *) dt->data);
752 textdomain((char *) dm->data);
754 mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
756 ret = 1;
758 #endif /* CONFOPT_GETTEXT */
760 #ifdef CONFOPT_WIN32
762 mpdm_t v;
764 if ((v = mpdm_hget_s(mpdm_root(), L"ENV")) != NULL &&
765 mpdm_hget_s(v, L"LANG") == NULL) {
766 wchar_t *wptr = L"en";
768 /* MS Windows crappy language constants... */
770 switch((GetSystemDefaultLangID() & 0x00ff)) {
771 case 0x01: wptr = L"ar"; break; /* arabic */
772 case 0x02: wptr = L"bg"; break; /* bulgarian */
773 case 0x03: wptr = L"ca"; break; /* catalan */
774 case 0x04: wptr = L"zh"; break; /* chinese */
775 case 0x05: wptr = L"cz"; break; /* czech */
776 case 0x06: wptr = L"da"; break; /* danish */
777 case 0x07: wptr = L"de"; break; /* german */
778 case 0x08: wptr = L"el"; break; /* greek */
779 case 0x09: wptr = L"en"; break; /* english */
780 case 0x0a: wptr = L"es"; break; /* spanish */
781 case 0x0b: wptr = L"fi"; break; /* finnish */
782 case 0x0c: wptr = L"fr"; break; /* french */
783 case 0x0d: wptr = L"he"; break; /* hebrew */
784 case 0x0e: wptr = L"hu"; break; /* hungarian */
785 case 0x0f: wptr = L"is"; break; /* icelandic */
786 case 0x10: wptr = L"it"; break; /* italian */
787 case 0x11: wptr = L"jp"; break; /* japanese */
788 case 0x12: wptr = L"ko"; break; /* korean */
789 case 0x13: wptr = L"nl"; break; /* dutch */
790 case 0x14: wptr = L"no"; break; /* norwegian */
791 case 0x15: wptr = L"po"; break; /* polish */
792 case 0x16: wptr = L"pt"; break; /* portuguese */
793 case 0x17: wptr = L"rm"; break; /* romansh (switzerland) */
794 case 0x18: wptr = L"ro"; break; /* romanian */
795 case 0x19: wptr = L"ru"; break; /* russian */
796 case 0x1a: wptr = L"sr"; break; /* serbian */
797 case 0x1b: wptr = L"sk"; break; /* slovak */
798 case 0x1c: wptr = L"sq"; break; /* albanian */
799 case 0x1d: wptr = L"sv"; break; /* swedish */
802 mpdm_hset_s(v, L"LANG", MPDM_S(wptr));
805 #endif /* CONFOPT_WIN32 */
807 return ret;
811 #ifdef CONFOPT_WCWIDTH
813 int wcwidth(wchar_t);
815 int mpdm_wcwidth(wchar_t c)
817 return wcwidth(c);
820 #else /* CONFOPT_WCWIDTH */
822 #include "wcwidth.c"
824 int mpdm_wcwidth(wchar_t c)
826 return mk_wcwidth(c);
829 #endif /* CONFOPT_WCWIDTH */
833 * mpdm_sprintf - Formats a sprintf()-like string.
834 * @fmt: the string format
835 * @args: an array of values
837 * Formats a string using the sprintf() format taking the values from @args.
838 * [Strings]
840 mpdm_t mpdm_sprintf(const mpdm_t fmt, const mpdm_t args)
842 const wchar_t *i = fmt->data;
843 wchar_t *o = NULL;
844 int l = 0, n = 0;
845 wchar_t c;
847 /* loop all characters */
848 while ((c = *i++) != L'\0') {
849 int m = 0;
850 wchar_t *tptr = NULL;
851 wchar_t *wptr = NULL;
853 if (c == L'%') {
854 /* format directive */
855 char t_fmt[128];
856 char tmp[1024];
857 mpdm_t v;
858 char *ptr = NULL;
860 /* transfer the % */
861 t_fmt[m++] = '%';
863 /* transform the format to mbs */
864 while (*i != L'\0' &&
865 m < (int)(sizeof(t_fmt) - MB_CUR_MAX - 1) &&
866 wcschr(L"-.0123456789", *i) != NULL)
867 m += wctomb(&t_fmt[m], *i++);
869 /* transfer the directive */
870 m += wctomb(&t_fmt[m], *i++);
872 t_fmt[m] = '\0';
874 /* by default, copies the format */
875 strcpy(tmp, t_fmt);
877 /* pick next value */
878 v = mpdm_aget(args, n++);
880 switch (t_fmt[m - 1]) {
881 case 'd':
882 case 'i':
883 case 'x':
884 case 'X':
885 case 'o':
887 /* integer value */
888 snprintf(tmp, sizeof(tmp) - 1,
889 t_fmt, mpdm_ival(v));
890 break;
892 case 'f':
894 /* float (real) value */
895 snprintf(tmp, sizeof(tmp) - 1,
896 t_fmt, mpdm_rval(v));
897 break;
899 case 's':
901 /* string value */
902 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
903 snprintf(tmp, sizeof(tmp) - 1, t_fmt, ptr);
904 free(ptr);
906 break;
908 case 'c':
910 /* char */
911 m = 1;
912 wptr = &c;
913 c = mpdm_ival(v);
914 break;
916 case '%':
918 /* percent sign */
919 m = 1;
920 wptr = &c;
921 break;
924 /* transfer */
925 if (wptr == NULL)
926 wptr = tptr = mpdm_mbstowcs(tmp, &m, -1);
928 else {
929 /* raw character */
930 m = 1;
931 wptr = &c;
934 /* transfer */
935 o = mpdm_poke(o, &l, wptr, m, sizeof(wchar_t));
937 /* free the temporary buffer, if any */
938 if (tptr != NULL)
939 free(tptr);
942 if (o == NULL)
943 return NULL;
945 /* null-terminate */
946 o = mpdm_poke(o, &l, L"", 1, sizeof(wchar_t));
948 return MPDM_ENS(o, l - 1);
953 * mpdm_ulc - Converts a string to uppercase or lowecase.
954 * @s: the string
955 * @u: convert to uppercase (1) or to lowercase (0).
957 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
958 * [Strings]
960 mpdm_t mpdm_ulc(const mpdm_t s, int u)
962 mpdm_t r = NULL;
963 wchar_t *optr;
964 int i = mpdm_size(s);
966 if ((optr = malloc((i + 1) * sizeof(wchar_t))) != NULL) {
967 wchar_t *iptr = mpdm_string(s);
968 int n;
970 for (n = 0; n < i; n++)
971 optr[n] = u ? towupper(iptr[n]) : towlower(iptr[n]);
973 optr[n] = L'\0';
974 r = MPDM_ENS(optr, i);
977 return r;
981 /* scanf working buffers */
982 #define SCANF_BUF_SIZE 1024
983 static wchar_t scanf_yset[SCANF_BUF_SIZE];
984 static wchar_t scanf_nset[SCANF_BUF_SIZE];
985 static wchar_t scanf_mark[SCANF_BUF_SIZE];
987 struct {
988 wchar_t cmd;
989 wchar_t *yset;
990 wchar_t *nset;
991 } scanf_sets[] = {
992 { L's', L"", L" \t" },
993 { L'u', L"0123456789", L"" },
994 { L'd', L"-0123456789", L"" },
995 { L'i', L"-0123456789", L"" },
996 { L'f', L"-0123456789.", L"" },
997 { L'x', L"-0123456789xabcdefABCDEF", L"" },
998 { L'\0', NULL, NULL },
1002 * mpdm_sscanf - Extracts data like sscanf().
1003 * @fmt: the string format
1004 * @str: the string to be parsed
1005 * @offset: the character offset to start scanning
1007 * Extracts data from a string using a special format pattern, very
1008 * much like the scanf() series of functions in the C library. Apart
1009 * from the standard percent-sign-commands (s, u, d, i, f, x,
1010 * n, [, with optional size and * to ignore), it implements S,
1011 * to match a string of characters upto what follows in the format
1012 * string. Also, the [ set of characters can include other % formats.
1014 * Returns an array with the extracted values. If %n is used, the
1015 * position in the scanned string is returned as the value.
1016 * [Strings]
1018 mpdm_t mpdm_sscanf(const mpdm_t fmt, const mpdm_t str, int offset)
1020 wchar_t *i = (wchar_t *)str->data;
1021 wchar_t *f = (wchar_t *)fmt->data;
1022 mpdm_t r;
1024 i += offset;
1025 r = MPDM_A(0);
1027 while (*f) {
1028 if (*f == L'%') {
1029 wchar_t *ptr = NULL;
1030 int size = 0;
1031 wchar_t cmd;
1032 int vsize = 0;
1033 int ignore = 0;
1034 int msize = 0;
1036 /* empty all buffers */
1037 scanf_yset[0] = scanf_nset[0] = scanf_mark[0] = L'\0';
1039 f++;
1041 /* an asterisk? don't return next value */
1042 if (*f == L'*') {
1043 ignore = 1;
1044 f++;
1047 /* does it have a size? */
1048 while (wcschr(L"0123456789", *f)) {
1049 vsize *= 10;
1050 vsize += *f - L'0';
1051 f++;
1054 /* if no size, set it to an arbitrary big limit */
1055 if (!vsize)
1056 vsize = 0xfffffff;
1058 /* now *f should contain a command */
1059 cmd = *f;
1060 f++;
1062 /* is it a verbatim percent sign? */
1063 if (cmd == L'%') {
1064 vsize = 1;
1065 ignore = 1;
1066 wcscpy(scanf_yset, L"%");
1068 else
1069 /* a position? */
1070 if (cmd == L'n') {
1071 vsize = 0;
1072 ignore = 1;
1073 mpdm_push(r, MPDM_I(i - (wchar_t *)str->data));
1075 else
1076 /* string upto a mark */
1077 if (cmd == L'S') {
1078 wchar_t *tmp = f;
1080 /* fill the mark upto another command */
1081 while (*tmp) {
1082 if (*tmp == L'%') {
1083 tmp++;
1085 /* is it an 'n'? ignore and go on */
1086 if (*tmp == L'n') {
1087 tmp++;
1088 continue;
1090 else
1091 if (*tmp == L'%')
1092 scanf_mark[msize++] = *tmp;
1093 else
1094 break;
1096 else
1097 scanf_mark[msize++] = *tmp;
1099 tmp++;
1102 scanf_mark[msize] = L'\0';
1104 else
1105 /* raw set */
1106 if (cmd == L'[') {
1107 int n = 0;
1108 wchar_t *set = scanf_yset;
1110 /* is it an inverse set? */
1111 if (*f == L'^') {
1112 set = scanf_nset;
1113 f++;
1116 /* first one is a ]? add it */
1117 if (*f == L']') {
1118 set[n++] = *f;
1119 f++;
1122 /* now build the set */
1123 for (; n < SCANF_BUF_SIZE - 1 && *f && *f != L']'; f++) {
1124 /* is it a range? */
1125 if (*f == L'-') {
1126 f++;
1128 /* start or end? hyphen itself */
1129 if (n == 0 || *f == L']')
1130 set[n++] = L'-';
1131 else {
1132 /* pick previous char */
1133 wchar_t c = set[n - 1];
1135 /* fill */
1136 while (n < SCANF_BUF_SIZE - 1 && c < *f)
1137 set[n++] = ++c;
1140 else
1141 /* is it another command? */
1142 if (*f == L'%') {
1143 int i;
1145 f++;
1146 for (i = 0; scanf_sets[i].cmd; i++) {
1147 if (*f == scanf_sets[i].cmd) {
1148 set[n] = L'\0';
1149 wcscat(set, scanf_sets[i].yset);
1150 n += wcslen(scanf_sets[i].yset);
1151 break;
1155 else
1156 set[n++] = *f;
1159 /* skip the ] */
1160 f++;
1162 set[n] = L'\0';
1164 else
1165 /* a standard set? */
1167 int n;
1169 for (n = 0; scanf_sets[n].cmd != L'\0'; n++) {
1170 if (cmd == scanf_sets[n].cmd) {
1171 wcscpy(scanf_yset, scanf_sets[n].yset);
1172 wcscpy(scanf_nset, scanf_sets[n].nset);
1173 break;
1178 /* now fill the dynamic string */
1179 while (vsize &&
1180 !wcschr(scanf_nset, *i) &&
1181 (scanf_yset[0] == L'\0' || wcschr(scanf_yset, *i)) &&
1182 (msize == 0 || wcsncmp(i, scanf_mark, msize) != 0)) {
1184 /* only add if not being ignored */
1185 if (!ignore)
1186 ptr = mpdm_poke(ptr, &size, i, 1, sizeof(wchar_t));
1188 i++;
1189 vsize--;
1192 if (!ignore && size) {
1193 /* null terminate and push */
1194 ptr = mpdm_poke(ptr, &size, L"", 1, sizeof(wchar_t));
1195 mpdm_push(r, MPDM_ENS(ptr, size));
1198 else
1199 if (*f == L' ' || *f == L'\t') {
1200 /* if it's a blank, sync to next non-blank */
1201 f++;
1203 while (*i == L' ' || *i == L'\t')
1204 i++;
1206 else
1207 /* test for literals in the format string */
1208 if (*i == *f) {
1209 i++;
1210 f++;
1212 else
1213 break;
1216 return r;