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
35 #ifdef CONFOPT_GETTEXT
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
) {
61 memcpy((char *)dst
+ (*dsize
* esize
), org
, osize
* esize
);
63 /* adds to final size */
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() */
76 const wchar_t *ptr
= mpdm_string(v
);
78 dst
= mpdm_poke(dst
, dsize
, ptr
, wcslen(ptr
), sizeof(wchar_t));
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 */
90 char tmp
[64]; /* really MB_CUR_MAX + 1 */
95 /* allow NULL values for s */
99 /* if there is a limit, duplicate and break the string */
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
);
116 /* zero everything */
120 /* no more characters to process? */
121 if ((c
= cstr
[n
+ i
]) == '\0' && i
== 0)
128 if (mbstowcs(&wc
, tmp
, 1) == (size_t) -1) {
129 /* can still be an incomplete multibyte char? */
130 if (c
!= '\0' && i
<= (int) MB_CUR_MAX
)
133 /* too many failing bytes; skip 1 byte */
139 /* skip used bytes and back again */
144 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
148 /* null terminate and count one less */
150 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
155 /* free the duplicate */
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 */
168 char tmp
[64]; /* really MB_CUR_MAX + 1 */
171 /* allow NULL values for s */
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
);
186 /* invalid encoding? convert characters one by one */
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
'?');
197 if ((ptr
= mpdm_poke(ptr
, s
, tmp
, l
, 1)) == NULL
)
203 /* null terminate and count one less */
205 ptr
= mpdm_poke(ptr
, s
, "", 1, 1);
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 */
218 /* a size of -1 means 'calculate it' */
219 if (size
== -1 && str
!= NULL
)
224 /* free() on destruction */
228 if ((ptr
= malloc((size
+ 1) * sizeof(wchar_t))) == NULL
)
231 /* if no source, reset to zeroes; otherwise, copy */
233 memset(ptr
, '\0', size
* sizeof(wchar_t));
235 wcsncpy(ptr
, str
, size
);
240 ptr
= (wchar_t *)str
;
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 */
255 if ((ptr
= mpdm_mbstowcs(str
, &size
, l
)) == NULL
)
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 */
271 ptr
= mpdm_wcstombs(str
, &size
);
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 */
288 /* creates the visual representation */
289 snprintf(tmp
, sizeof(tmp
), "%d", ival
);
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 */
303 /* creates the visual representation */
304 snprintf(tmp
, sizeof(tmp
), "%lf", rval
);
306 /* manually strip useless zeroes */
307 if (strchr(tmp
, '.') != NULL
) {
310 for (ptr
= tmp
+ strlen(tmp
) - 1; *ptr
== '0'; ptr
--);
312 /* if it's over the ., strip it also */
321 return mpdm_set_rval(v
, rval
);
328 * mpdm_string - Returns a printable representation of a 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.
337 wchar_t *mpdm_string(const mpdm_t v
)
339 static wchar_t wtmp
[32];
342 /* if it's NULL, return a constant */
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
) / sizeof(wchar_t)) - 1] = L
'\0';
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.
371 int mpdm_cmp(const mpdm_t v1
, const mpdm_t v2
)
379 /* is any value NULL? */
385 /* different values, but same content? (unlikely) */
386 if (v1
->data
== v2
->data
)
389 if (MPDM_IS_STRING(v1
) && MPDM_IS_STRING(v2
))
390 r
= wcscoll((wchar_t *) v1
->data
, (wchar_t *) v2
->data
);
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) {
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)
407 /* in any other case, compare just pointers */
408 r
= (int) ((char *)v1
->data
- (char *)v2
->data
);
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
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
434 mpdm_t
mpdm_splice(const mpdm_t v
, const mpdm_t i
, int offset
, int del
)
446 /* negative offsets start from the end */
448 offset
= os
+ 1 - offset
;
450 /* never add further the end */
454 /* negative del counts as 'characters left' */
456 del
= os
+ 1 - offset
+ del
;
458 /* something to delete? */
460 /* never delete further the end */
461 if (offset
+ del
> os
)
465 d
= MPDM_NS(((wchar_t *) v
->data
) + offset
, del
);
470 /* something to insert? */
473 /* new size and remainder */
477 if ((n
= MPDM_NS(NULL
, ns
)) == NULL
)
480 ptr
= (wchar_t *)n
->data
;
482 /* copy the beginning */
484 wcsncpy(ptr
, v
->data
, offset
);
488 /* copy the text to be inserted */
490 wcsncpy(ptr
, i
->data
, ins
);
494 /* copy the remaining */
497 wcsncpy(ptr
, ((wchar_t *) v
->data
) + r
, os
);
507 /* creates the output array */
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.
525 mpdm_t
mpdm_strcat(const mpdm_t s1
, const mpdm_t s2
)
530 if (s1
== NULL
&& s2
== 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 */
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.
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.
557 int mpdm_ival(mpdm_t v
)
562 /* if there is no cached integer, calculate it */
563 if (!(v
->flags
& MPDM_IVAL
)) {
566 /* if it's a string, calculate it; other
567 values will have an ival of 0 */
568 if (v
->flags
& MPDM_STRING
) {
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 */
579 if (tmp
[1] == 'x' || tmp
[1] == 'X')
585 sscanf(tmp
, fmt
, &i
);
596 * mpdm_rval - Returns a value's data as a real number (double).
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.
607 double mpdm_rval(mpdm_t v
)
612 /* if there is no cached double, calculate it */
613 if (!(v
->flags
& MPDM_RVAL
)) {
616 /* if it's a string, calculate it; other
617 values will have an rval of 0.0 */
618 if (v
->flags
& MPDM_STRING
) {
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
);
631 /* set locale to C for non locale-dependent
632 floating point conversion */
633 prev_locale
= setlocale(LC_NUMERIC
, "C");
636 sscanf(tmp
, "%lf", &r
);
638 /* set previous locale */
639 setlocale(LC_NUMERIC
, prev_locale
);
651 * mpdm_gettext - Translates a string to the current language.
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.
664 mpdm_t
mpdm_gettext(const mpdm_t str
)
669 /* gets the cache, if any */
670 if ((i18n
= mpdm_hget_s(mpdm_root(), L
"__I18N__")) == NULL
)
673 /* try first the cache */
674 if ((v
= mpdm_hget(i18n
, str
)) == NULL
) {
675 #ifdef CONFOPT_GETTEXT
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 */
688 /* store in the cache */
689 mpdm_hset(i18n
, str
, v
);
692 #endif /* CONFOPT_GETTEXT */
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.
714 int mpdm_gettext_domain(const mpdm_t dom
, const mpdm_t data
)
718 #ifdef CONFOPT_GETTEXT
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));
735 #endif /* CONFOPT_GETTEXT */
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 */
788 #ifdef CONFOPT_WCWIDTH
790 int wcwidth(wchar_t);
792 int mpdm_wcwidth(wchar_t c
)
797 #else /* CONFOPT_WCWIDTH */
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.
817 mpdm_t
mpdm_sprintf(const mpdm_t fmt
, const mpdm_t args
)
819 const wchar_t *i
= fmt
->data
;
824 /* loop all characters */
825 while ((c
= *i
++) != L
'\0') {
827 wchar_t *tptr
= NULL
;
828 wchar_t *wptr
= NULL
;
831 /* format directive */
840 /* transform the format to mbs */
841 while (*i
!= L
'\0' &&
842 m
< (int)(sizeof(t_fmt
) - MB_CUR_MAX
- 1) &&
843 wcschr(L
"-.0123456789", *i
) != NULL
)
844 m
+= wctomb(&t_fmt
[m
], *i
++);
846 /* transfer the directive */
847 m
+= wctomb(&t_fmt
[m
], *i
++);
851 /* by default, copies the format */
854 /* pick next value */
855 v
= mpdm_aget(args
, n
++);
857 switch (t_fmt
[m
- 1]) {
865 snprintf(tmp
, sizeof(tmp
) - 1,
866 t_fmt
, mpdm_ival(v
));
871 /* float (real) value */
872 snprintf(tmp
, sizeof(tmp
) - 1,
873 t_fmt
, mpdm_rval(v
));
879 ptr
= mpdm_wcstombs(mpdm_string(v
), NULL
);
880 snprintf(tmp
, sizeof(tmp
) - 1, t_fmt
, ptr
);
903 wptr
= tptr
= mpdm_mbstowcs(tmp
, &m
, -1);
912 o
= mpdm_poke(o
, &l
, wptr
, m
, sizeof(wchar_t));
914 /* free the temporary buffer, if any */
923 o
= mpdm_poke(o
, &l
, L
"", 1, sizeof(wchar_t));
925 return MPDM_ENS(o
, l
- 1);
930 * mpdm_ulc - Converts a string to uppercase or lowecase
932 * @u: convert to uppercase (1) or to lowercase (0).
934 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
937 mpdm_t
mpdm_ulc(const mpdm_t s
, int u
)
941 int i
= mpdm_size(s
);
943 if ((optr
= malloc((i
+ 1) * sizeof(wchar_t))) != NULL
) {
944 wchar_t *iptr
= mpdm_string(s
);
947 for (n
= 0; n
< i
; n
++)
948 optr
[n
] = u
? towupper(iptr
[n
]) : towlower(iptr
[n
]);
951 r
= MPDM_ENS(optr
, i
);