Changed the order in mpdm_cmp() arguments inside mpdm_bseek().
[mpdm.git] / mpdm_s.c
blobedcf4c454f852c3c1e2f29735ed83e49bd5d491a
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_o(void *dst, int *dsize, int *offset, const void *org, int osize, int esize)
56 if (org != NULL && osize) {
57 /* enough room? */
58 if (*offset + osize > *dsize) {
59 /* no; enlarge */
60 *dsize += osize;
62 dst = realloc(dst, *dsize * esize);
65 memcpy((char *)dst + (*offset * esize), org, osize * esize);
66 *offset += osize;
69 return dst;
73 void *mpdm_poke(void *dst, int *dsize, const void *org, int osize, int esize)
74 /* pokes (adds) org into dst, which is a dynamic string, making it grow */
76 int offset = *dsize;
78 return mpdm_poke_o(dst, dsize, &offset, org, osize, esize);
82 wchar_t *mpdm_pokews(wchar_t *dst, int *dsize, const wchar_t *str)
83 /* adds a wide string to dst using mpdm_poke() */
85 return mpdm_poke(dst, dsize, str, wcslen(str), sizeof(wchar_t));
89 wchar_t *mpdm_pokev(wchar_t * dst, int *dsize, const mpdm_t v)
90 /* adds the string in v to dst using mpdm_poke() */
92 if (v != NULL) {
93 const wchar_t *ptr = mpdm_string(v);
95 dst = mpdm_pokews(dst, dsize, ptr);
98 return dst;
102 wchar_t *mpdm_mbstowcs(const char *str, int *s, int l)
103 /* converts an mbs to a wcs, but filling invalid chars
104 with question marks instead of just failing */
106 wchar_t *ptr = NULL;
107 char tmp[64]; /* really MB_CUR_MAX + 1 */
108 wchar_t wc;
109 int n, i, c, t = 0;
110 char *cstr;
112 /* allow NULL values for s */
113 if (s == NULL)
114 s = &t;
116 /* if there is a limit, duplicate and break the string */
117 if (l >= 0) {
118 cstr = strdup(str);
119 cstr[l] = '\0';
121 else
122 cstr = (char *) str;
124 /* try first a direct conversion with mbstowcs */
125 if ((*s = mbstowcs(NULL, cstr, 0)) != -1) {
126 /* direct conversion is possible; do it */
127 if ((ptr = malloc((*s + 1) * sizeof(wchar_t))) != NULL) {
128 mbstowcs(ptr, cstr, *s);
129 ptr[*s] = L'\0';
132 else {
133 /* zero everything */
134 *s = n = i = 0;
136 for (;;) {
137 /* no more characters to process? */
138 if ((c = cstr[n + i]) == '\0' && i == 0)
139 break;
141 tmp[i++] = c;
142 tmp[i] = '\0';
144 /* try to convert */
145 if (mbstowcs(&wc, tmp, 1) == (size_t) -1) {
146 /* can still be an incomplete multibyte char? */
147 if (c != '\0' && i <= (int) MB_CUR_MAX)
148 continue;
149 else {
150 /* too many failing bytes; skip 1 byte */
151 wc = L'?';
152 i = 1;
156 /* skip used bytes and back again */
157 n += i;
158 i = 0;
160 /* store new char */
161 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
162 break;
165 /* null terminate and count one less */
166 if (ptr != NULL) {
167 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
168 (*s)--;
172 /* free the duplicate */
173 if (cstr != str)
174 free(cstr);
176 return ptr;
180 char *mpdm_wcstombs(const wchar_t * str, int *s)
181 /* converts a wcs to an mbs, but filling invalid chars
182 with question marks instead of just failing */
184 char *ptr = NULL;
185 char tmp[64]; /* really MB_CUR_MAX + 1 */
186 int l, t = 0;
188 /* allow NULL values for s */
189 if (s == NULL)
190 s = &t;
192 /* try first a direct conversion with wcstombs */
193 if ((*s = wcstombs(NULL, str, 0)) != -1) {
194 /* direct conversion is possible; do it and return */
195 if ((ptr = malloc(*s + 1)) != NULL) {
196 wcstombs(ptr, str, *s);
197 ptr[*s] = '\0';
200 return ptr;
203 /* invalid encoding? convert characters one by one */
204 *s = 0;
206 while (*str) {
207 if ((l = wctomb(tmp, *str)) <= 0) {
208 /* if char couldn't be converted,
209 write a question mark instead */
210 l = wctomb(tmp, L'?');
213 tmp[l] = '\0';
214 if ((ptr = mpdm_poke(ptr, s, tmp, l, 1)) == NULL)
215 break;
217 str++;
220 /* null terminate and count one less */
221 if (ptr != NULL) {
222 ptr = mpdm_poke(ptr, s, "", 1, 1);
223 (*s)--;
226 return ptr;
230 mpdm_t mpdm_new_wcs(int flags, const wchar_t * str, int size, int cpy)
231 /* creates a new string value from a wcs */
233 wchar_t *ptr;
235 /* a size of -1 means 'calculate it' */
236 if (size == -1 && str != NULL)
237 size = wcslen(str);
239 /* create a copy? */
240 if (cpy) {
241 /* free() on destruction */
242 flags |= MPDM_FREE;
244 /* allocs */
245 if ((ptr = malloc((size + 1) * sizeof(wchar_t))) == NULL)
246 return NULL;
248 /* if no source, reset to zeroes; otherwise, copy */
249 if (str == NULL)
250 memset(ptr, '\0', size * sizeof(wchar_t));
251 else {
252 wcsncpy(ptr, str, size);
253 ptr[size] = L'\0';
256 else
257 ptr = (wchar_t *)str;
259 /* it's a string */
260 flags |= MPDM_STRING;
262 return mpdm_new(flags, ptr, size);
266 mpdm_t mpdm_new_mbstowcs(int flags, const char *str, int l)
267 /* creates a new string value from an mbs */
269 wchar_t *ptr;
270 int size;
272 if ((ptr = mpdm_mbstowcs(str, &size, l)) == NULL)
273 return NULL;
275 /* it's a string */
276 flags |= (MPDM_STRING | MPDM_FREE);
278 return mpdm_new(flags, ptr, size);
282 mpdm_t mpdm_new_wcstombs(int flags, const wchar_t * str)
283 /* creates a new mbs value from a wbs */
285 char *ptr;
286 int size;
288 ptr = mpdm_wcstombs(str, &size);
290 flags |= MPDM_FREE;
292 /* unset the string flag; mbs,s are not 'strings' */
293 flags &= ~MPDM_STRING;
295 return mpdm_new(flags, ptr, size);
299 mpdm_t mpdm_new_i(int ival)
300 /* creates a new string value from an integer */
302 mpdm_t v;
303 char tmp[32];
305 /* creates the visual representation */
306 snprintf(tmp, sizeof(tmp), "%d", ival);
308 v = MPDM_MBS(tmp);
310 return mpdm_set_ival(v, ival);
314 mpdm_t mpdm_new_r(double rval)
315 /* creates a new string value from a real number */
317 mpdm_t v;
318 char tmp[128];
320 /* creates the visual representation */
321 snprintf(tmp, sizeof(tmp), "%lf", rval);
323 /* manually strip useless zeroes */
324 if (strchr(tmp, '.') != NULL) {
325 char *ptr;
327 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
329 /* if it's over the ., strip it also */
330 if (*ptr != '.')
331 ptr++;
333 *ptr = '\0';
336 v = MPDM_MBS(tmp);
338 return mpdm_set_rval(v, rval);
342 /* interface */
345 * mpdm_string - Returns a printable representation of a value.
346 * @v: the value
348 * Returns a printable representation of a value. For strings, it's
349 * the value data itself; for any other type, a conversion to string
350 * is returned instead. This value should be used immediately, as it
351 * can be a pointer to a static buffer.
352 * [Strings]
354 wchar_t *mpdm_string(const mpdm_t v)
356 static wchar_t wtmp[32];
357 char tmp[32];
359 /* if it's NULL, return a constant */
360 if (v == NULL)
361 return L"[NULL]";
363 /* if it's a string, return it */
364 if (v->flags & MPDM_STRING)
365 return (wchar_t *) v->data;
367 /* otherwise, return a visual representation */
368 snprintf(tmp, sizeof(tmp), "%p", v);
369 mbstowcs(wtmp, tmp, sizeof(wtmp));
370 wtmp[(sizeof(wtmp) / sizeof(wchar_t)) - 1] = L'\0';
372 return wtmp;
377 * mpdm_cmp - Compares two values.
378 * @v1: the first value
379 * @v2: the second value
381 * Compares two values. If both has the MPDM_STRING flag set,
382 * a comparison using wcscoll() is returned; if both are arrays,
383 * the size is compared first and, if they have the same number
384 * elements, each one is compared; otherwise, a simple pointer
385 * comparison is done.
386 * [Strings]
388 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
390 int r;
392 /* same values? */
393 if (v1 == v2)
394 return 0;
396 /* is any value NULL? */
397 if (v1 == NULL)
398 return -1;
399 if (v2 == NULL)
400 return 1;
402 /* different values, but same content? (unlikely) */
403 if (v1->data == v2->data)
404 return 0;
406 if (MPDM_IS_STRING(v1) && MPDM_IS_STRING(v2))
407 r = wcscoll((wchar_t *) v1->data, (wchar_t *) v2->data);
408 else
409 if (MPDM_IS_ARRAY(v1) && MPDM_IS_ARRAY(v2)) {
410 /* compare first the sizes */
411 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
412 int n;
414 /* they have the same size;
415 compare each pair of elements */
416 for (n = 0; n < mpdm_size(v1); n++) {
417 if ((r = mpdm_cmp(mpdm_aget(v1, n),
418 mpdm_aget(v2, n))) != 0)
419 break;
423 else
424 /* in any other case, compare just pointers */
425 r = (int) ((char *)v1->data - (char *)v2->data);
427 return r;
432 * mpdm_cmp_s - Compares two values (string version).
433 * @v1: the first value
434 * @v2: the second value
436 * Compares two values. Compares both values using wcscoll()
437 * if the first one is a string, or returns 1 otherwise.
439 int mpdm_cmp_s(const mpdm_t v1, const wchar_t *v2)
441 int r = 1;
443 if (MPDM_IS_STRING(v1))
444 r = wcscoll((wchar_t *) v1->data, v2);
446 return r;
451 * mpdm_splice - Creates a new string value from another.
452 * @v: the original value
453 * @i: the value to be inserted
454 * @offset: offset where the substring is to be inserted
455 * @del: number of characters to delete
457 * Creates a new string value from @v, deleting @del chars at @offset
458 * and substituting them by @i. If @del is 0, no deletion is done.
459 * both @offset and @del can be negative; if this is the case, it's
460 * assumed as counting from the end of @v. If @v is NULL, @i will become
461 * the new string, and both @offset and @del will be ignored. If @v is
462 * not NULL and @i is, no insertion process is done (only deletion, if
463 * applicable).
465 * Returns a two element array, with the new string in the first
466 * element and the deleted string in the second (with a NULL value
467 * if @del is 0).
468 * [Strings]
470 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i, int offset, int del)
472 mpdm_t w;
473 mpdm_t n = NULL;
474 mpdm_t d = NULL;
475 int os, ns, r;
476 int ins = 0;
477 wchar_t *ptr;
479 if (v != NULL) {
480 os = mpdm_size(v);
482 /* negative offsets start from the end */
483 if (offset < 0)
484 offset = os + 1 - offset;
486 /* never add further the end */
487 if (offset > os)
488 offset = os;
490 /* negative del counts as 'characters left' */
491 if (del < 0)
492 del = os + 1 - offset + del;
494 /* something to delete? */
495 if (del > 0) {
496 /* never delete further the end */
497 if (offset + del > os)
498 del = os - offset;
500 /* deleted string */
501 d = MPDM_NS(((wchar_t *) v->data) + offset, del);
503 else
504 del = 0;
506 /* something to insert? */
507 ins = mpdm_size(i);
509 /* new size and remainder */
510 ns = os + ins - del;
511 r = offset + del;
513 if ((n = MPDM_NS(NULL, ns)) == NULL)
514 return NULL;
516 ptr = (wchar_t *)n->data;
518 /* copy the beginning */
519 if (offset > 0) {
520 wcsncpy(ptr, v->data, offset);
521 ptr += offset;
524 /* copy the text to be inserted */
525 if (ins > 0) {
526 wcsncpy(ptr, i->data, ins);
527 ptr += ins;
530 /* copy the remaining */
531 os -= r;
532 if (os > 0) {
533 wcsncpy(ptr, ((wchar_t *) v->data) + r, os);
534 ptr += os;
537 /* null terminate */
538 *ptr = L'\0';
540 else
541 n = i;
543 /* creates the output array */
544 w = MPDM_A(2);
546 mpdm_aset(w, n, 0);
547 mpdm_aset(w, d, 1);
549 return w;
554 * mpdm_strcat_s - Concatenates two strings (string version).
555 * @s1: the first string
556 * @s2: the second string
558 * Returns a new string formed by the concatenation of @s1 and @s2.
559 * [Strings]
561 mpdm_t mpdm_strcat_s(const mpdm_t s1, const wchar_t *s2)
563 wchar_t *ptr = NULL;
564 int s = 0;
566 if (s1 == NULL && s2 == NULL)
567 return NULL;
569 ptr = mpdm_pokev(ptr, &s, s1);
570 ptr = mpdm_pokews(ptr, &s, s2);
572 /* if no characters were added, returns an empty string */
573 if (ptr == NULL)
574 return MPDM_LS(L"");
576 ptr = mpdm_poke(ptr, &s, L"", 1, sizeof(wchar_t));
577 return MPDM_ENS(ptr, s - 1);
582 * mpdm_strcat - Concatenates two strings.
583 * @s1: the first string
584 * @s2: the second string
586 * Returns a new string formed by the concatenation of @s1 and @s2.
587 * [Strings]
589 mpdm_t mpdm_strcat(const mpdm_t s1, const mpdm_t s2)
591 if (s2 == NULL)
592 return s1;
594 return mpdm_strcat_s(s1, mpdm_string(s2));
599 * mpdm_ival - Returns a value's data as an integer.
600 * @v: the value
602 * Returns a value's data as an integer. If the value is a string,
603 * it's converted via sscanf and returned; non-string values have all
604 * an ival of 0. The converted integer is cached, so costly string
605 * conversions are only done once. Values created with the MPDM_IVAL
606 * flag set have its ival cached from the beginning.
607 * [Strings]
608 * [Value Management]
610 int mpdm_ival(mpdm_t v)
612 if (v == NULL)
613 return 0;
615 /* if there is no cached integer, calculate it */
616 if (!(v->flags & MPDM_IVAL)) {
617 int i = 0;
619 /* if it's a string, calculate it; other
620 values will have an ival of 0 */
621 if (v->flags & MPDM_STRING) {
622 char tmp[32];
623 char *fmt = "%i";
625 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
626 tmp[sizeof(tmp) - 1] = '\0';
628 /* workaround for mingw32: as it doesn't
629 correctly parse octal and hexadecimal
630 numbers, they are tried as special cases */
631 if (tmp[0] == '0') {
632 if (tmp[1] == 'b' || tmp[1] == 'B') {
633 /* binary number */
634 fmt = NULL;
635 char *ptr = &tmp[2];
637 while (*ptr == '0' || *ptr == '1') {
638 i <<= 1;
640 if (*ptr == '1')
641 i |= 1;
643 ptr++;
646 else
647 if (tmp[1] == 'x' || tmp[1] == 'X')
648 fmt = "%x";
649 else
650 fmt = "%o";
653 if (fmt != NULL)
654 sscanf(tmp, fmt, &i);
657 mpdm_set_ival(v, i);
660 return v->ival;
665 * mpdm_rval - Returns a value's data as a real number (double).
666 * @v: the value
668 * Returns a value's data as a real number (double float). If the value
669 * is a string, it's converted via sscanf and returned; non-string values
670 * have all an rval of 0. The converted double is cached, so costly string
671 * conversions are only done once. Values created with the MPDM_RVAL
672 * flag set have its rval cached from the beginning.
673 * [Strings]
674 * [Value Management]
676 double mpdm_rval(mpdm_t v)
678 if (v == NULL)
679 return 0;
681 /* if there is no cached double, calculate it */
682 if (!(v->flags & MPDM_RVAL)) {
683 double r = 0.0;
685 /* if it's a string, calculate it; other
686 values will have an rval of 0.0 */
687 if (v->flags & MPDM_STRING) {
688 char tmp[128];
689 char *prev_locale;
691 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
692 tmp[sizeof(tmp) - 1] = '\0';
694 /* if the number starts with 0, it's
695 an octal or hexadecimal number; just
696 take the integer value and cast it */
697 if (tmp[0] == '0' && tmp[1] != '.')
698 r = (double) mpdm_ival(v);
699 else {
700 /* set locale to C for non locale-dependent
701 floating point conversion */
702 prev_locale = setlocale(LC_NUMERIC, "C");
704 /* read */
705 sscanf(tmp, "%lf", &r);
707 /* set previous locale */
708 setlocale(LC_NUMERIC, prev_locale);
712 mpdm_set_rval(v, r);
715 return v->rval;
720 * mpdm_gettext - Translates a string to the current language.
721 * @str: the string
723 * Translates the @str string to the current language.
725 * This function can still be used even if there is no real gettext
726 * support() by manually filling the __I18N__ hash.
728 * If the string is found in the current table, the translation is
729 * returned; otherwise, the same @str value is returned.
730 * [Strings]
731 * [Localization]
733 mpdm_t mpdm_gettext(const mpdm_t str)
735 mpdm_t v;
736 mpdm_t i18n = NULL;
738 /* gets the cache, if any */
739 if ((i18n = mpdm_hget_s(mpdm_root(), L"__I18N__")) == NULL)
740 return str;
742 /* try first the cache */
743 if ((v = mpdm_hget(i18n, str)) == NULL) {
744 #ifdef CONFOPT_GETTEXT
745 char *s;
747 /* convert to mbs */
748 v = MPDM_2MBS(str->data);
750 /* ask gettext for it */
751 s = gettext((char *) v->data);
753 /* create new value only if it's different */
754 if (s != v->data) {
755 v = MPDM_MBS(s);
757 /* store in the cache */
758 mpdm_hset(i18n, str, v);
760 else
761 #endif /* CONFOPT_GETTEXT */
763 v = str;
766 return v;
771 * mpdm_gettext_domain - Sets domain and data directory for translations.
772 * @dom: the domain (application name)
773 * @data: directory contaning the .mo files
775 * Sets the domain (application name) and translation data for translating
776 * strings that will be returned by mpdm_gettext().@data must point to a
777 * directory containing the .mo (compiled .po) files.
779 * If there is no gettext support, returns 0, or 1 otherwise.
780 * [Strings]
781 * [Localization]
783 int mpdm_gettext_domain(const mpdm_t dom, const mpdm_t data)
785 int ret = 0;
787 #ifdef CONFOPT_GETTEXT
789 mpdm_t dm;
790 mpdm_t dt;
792 /* convert both to mbs,s */
793 dm = MPDM_2MBS(dom->data);
794 dt = MPDM_2MBS(data->data);
796 /* bind and set domain */
797 bindtextdomain((char *) dm->data, (char *) dt->data);
798 textdomain((char *) dm->data);
800 mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
802 ret = 1;
804 #endif /* CONFOPT_GETTEXT */
806 #ifdef CONFOPT_WIN32
808 mpdm_t v;
810 if ((v = mpdm_hget_s(mpdm_root(), L"ENV")) != NULL &&
811 mpdm_hget_s(v, L"LANG") == NULL) {
812 wchar_t *wptr = L"en";
814 /* MS Windows crappy language constants... */
816 switch((GetSystemDefaultLangID() & 0x00ff)) {
817 case 0x01: wptr = L"ar"; break; /* arabic */
818 case 0x02: wptr = L"bg"; break; /* bulgarian */
819 case 0x03: wptr = L"ca"; break; /* catalan */
820 case 0x04: wptr = L"zh"; break; /* chinese */
821 case 0x05: wptr = L"cz"; break; /* czech */
822 case 0x06: wptr = L"da"; break; /* danish */
823 case 0x07: wptr = L"de"; break; /* german */
824 case 0x08: wptr = L"el"; break; /* greek */
825 case 0x09: wptr = L"en"; break; /* english */
826 case 0x0a: wptr = L"es"; break; /* spanish */
827 case 0x0b: wptr = L"fi"; break; /* finnish */
828 case 0x0c: wptr = L"fr"; break; /* french */
829 case 0x0d: wptr = L"he"; break; /* hebrew */
830 case 0x0e: wptr = L"hu"; break; /* hungarian */
831 case 0x0f: wptr = L"is"; break; /* icelandic */
832 case 0x10: wptr = L"it"; break; /* italian */
833 case 0x11: wptr = L"jp"; break; /* japanese */
834 case 0x12: wptr = L"ko"; break; /* korean */
835 case 0x13: wptr = L"nl"; break; /* dutch */
836 case 0x14: wptr = L"no"; break; /* norwegian */
837 case 0x15: wptr = L"po"; break; /* polish */
838 case 0x16: wptr = L"pt"; break; /* portuguese */
839 case 0x17: wptr = L"rm"; break; /* romansh (switzerland) */
840 case 0x18: wptr = L"ro"; break; /* romanian */
841 case 0x19: wptr = L"ru"; break; /* russian */
842 case 0x1a: wptr = L"sr"; break; /* serbian */
843 case 0x1b: wptr = L"sk"; break; /* slovak */
844 case 0x1c: wptr = L"sq"; break; /* albanian */
845 case 0x1d: wptr = L"sv"; break; /* swedish */
848 mpdm_hset_s(v, L"LANG", MPDM_S(wptr));
851 #endif /* CONFOPT_WIN32 */
853 return ret;
857 #ifdef CONFOPT_WCWIDTH
859 int wcwidth(wchar_t);
861 int mpdm_wcwidth(wchar_t c)
863 return wcwidth(c);
866 #else /* CONFOPT_WCWIDTH */
868 #include "wcwidth.c"
870 int mpdm_wcwidth(wchar_t c)
872 return mk_wcwidth(c);
875 #endif /* CONFOPT_WCWIDTH */
879 * mpdm_sprintf - Formats a sprintf()-like string.
880 * @fmt: the string format
881 * @args: an array of values
883 * Formats a string using the sprintf() format taking the values from @args.
884 * [Strings]
886 mpdm_t mpdm_sprintf(const mpdm_t fmt, const mpdm_t args)
888 const wchar_t *i = fmt->data;
889 wchar_t *o = NULL;
890 int l = 0, n = 0;
891 wchar_t c;
893 /* loop all characters */
894 while ((c = *i++) != L'\0') {
895 int m = 0;
896 wchar_t *tptr = NULL;
897 wchar_t *wptr = NULL;
899 if (c == L'%') {
900 /* format directive */
901 char t_fmt[128];
902 char tmp[1024];
903 mpdm_t v;
904 char *ptr = NULL;
906 /* transfer the % */
907 t_fmt[m++] = '%';
909 /* transform the format to mbs */
910 while (*i != L'\0' &&
911 m < (int)(sizeof(t_fmt) - MB_CUR_MAX - 1) &&
912 wcschr(L"-.0123456789", *i) != NULL)
913 m += wctomb(&t_fmt[m], *i++);
915 /* transfer the directive */
916 m += wctomb(&t_fmt[m], *i++);
918 t_fmt[m] = '\0';
920 /* by default, copies the format */
921 strcpy(tmp, t_fmt);
923 /* pick next value */
924 v = mpdm_aget(args, n++);
926 switch (t_fmt[m - 1]) {
927 case 'd':
928 case 'i':
929 case 'u':
930 case 'x':
931 case 'X':
932 case 'o':
934 /* integer value */
935 snprintf(tmp, sizeof(tmp) - 1,
936 t_fmt, mpdm_ival(v));
937 break;
939 case 'f':
941 /* float (real) value */
942 snprintf(tmp, sizeof(tmp) - 1,
943 t_fmt, mpdm_rval(v));
944 break;
946 case 's':
948 /* string value */
949 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
950 snprintf(tmp, sizeof(tmp) - 1, t_fmt, ptr);
951 free(ptr);
953 break;
955 case 'c':
957 /* char */
958 m = 1;
959 wptr = &c;
960 c = mpdm_ival(v);
961 break;
963 case 'b':
965 ptr = tmp;
966 unsigned int mask;
967 int p = 0;
969 mask = 1 << ((sizeof(int) * 8) - 1);
970 while (mask) {
971 if (mask & (unsigned int) mpdm_ival(v)) {
972 *ptr++ = '1';
973 p = 1;
975 else
976 if (p)
977 *ptr++ = '0';
979 mask >>= 1;
982 if (ptr == tmp)
983 *ptr++ = '0';
985 *ptr = '\0';
986 break;
988 case '%':
990 /* percent sign */
991 m = 1;
992 wptr = &c;
993 break;
996 /* transfer */
997 if (wptr == NULL)
998 wptr = tptr = mpdm_mbstowcs(tmp, &m, -1);
1000 else {
1001 /* raw character */
1002 m = 1;
1003 wptr = &c;
1006 /* transfer */
1007 o = mpdm_poke(o, &l, wptr, m, sizeof(wchar_t));
1009 /* free the temporary buffer, if any */
1010 if (tptr != NULL)
1011 free(tptr);
1014 if (o == NULL)
1015 return NULL;
1017 /* null-terminate */
1018 o = mpdm_poke(o, &l, L"", 1, sizeof(wchar_t));
1020 return MPDM_ENS(o, l - 1);
1025 * mpdm_ulc - Converts a string to uppercase or lowecase.
1026 * @s: the string
1027 * @u: convert to uppercase (1) or to lowercase (0).
1029 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
1030 * [Strings]
1032 mpdm_t mpdm_ulc(const mpdm_t s, int u)
1034 mpdm_t r = NULL;
1035 wchar_t *optr;
1036 int i = mpdm_size(s);
1038 if ((optr = malloc((i + 1) * sizeof(wchar_t))) != NULL) {
1039 wchar_t *iptr = mpdm_string(s);
1040 int n;
1042 for (n = 0; n < i; n++)
1043 optr[n] = u ? towupper(iptr[n]) : towlower(iptr[n]);
1045 optr[n] = L'\0';
1046 r = MPDM_ENS(optr, i);
1049 return r;
1053 /* scanf working buffers */
1054 #define SCANF_BUF_SIZE 1024
1055 static wchar_t scanf_yset[SCANF_BUF_SIZE];
1056 static wchar_t scanf_nset[SCANF_BUF_SIZE];
1057 static wchar_t scanf_mark[SCANF_BUF_SIZE];
1059 struct {
1060 wchar_t cmd;
1061 wchar_t *yset;
1062 wchar_t *nset;
1063 } scanf_sets[] = {
1064 { L's', L"", L" \t" },
1065 { L'u', L"0123456789", L"" },
1066 { L'd', L"-0123456789", L"" },
1067 { L'i', L"-0123456789", L"" },
1068 { L'f', L"-0123456789.", L"" },
1069 { L'x', L"-0123456789xabcdefABCDEF", L"" },
1070 { L'\0', NULL, NULL },
1074 * mpdm_sscanf - Extracts data like sscanf().
1075 * @fmt: the string format
1076 * @str: the string to be parsed
1077 * @offset: the character offset to start scanning
1079 * Extracts data from a string using a special format pattern, very
1080 * much like the scanf() series of functions in the C library. Apart
1081 * from the standard percent-sign-commands (s, u, d, i, f, x,
1082 * n, [, with optional size and * to ignore), it implements S,
1083 * to match a string of characters upto what follows in the format
1084 * string. Also, the [ set of characters can include other % formats.
1086 * Returns an array with the extracted values. If %n is used, the
1087 * position in the scanned string is returned as the value.
1088 * [Strings]
1090 mpdm_t mpdm_sscanf(const mpdm_t fmt, const mpdm_t str, int offset)
1092 wchar_t *i = (wchar_t *)str->data;
1093 wchar_t *f = (wchar_t *)fmt->data;
1094 mpdm_t r;
1096 i += offset;
1097 r = MPDM_A(0);
1099 while (*f) {
1100 if (*f == L'%') {
1101 wchar_t *ptr = NULL;
1102 int size = 0;
1103 wchar_t cmd;
1104 int vsize = 0;
1105 int ignore = 0;
1106 int msize = 0;
1108 /* empty all buffers */
1109 scanf_yset[0] = scanf_nset[0] = scanf_mark[0] = L'\0';
1111 f++;
1113 /* an asterisk? don't return next value */
1114 if (*f == L'*') {
1115 ignore = 1;
1116 f++;
1119 /* does it have a size? */
1120 while (wcschr(L"0123456789", *f)) {
1121 vsize *= 10;
1122 vsize += *f - L'0';
1123 f++;
1126 /* if no size, set it to an arbitrary big limit */
1127 if (!vsize)
1128 vsize = 0xfffffff;
1130 /* now *f should contain a command */
1131 cmd = *f;
1132 f++;
1134 /* is it a verbatim percent sign? */
1135 if (cmd == L'%') {
1136 vsize = 1;
1137 ignore = 1;
1138 wcscpy(scanf_yset, L"%");
1140 else
1141 /* a position? */
1142 if (cmd == L'n') {
1143 vsize = 0;
1144 ignore = 1;
1145 mpdm_push(r, MPDM_I(i - (wchar_t *)str->data));
1147 else
1148 /* string upto a mark */
1149 if (cmd == L'S') {
1150 wchar_t *tmp = f;
1152 /* fill the mark upto another command */
1153 while (*tmp) {
1154 if (*tmp == L'%') {
1155 tmp++;
1157 /* is it an 'n'? ignore and go on */
1158 if (*tmp == L'n') {
1159 tmp++;
1160 continue;
1162 else
1163 if (*tmp == L'%')
1164 scanf_mark[msize++] = *tmp;
1165 else
1166 break;
1168 else
1169 scanf_mark[msize++] = *tmp;
1171 tmp++;
1174 scanf_mark[msize] = L'\0';
1176 else
1177 /* raw set */
1178 if (cmd == L'[') {
1179 int n = 0;
1180 wchar_t *set = scanf_yset;
1182 /* is it an inverse set? */
1183 if (*f == L'^') {
1184 set = scanf_nset;
1185 f++;
1188 /* first one is a ]? add it */
1189 if (*f == L']') {
1190 set[n++] = *f;
1191 f++;
1194 /* now build the set */
1195 for (; n < SCANF_BUF_SIZE - 1 && *f && *f != L']'; f++) {
1196 /* is it a range? */
1197 if (*f == L'-') {
1198 f++;
1200 /* start or end? hyphen itself */
1201 if (n == 0 || *f == L']')
1202 set[n++] = L'-';
1203 else {
1204 /* pick previous char */
1205 wchar_t c = set[n - 1];
1207 /* fill */
1208 while (n < SCANF_BUF_SIZE - 1 && c < *f)
1209 set[n++] = ++c;
1212 else
1213 /* is it another command? */
1214 if (*f == L'%') {
1215 int i;
1217 f++;
1218 for (i = 0; scanf_sets[i].cmd; i++) {
1219 if (*f == scanf_sets[i].cmd) {
1220 set[n] = L'\0';
1221 wcscat(set, scanf_sets[i].yset);
1222 n += wcslen(scanf_sets[i].yset);
1223 break;
1227 else
1228 set[n++] = *f;
1231 /* skip the ] */
1232 f++;
1234 set[n] = L'\0';
1236 else
1237 /* a standard set? */
1239 int n;
1241 for (n = 0; scanf_sets[n].cmd != L'\0'; n++) {
1242 if (cmd == scanf_sets[n].cmd) {
1243 wcscpy(scanf_yset, scanf_sets[n].yset);
1244 wcscpy(scanf_nset, scanf_sets[n].nset);
1245 break;
1250 /* now fill the dynamic string */
1251 while (vsize &&
1252 !wcschr(scanf_nset, *i) &&
1253 (scanf_yset[0] == L'\0' || wcschr(scanf_yset, *i)) &&
1254 (msize == 0 || wcsncmp(i, scanf_mark, msize) != 0)) {
1256 /* only add if not being ignored */
1257 if (!ignore)
1258 ptr = mpdm_poke(ptr, &size, i, 1, sizeof(wchar_t));
1260 i++;
1261 vsize--;
1264 if (!ignore && size) {
1265 /* null terminate and push */
1266 ptr = mpdm_poke(ptr, &size, L"", 1, sizeof(wchar_t));
1267 mpdm_push(r, MPDM_ENS(ptr, size));
1270 else
1271 if (*f == L' ' || *f == L'\t') {
1272 /* if it's a blank, sync to next non-blank */
1273 f++;
1275 while (*i == L' ' || *i == L'\t')
1276 i++;
1278 else
1279 /* test for literals in the format string */
1280 if (*i == *f) {
1281 i++;
1282 f++;
1284 else
1285 break;
1288 return r;