Improved mp_doccer detection.
[mpdm.git] / mpdm_s.c
bloba2114bfebbf3601017ac3662a1afa11b147582ef
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2007 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(dst + (*dsize * esize), org, osize * esize);
63 /* adds to final size */
64 *dsize += osize;
68 return dst;
72 wchar_t *mpdm_pokev(wchar_t * dst, int *dsize, const mpdm_t v)
73 /* adds the string in v to dst using mpdm_poke() */
75 if (v != NULL) {
76 const wchar_t *ptr = mpdm_string(v);
78 dst = mpdm_poke(dst, dsize, ptr, wcslen(ptr), sizeof(wchar_t));
81 return dst;
85 wchar_t *mpdm_mbstowcs(const char *str, int *s, int l)
86 /* converts an mbs to a wcs, but filling invalid chars
87 with question marks instead of just failing */
89 wchar_t *ptr = NULL;
90 char tmp[64]; /* really MB_CUR_MAX + 1 */
91 wchar_t wc;
92 int n, i, c, t = 0;
93 char *cstr;
95 /* allow NULL values for s */
96 if (s == NULL)
97 s = &t;
99 /* if there is a limit, duplicate and break the string */
100 if (l >= 0) {
101 cstr = strdup(str);
102 cstr[l] = '\0';
104 else
105 cstr = (char *) str;
107 /* try first a direct conversion with mbstowcs */
108 if ((*s = mbstowcs(NULL, cstr, 0)) != -1) {
109 /* direct conversion is possible; do it */
110 if ((ptr = malloc((*s + 1) * sizeof(wchar_t))) != NULL) {
111 mbstowcs(ptr, cstr, *s);
112 ptr[*s] = L'\0';
115 else {
116 /* zero everything */
117 *s = n = i = 0;
119 for (;;) {
120 /* no more characters to process? */
121 if ((c = cstr[n + i]) == '\0' && i == 0)
122 break;
124 tmp[i++] = c;
125 tmp[i] = '\0';
127 /* try to convert */
128 if (mbstowcs(&wc, tmp, 1) == -1) {
129 /* can still be an incomplete multibyte char? */
130 if (c != '\0' && i <= MB_CUR_MAX)
131 continue;
132 else {
133 /* too many failing bytes; skip 1 byte */
134 wc = L'?';
135 i = 1;
139 /* skip used bytes and back again */
140 n += i;
141 i = 0;
143 /* store new char */
144 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
145 break;
148 /* null terminate and count one less */
149 if (ptr != NULL) {
150 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
151 (*s)--;
155 /* free the duplicate */
156 if (cstr != str)
157 free(cstr);
159 return ptr;
163 char *mpdm_wcstombs(const wchar_t * str, int *s)
164 /* converts a wcs to an mbs, but filling invalid chars
165 with question marks instead of just failing */
167 char *ptr = NULL;
168 char tmp[64]; /* really MB_CUR_MAX + 1 */
169 int l, t = 0;
171 /* allow NULL values for s */
172 if (s == NULL)
173 s = &t;
175 /* try first a direct conversion with wcstombs */
176 if ((*s = wcstombs(NULL, str, 0)) != -1) {
177 /* direct conversion is possible; do it and return */
178 if ((ptr = malloc(*s + 1)) != NULL) {
179 wcstombs(ptr, str, *s);
180 ptr[*s] = '\0';
183 return ptr;
186 /* invalid encoding? convert characters one by one */
187 *s = 0;
189 while (*str) {
190 if ((l = wctomb(tmp, *str)) <= 0) {
191 /* if char couldn't be converted,
192 write a question mark instead */
193 l = wctomb(tmp, L'?');
196 tmp[l] = '\0';
197 if ((ptr = mpdm_poke(ptr, s, tmp, l, 1)) == NULL)
198 break;
200 str++;
203 /* null terminate and count one less */
204 if (ptr != NULL) {
205 ptr = mpdm_poke(ptr, s, "", 1, 1);
206 (*s)--;
209 return ptr;
213 mpdm_t mpdm_new_wcs(int flags, const wchar_t * str, int size, int cpy)
214 /* creates a new string value from a wcs */
216 wchar_t *ptr;
218 /* a size of -1 means 'calculate it' */
219 if (size == -1 && str != NULL)
220 size = wcslen(str);
222 /* create a copy? */
223 if (cpy) {
224 /* free() on destruction */
225 flags |= MPDM_FREE;
227 /* allocs */
228 if ((ptr = malloc((size + 1) * sizeof(wchar_t))) == NULL)
229 return NULL;
231 /* if no source, reset to zeroes; otherwise, copy */
232 if (str == NULL)
233 memset(ptr, '\0', size * sizeof(wchar_t));
234 else {
235 wcsncpy(ptr, str, size);
236 ptr[size] = L'\0';
239 else
240 ptr = (wchar_t *)str;
242 /* it's a string */
243 flags |= MPDM_STRING;
245 return mpdm_new(flags, ptr, size);
249 mpdm_t mpdm_new_mbstowcs(int flags, const char *str, int l)
250 /* creates a new string value from an mbs */
252 wchar_t *ptr;
253 int size;
255 if ((ptr = mpdm_mbstowcs(str, &size, l)) == NULL)
256 return NULL;
258 /* it's a string */
259 flags |= (MPDM_STRING | MPDM_FREE);
261 return mpdm_new(flags, ptr, size);
265 mpdm_t mpdm_new_wcstombs(int flags, const wchar_t * str)
266 /* creates a new mbs value from a wbs */
268 char *ptr;
269 int size;
271 ptr = mpdm_wcstombs(str, &size);
273 flags |= MPDM_FREE;
275 /* unset the string flag; mbs,s are not 'strings' */
276 flags &= ~MPDM_STRING;
278 return mpdm_new(flags, ptr, size);
282 mpdm_t mpdm_new_i(int ival)
283 /* creates a new string value from an integer */
285 mpdm_t v;
286 char tmp[32];
288 /* creates the visual representation */
289 snprintf(tmp, sizeof(tmp), "%d", ival);
291 v = MPDM_MBS(tmp);
293 return mpdm_set_ival(v, ival);
297 mpdm_t mpdm_new_r(double rval)
298 /* creates a new string value from a real number */
300 mpdm_t v;
301 char tmp[128];
303 /* creates the visual representation */
304 snprintf(tmp, sizeof(tmp), "%lf", rval);
306 /* manually strip useless zeroes */
307 if (strchr(tmp, '.') != NULL) {
308 char *ptr;
310 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
312 /* if it's over the ., strip it also */
313 if (*ptr != '.')
314 ptr++;
316 *ptr = '\0';
319 v = MPDM_MBS(tmp);
321 return mpdm_set_rval(v, rval);
325 /* interface */
328 * mpdm_string - Returns a printable representation of a value.
329 * @v: the value
331 * Returns a printable representation of a value. For strings, it's
332 * the value data itself; for any other type, a conversion to string
333 * is returned instead. This value should be used immediately, as it
334 * can be a pointer to a static buffer.
335 * [Strings]
337 wchar_t *mpdm_string(const mpdm_t v)
339 static wchar_t wtmp[32];
340 char tmp[32];
342 /* if it's NULL, return a constant */
343 if (v == NULL)
344 return L"[NULL]";
346 /* if it's a string, return it */
347 if (v->flags & MPDM_STRING)
348 return (wchar_t *) v->data;
350 /* otherwise, return a visual representation */
351 snprintf(tmp, sizeof(tmp), "%p", v);
352 mbstowcs(wtmp, tmp, sizeof(wtmp));
353 wtmp[sizeof(wtmp) - 1] = L'\0';
355 return wtmp;
360 * mpdm_cmp - Compares two values.
361 * @v1: the first value
362 * @v2: the second value
364 * Compares two values. If both has the MPDM_STRING flag set,
365 * a comparison using wcscmp() is returned; if both are arrays,
366 * the size is compared first and, if they have the same number
367 * elements, each one is compared; otherwise, a simple pointer
368 * comparison is done.
369 * [Strings]
371 int mpdm_cmp(const mpdm_t v1, const mpdm_t v2)
373 int r;
375 /* same values? */
376 if (v1 == v2)
377 return 0;
379 /* is any value NULL? */
380 if (v1 == NULL)
381 return -1;
382 if (v2 == NULL)
383 return 1;
385 /* different values, but same content? (unlikely) */
386 if (v1->data == v2->data)
387 return 0;
389 if (MPDM_IS_STRING(v1) && MPDM_IS_STRING(v2))
390 r = wcscoll((wchar_t *) v1->data, (wchar_t *) v2->data);
391 else
392 if (MPDM_IS_ARRAY(v1) && MPDM_IS_ARRAY(v2)) {
393 /* compare first the sizes */
394 if ((r = mpdm_size(v1) - mpdm_size(v2)) == 0) {
395 int n;
397 /* they have the same size;
398 compare each pair of elements */
399 for (n = 0; n < mpdm_size(v1); n++) {
400 if ((r = mpdm_cmp(mpdm_aget(v1, n),
401 mpdm_aget(v2, n))) != 0)
402 break;
406 else
407 /* in any other case, compare just pointers */
408 r = (int) (v1->data - v2->data);
410 return r;
415 * mpdm_splice - Creates a new string value from another.
416 * @v: the original value
417 * @i: the value to be inserted
418 * @offset: offset where the substring is to be inserted
419 * @del: number of characters to delete
421 * Creates a new string value from @v, deleting @del chars at @offset
422 * and substituting them by @i. If @del is 0, no deletion is done.
423 * both @offset and @del can be negative; if this is the case, it's
424 * assumed as counting from the end of @v. If @v is NULL, @i will become
425 * the new string, and both @offset and @del will be ignored. If @v is
426 * not NULL and @i is, no insertion process is done (only deletion, if
427 * applicable).
429 * Returns a two element array, with the new string in the first
430 * element and the deleted string in the second (with a NULL value
431 * if @del is 0).
432 * [Strings]
434 mpdm_t mpdm_splice(const mpdm_t v, const mpdm_t i, int offset, int del)
436 mpdm_t w;
437 mpdm_t n = NULL;
438 mpdm_t d = NULL;
439 int os, ns, r;
440 int ins = 0;
441 wchar_t *ptr;
443 if (v != NULL) {
444 os = mpdm_size(v);
446 /* negative offsets start from the end */
447 if (offset < 0)
448 offset = os + 1 - offset;
450 /* never add further the end */
451 if (offset > os)
452 offset = os;
454 /* negative del counts as 'characters left' */
455 if (del < 0)
456 del = os + 1 - offset + del;
458 /* something to delete? */
459 if (del > 0) {
460 /* never delete further the end */
461 if (offset + del > os)
462 del = os - offset;
464 /* deleted string */
465 d = MPDM_NS(((wchar_t *) v->data) + offset, del);
467 else
468 del = 0;
470 /* something to insert? */
471 ins = mpdm_size(i);
473 /* new size and remainder */
474 ns = os + ins - del;
475 r = offset + del;
477 if ((n = MPDM_NS(NULL, ns)) == NULL)
478 return NULL;
480 ptr = (wchar_t *)n->data;
482 /* copy the beginning */
483 if (offset > 0) {
484 wcsncpy(ptr, v->data, offset);
485 ptr += offset;
488 /* copy the text to be inserted */
489 if (ins > 0) {
490 wcsncpy(ptr, i->data, ins);
491 ptr += ins;
494 /* copy the remaining */
495 os -= r;
496 if (os > 0) {
497 wcsncpy(ptr, ((wchar_t *) v->data) + r, os);
498 ptr += os;
501 /* null terminate */
502 *ptr = L'\0';
504 else
505 n = i;
507 /* creates the output array */
508 w = MPDM_A(2);
510 mpdm_aset(w, n, 0);
511 mpdm_aset(w, d, 1);
513 return w;
518 * mpdm_strcat - Concatenates two strings.
519 * @s1: the first string
520 * @s2: the second string
522 * Returns a new string formed by the concatenation of @s1 and @s2.
523 * [Strings]
525 mpdm_t mpdm_strcat(const mpdm_t s1, const mpdm_t s2)
527 wchar_t *ptr = NULL;
528 int s = 0;
530 if (s1 == NULL && s2 == NULL)
531 return NULL;
533 ptr = mpdm_pokev(ptr, &s, s1);
534 ptr = mpdm_pokev(ptr, &s, s2);
536 /* if no characters were added, returns an empty string */
537 if (ptr == NULL)
538 return MPDM_LS(L"");
540 ptr = mpdm_poke(ptr, &s, L"", 1, sizeof(wchar_t));
541 return MPDM_ENS(ptr, s - 1);
546 * mpdm_ival - Returns a value's data as an integer.
547 * @v: the value
549 * Returns a value's data as an integer. If the value is a string,
550 * it's converted via sscanf and returned; non-string values have all
551 * an ival of 0. The converted integer is cached, so costly string
552 * conversions are only done once. Values created with the MPDM_IVAL
553 * flag set have its ival cached from the beginning.
554 * [Strings]
555 * [Value Management]
557 int mpdm_ival(mpdm_t v)
559 if (v == NULL)
560 return 0;
562 /* if there is no cached integer, calculate it */
563 if (!(v->flags & MPDM_IVAL)) {
564 int i = 0;
566 /* if it's a string, calculate it; other
567 values will have an ival of 0 */
568 if (v->flags & MPDM_STRING) {
569 char tmp[32];
570 char *fmt = "%i";
572 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
573 tmp[sizeof(tmp) - 1] = '\0';
575 /* workaround for mingw32: as it doesn't
576 correctly parse octal and hexadecimal
577 numbers, they are tried as special cases */
578 if (tmp[0] == '0') {
579 if (tmp[1] == 'x' || tmp[1] == 'X')
580 fmt = "%x";
581 else
582 fmt = "%o";
585 sscanf(tmp, fmt, &i);
588 mpdm_set_ival(v, i);
591 return v->ival;
596 * mpdm_rval - Returns a value's data as a real number (double).
597 * @v: the value
599 * Returns a value's data as a real number (double float). If the value
600 * is a string, it's converted via sscanf and returned; non-string values
601 * have all an rval of 0. The converted double is cached, so costly string
602 * conversions are only done once. Values created with the MPDM_RVAL
603 * flag set have its rval cached from the beginning.
604 * [Strings]
605 * [Value Management]
607 double mpdm_rval(mpdm_t v)
609 if (v == NULL)
610 return 0;
612 /* if there is no cached double, calculate it */
613 if (!(v->flags & MPDM_RVAL)) {
614 double r = 0.0;
616 /* if it's a string, calculate it; other
617 values will have an rval of 0.0 */
618 if (v->flags & MPDM_STRING) {
619 char tmp[128];
620 char *prev_locale;
622 wcstombs(tmp, (wchar_t *) v->data, sizeof(tmp));
623 tmp[sizeof(tmp) - 1] = '\0';
625 /* if the number starts with 0, it's
626 an octal or hexadecimal number; just
627 take the integer value and cast it */
628 if (tmp[0] == '0' && tmp[1] != '.')
629 r = (double) mpdm_ival(v);
630 else {
631 /* set locale to C for non locale-dependent
632 floating point conversion */
633 prev_locale = setlocale(LC_NUMERIC, "C");
635 /* read */
636 sscanf(tmp, "%lf", &r);
638 /* set previous locale */
639 setlocale(LC_NUMERIC, prev_locale);
643 mpdm_set_rval(v, r);
646 return v->rval;
651 * mpdm_gettext - Translates a string to the current language.
652 * @str: the string
654 * Translates the @str string to the current language.
656 * This function can still be used even if there is no real gettext
657 * support() by manually filling the __I18N__ hash.
659 * If the string is found in the current table, the translation is
660 * returned; otherwise, the same @str value is returned.
661 * [Strings]
662 * [Localization]
664 mpdm_t mpdm_gettext(const mpdm_t str)
666 mpdm_t v;
667 mpdm_t i18n = NULL;
669 /* gets the cache, if any */
670 if ((i18n = mpdm_hget_s(mpdm_root(), L"__I18N__")) == NULL)
671 return str;
673 /* try first the cache */
674 if ((v = mpdm_hget(i18n, str)) == NULL) {
675 #ifdef CONFOPT_GETTEXT
676 char *s;
678 /* convert to mbs */
679 v = MPDM_2MBS(str->data);
681 /* ask gettext for it */
682 s = gettext((char *) v->data);
684 /* create new value only if it's different */
685 if (s != v->data) {
686 v = MPDM_MBS(s);
688 /* store in the cache */
689 mpdm_hset(i18n, str, v);
691 else
692 #endif /* CONFOPT_GETTEXT */
694 v = str;
697 return v;
702 * mpdm_gettext_domain - Sets domain and data directory for translations.
703 * @dom: the domain (application name)
704 * @data: directory contaning the .mo files
706 * Sets the domain (application name) and translation data for translating
707 * strings that will be returned by mpdm_gettext().@data must point to a
708 * directory containing the .mo (compiled .po) files.
710 * If there is no gettext support, returns 0, or 1 otherwise.
711 * [Strings]
712 * [Localization]
714 int mpdm_gettext_domain(const mpdm_t dom, const mpdm_t data)
716 int ret = 0;
718 #ifdef CONFOPT_GETTEXT
720 mpdm_t dm;
721 mpdm_t dt;
723 /* convert both to mbs,s */
724 dm = MPDM_2MBS(dom->data);
725 dt = MPDM_2MBS(data->data);
727 /* bind and set domain */
728 bindtextdomain((char *) dm->data, (char *) dt->data);
729 textdomain((char *) dm->data);
731 mpdm_hset_s(mpdm_root(), L"__I18N__", MPDM_H(0));
733 ret = 1;
735 #endif /* CONFOPT_GETTEXT */
737 #ifdef CONFOPT_WIN32
739 mpdm_t v;
741 if ((v = mpdm_hget_s(mpdm_root(), L"ENV")) != NULL &&
742 mpdm_hget_s(v, L"LANG") == NULL) {
743 wchar_t *wptr = L"en";
745 /* MS Windows crappy language constants... */
747 switch((GetSystemDefaultLangID() & 0x00ff)) {
748 case 0x01: wptr = L"ar"; break; /* arabic */
749 case 0x02: wptr = L"bg"; break; /* bulgarian */
750 case 0x03: wptr = L"ca"; break; /* catalan */
751 case 0x04: wptr = L"zh"; break; /* chinese */
752 case 0x05: wptr = L"cz"; break; /* czech */
753 case 0x06: wptr = L"da"; break; /* danish */
754 case 0x07: wptr = L"de"; break; /* german */
755 case 0x08: wptr = L"el"; break; /* greek */
756 case 0x09: wptr = L"en"; break; /* english */
757 case 0x0a: wptr = L"es"; break; /* spanish */
758 case 0x0b: wptr = L"fi"; break; /* finnish */
759 case 0x0c: wptr = L"fr"; break; /* french */
760 case 0x0d: wptr = L"he"; break; /* hebrew */
761 case 0x0e: wptr = L"hu"; break; /* hungarian */
762 case 0x0f: wptr = L"is"; break; /* icelandic */
763 case 0x10: wptr = L"it"; break; /* italian */
764 case 0x11: wptr = L"jp"; break; /* japanese */
765 case 0x12: wptr = L"ko"; break; /* korean */
766 case 0x13: wptr = L"nl"; break; /* dutch */
767 case 0x14: wptr = L"no"; break; /* norwegian */
768 case 0x15: wptr = L"po"; break; /* polish */
769 case 0x16: wptr = L"pt"; break; /* portuguese */
770 case 0x17: wptr = L"rm"; break; /* romansh (switzerland) */
771 case 0x18: wptr = L"ro"; break; /* romanian */
772 case 0x19: wptr = L"ru"; break; /* russian */
773 case 0x1a: wptr = L"sr"; break; /* serbian */
774 case 0x1b: wptr = L"sk"; break; /* slovak */
775 case 0x1c: wptr = L"sq"; break; /* albanian */
776 case 0x1d: wptr = L"sv"; break; /* swedish */
779 mpdm_hset_s(v, L"LANG", MPDM_S(wptr));
782 #endif /* CONFOPT_WIN32 */
784 return ret;
788 #ifdef CONFOPT_WCWIDTH
790 int wcwidth(wchar_t);
792 int mpdm_wcwidth(wchar_t c)
794 return wcwidth(c);
797 #else /* CONFOPT_WCWIDTH */
799 #include "wcwidth.c"
801 int mpdm_wcwidth(wchar_t c)
803 return mk_wcwidth(c);
806 #endif /* CONFOPT_WCWIDTH */
810 * mpdm_sprintf - Formats a sprintf()-like string
811 * @fmt: the string format
812 * @args: an array of values
814 * Formats a string using the sprintf() format taking the values from @args.
815 * [Strings]
817 mpdm_t mpdm_sprintf(const mpdm_t fmt, const mpdm_t args)
819 const wchar_t *i = fmt->data;
820 wchar_t *o = NULL;
821 int l = 0, n = 0;
822 wchar_t c;
824 /* loop all characters */
825 while ((c = *i++) != L'\0') {
826 int m = 0;
827 wchar_t *tptr = NULL;
828 wchar_t *wptr = NULL;
830 if (c == L'%') {
831 /* format directive */
832 char t_fmt[128];
833 char tmp[1024];
834 mpdm_t v;
835 char *ptr = NULL;
837 /* transfer the % */
838 t_fmt[m++] = '%';
840 /* transform the format to mbs */
841 while (*i != L'\0' && m < sizeof(t_fmt) - MB_CUR_MAX - 1 &&
842 wcschr(L"-.0123456789", *i) != NULL)
843 m += wctomb(&t_fmt[m], *i++);
845 /* transfer the directive */
846 m += wctomb(&t_fmt[m], *i++);
848 t_fmt[m] = '\0';
850 /* by default, copies the format */
851 strcpy(tmp, t_fmt);
853 /* pick next value */
854 v = mpdm_aget(args, n++);
856 switch (t_fmt[m - 1]) {
857 case 'd':
858 case 'i':
859 case 'x':
860 case 'X':
861 case 'o':
863 /* integer value */
864 snprintf(tmp, sizeof(tmp) - 1,
865 t_fmt, mpdm_ival(v));
866 break;
868 case 'f':
870 /* float (real) value */
871 snprintf(tmp, sizeof(tmp) - 1,
872 t_fmt, mpdm_rval(v));
873 break;
875 case 's':
877 /* string value */
878 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
879 snprintf(tmp, sizeof(tmp) - 1, t_fmt, ptr);
880 free(ptr);
882 break;
884 case 'c':
886 /* char */
887 m = 1;
888 wptr = &c;
889 c = mpdm_ival(v);
890 break;
892 case '%':
894 /* percent sign */
895 m = 1;
896 wptr = &c;
897 break;
900 /* transfer */
901 if (wptr == NULL)
902 wptr = tptr = mpdm_mbstowcs(tmp, &m, -1);
904 else {
905 /* raw character */
906 m = 1;
907 wptr = &c;
910 /* transfer */
911 o = mpdm_poke(o, &l, wptr, m, sizeof(wchar_t));
913 /* free the temporary buffer, if any */
914 if (tptr != NULL)
915 free(tptr);
918 if (o == NULL)
919 return NULL;
921 /* null-terminate */
922 o = mpdm_poke(o, &l, L"", 1, sizeof(wchar_t));
924 return MPDM_ENS(o, l - 1);
929 * mpdm_ulc - Converts a string to uppercase or lowecase
930 * @s: the string
931 * @u: convert to uppercase (1) or to lowercase (0).
933 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
934 * [Strings]
936 mpdm_t mpdm_ulc(const mpdm_t s, int u)
938 mpdm_t r = NULL;
939 wchar_t *optr;
940 int i = mpdm_size(s);
942 if ((optr = malloc((i + 1) * sizeof(wchar_t))) != NULL) {
943 wchar_t *iptr = mpdm_string(s);
944 int n;
946 for (n = 0; n < i; n++)
947 optr[n] = u ? towupper(iptr[n]) : towlower(iptr[n]);
949 optr[n] = L'\0';
950 r = MPDM_ENS(optr, i);
953 return r;