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
50 void * mpdm_poke(void * dst
, int * dsize
, void * org
, int osize
, int esize
)
51 /* pokes (adds) org into dst, which is a dynamic string, making it grow */
53 if(org
!= NULL
&& osize
)
55 /* makes room for the new string */
56 if((dst
= realloc(dst
, (*dsize
+ osize
) * esize
)) != NULL
)
59 memcpy(dst
+ (*dsize
* esize
), org
, osize
* esize
);
61 /* adds to final size */
70 wchar_t * mpdm_pokev(wchar_t * dst
, int * dsize
, mpdm_t v
)
71 /* adds the string in v to dst using mpdm_poke() */
75 wchar_t * ptr
= mpdm_string(v
);
77 dst
= mpdm_poke(dst
, dsize
, ptr
, wcslen(ptr
), sizeof(wchar_t));
84 wchar_t * mpdm_mbstowcs(char * str
, int * s
, int l
)
85 /* converts an mbs to a wcs, but filling invalid chars
86 with question marks instead of just failing */
89 char tmp
[64]; /* really MB_CUR_MAX + 1 */
94 /* allow NULL values for s */
97 /* if there is a limit, duplicate and break the string */
106 /* try first a direct conversion with mbstowcs */
107 if((*s
= mbstowcs(NULL
, cstr
, 0)) != -1)
109 /* direct conversion is possible; do it */
110 if((ptr
= malloc((*s
+ 1) * sizeof(wchar_t))) != NULL
)
112 mbstowcs(ptr
, cstr
, *s
);
118 /* zero everything */
123 /* no more characters to process? */
124 if((c
= cstr
[n
+ i
]) == '\0' && i
== 0)
127 tmp
[i
++] = c
; tmp
[i
] = '\0';
130 if(mbstowcs(&wc
, tmp
, 1) == -1)
132 /* can still be an incomplete multibyte char? */
133 if(c
!= '\0' && i
<= MB_CUR_MAX
)
137 /* too many failing bytes; skip 1 byte */
143 /* skip used bytes and back again */
148 if((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
152 /* null terminate and count one less */
155 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
160 /* free the duplicate */
161 if(cstr
!= str
) free(cstr
);
167 char * mpdm_wcstombs(wchar_t * str
, int * s
)
168 /* converts a wcs to an mbs, but filling invalid chars
169 with question marks instead of just failing */
172 char tmp
[64]; /* really MB_CUR_MAX + 1 */
175 /* allow NULL values for s */
176 if(s
== NULL
) s
= &t
;
178 /* try first a direct conversion with wcstombs */
179 if((*s
= wcstombs(NULL
, str
, 0)) != -1)
181 /* direct conversion is possible; do it and return */
182 if((ptr
= malloc(*s
+ 1)) != NULL
)
184 wcstombs(ptr
, str
, *s
);
191 /* invalid encoding? convert characters one by one */
196 if((l
= wctomb(tmp
, *str
)) <= 0)
198 /* if char couldn't be converted,
199 write a question mark instead */
200 l
= wctomb(tmp
, L
'?');
204 if((ptr
= mpdm_poke(ptr
, s
, tmp
, l
, 1)) == NULL
)
210 /* null terminate and count one less */
213 ptr
= mpdm_poke(ptr
, s
, "", 1, 1);
221 mpdm_t
mpdm_new_wcs(int flags
, wchar_t * str
, int size
, int cpy
)
222 /* creates a new string value from a wcs */
226 /* a size of -1 means 'calculate it' */
227 if(size
== -1 && str
!= NULL
)
233 /* free() on destruction */
237 if((ptr
= malloc((size
+ 1) * sizeof(wchar_t))) == NULL
)
240 /* if no source, reset to zeroes; otherwise, copy */
242 memset(ptr
, '\0', size
* sizeof(wchar_t));
245 wcsncpy(ptr
, str
, size
);
253 flags
|= MPDM_STRING
;
255 return(mpdm_new(flags
, str
, size
));
259 mpdm_t
mpdm_new_mbstowcs(int flags
, char * str
, int l
)
260 /* creates a new string value from an mbs */
265 if((ptr
= mpdm_mbstowcs(str
, &size
, l
)) == NULL
)
269 flags
|= (MPDM_STRING
|MPDM_FREE
);
271 return(mpdm_new(flags
, ptr
, size
));
275 mpdm_t
mpdm_new_wcstombs(int flags
, wchar_t * str
)
276 /* creates a new mbs value from a wbs */
281 ptr
= mpdm_wcstombs(str
, &size
);
285 /* unset the string flag; mbs,s are not 'strings' */
286 flags
&= ~ MPDM_STRING
;
288 return(mpdm_new(flags
, ptr
, size
));
292 mpdm_t
mpdm_new_i(int ival
)
293 /* creates a new string value from an integer */
298 /* creates the visual representation */
299 snprintf(tmp
, sizeof(tmp
), "%d", ival
);
302 v
->flags
|= MPDM_IVAL
;
309 mpdm_t
mpdm_new_r(double rval
)
310 /* creates a new string value from a real number */
315 /* creates the visual representation */
316 snprintf(tmp
, sizeof(tmp
), "%lf", rval
);
318 /* manually strip useless zeroes */
319 if(strchr(tmp
, '.') != NULL
)
323 for(ptr
= tmp
+ strlen(tmp
) - 1;*ptr
== '0';ptr
--);
325 /* if it's over the ., strip it also */
326 if(*ptr
!= '.') ptr
++;
332 v
->flags
|= MPDM_RVAL
;
342 * mpdm_string - Returns a printable representation of a value.
345 * Returns a printable representation of a value. For strings, it's
346 * the value data itself; for any other type, a conversion to string
347 * is returned instead. This value should be used immediately, as it
348 * can be a pointer to a static buffer.
351 wchar_t * mpdm_string(mpdm_t v
)
353 static wchar_t wtmp
[32];
356 /* if it's NULL, return a constant */
360 /* if it's a string, return it */
361 if(v
->flags
& MPDM_STRING
)
362 return((wchar_t *)v
->data
);
364 /* otherwise, return a visual representation */
365 snprintf(tmp
, sizeof(tmp
), "%p", v
);
366 mbstowcs(wtmp
, tmp
, sizeof(wtmp
));
367 wtmp
[sizeof(wtmp
) - 1] = L
'\0';
374 * mpdm_cmp - Compares two values.
375 * @v1: the first value
376 * @v2: the second value
378 * Compares two values. If both has the MPDM_STRING flag set,
379 * a comparison using wcscmp() is returned; if both are arrays,
380 * the size is compared first and, if they have the same number
381 * elements, each one is compared; otherwise, a simple pointer
382 * comparison is done.
385 int mpdm_cmp(mpdm_t v1
, mpdm_t v2
)
389 /* special treatment to NULL values */
395 if(MPDM_IS_STRING(v1
) && MPDM_IS_STRING(v2
))
396 r
= wcscoll((wchar_t *)v1
->data
, (wchar_t *)v2
->data
);
398 if(MPDM_IS_ARRAY(v1
) && MPDM_IS_ARRAY(v2
))
400 /* compare first the sizes */
401 if((r
= mpdm_size(v1
) - mpdm_size(v2
)) == 0)
405 /* they have the same size;
406 compare each pair of elements */
407 for(n
= 0;n
< mpdm_size(v1
);n
++)
409 if((r
= mpdm_cmp(mpdm_aget(v1
, n
),
410 mpdm_aget(v2
, n
))) != 0)
416 /* in any other case, compare just pointers */
417 r
= (int)(v1
->data
- v2
->data
);
424 * mpdm_splice - Creates a new string value from another.
425 * @v: the original value
426 * @i: the value to be inserted
427 * @offset: offset where the substring is to be inserted
428 * @del: number of characters to delete
430 * Creates a new string value from @v, deleting @del chars at @offset
431 * and substituting them by @i. If @del is 0, no deletion is done.
432 * both @offset and @del can be negative; if this is the case, it's
433 * assumed as counting from the end of @v. If @v is NULL, @i will become
434 * the new string, and both @offset and @del will be ignored. If @v is
435 * not NULL and @i is, no insertion process is done (only deletion, if
438 * Returns a two element array, with the new string in the first
439 * element and the deleted string in the second (with a NULL value
443 mpdm_t
mpdm_splice(mpdm_t v
, mpdm_t i
, int offset
, int del
)
456 /* negative offsets start from the end */
457 if(offset
< 0) offset
= os
+ 1 - offset
;
459 /* never add further the end */
460 if(offset
> os
) offset
= os
;
462 /* negative del counts as 'characters left' */
463 if(del
< 0) del
= os
+ 1 - offset
+ del
;
465 /* something to delete? */
468 /* never delete further the end */
469 if(offset
+ del
> os
) del
= os
- offset
;
472 d
= MPDM_NS(((wchar_t *) v
->data
) + offset
, del
);
477 /* something to insert? */
480 /* new size and remainder */
484 if((n
= MPDM_NS(NULL
, ns
)) == NULL
)
489 /* copy the beginning */
492 wcsncpy(ptr
, v
->data
, offset
);
496 /* copy the text to be inserted */
499 wcsncpy(ptr
, i
->data
, ins
);
503 /* copy the remaining */
507 wcsncpy(ptr
, ((wchar_t *) v
->data
) + r
, os
);
517 /* creates the output array */
528 * mpdm_strcat - Concatenates two strings.
529 * @s1: the first string
530 * @s2: the second string
532 * Returns a new string formed by the concatenation of @s1 and @s2.
535 mpdm_t
mpdm_strcat(mpdm_t s1
, mpdm_t s2
)
537 wchar_t * ptr
= NULL
;
540 if(s1
== NULL
&& s2
== NULL
)
543 ptr
= mpdm_pokev(ptr
, &s
, s1
);
544 ptr
= mpdm_pokev(ptr
, &s
, s2
);
546 /* if no characters were added, returns an empty string */
548 return(MPDM_LS(L
""));
550 ptr
= mpdm_poke(ptr
, &s
, L
"", 1, sizeof(wchar_t));
551 return(MPDM_ENS(ptr
, s
- 1));
556 * mpdm_ival - Returns a value's data as an integer.
559 * Returns a value's data as an integer. If the value is a string,
560 * it's converted via sscanf and returned; non-string values have all
561 * an ival of 0. The converted integer is cached, so costly string
562 * conversions are only done once. Values created with the MPDM_IVAL
563 * flag set have its ival cached from the beginning.
567 int mpdm_ival(mpdm_t v
)
572 /* if there is no cached integer, calculate it */
573 if(!(v
->flags
& MPDM_IVAL
))
577 /* if it's a string, calculate it; other
578 values will have an ival of 0 */
579 if(v
->flags
& MPDM_STRING
)
584 wcstombs(tmp
, (wchar_t *)v
->data
, sizeof(tmp
));
585 tmp
[sizeof(tmp
) - 1]='\0';
587 /* workaround for mingw32: as it doesn't
588 correctly parse octal and hexadecimal
589 numbers, they are tried as special cases */
592 if(tmp
[1] == 'x' || tmp
[1] == 'X')
598 sscanf(tmp
, fmt
, &i
);
602 v
->flags
|= MPDM_IVAL
;
610 * mpdm_rval - Returns a value's data as a real number (double).
613 * Returns a value's data as a real number (double float). If the value
614 * is a string, it's converted via sscanf and returned; non-string values
615 * have all an rval of 0. The converted double is cached, so costly string
616 * conversions are only done once. Values created with the MPDM_RVAL
617 * flag set have its rval cached from the beginning.
621 double mpdm_rval(mpdm_t v
)
626 /* if there is no cached double, calculate it */
627 if(!(v
->flags
& MPDM_RVAL
))
631 /* if it's a string, calculate it; other
632 values will have an rval of 0.0 */
633 if(v
->flags
& MPDM_STRING
)
638 wcstombs(tmp
, (wchar_t *)v
->data
, sizeof(tmp
));
639 tmp
[sizeof(tmp
) - 1] = '\0';
641 /* if the number starts with 0, it's
642 an octal or hexadecimal number; just
643 take the integer value and cast it */
644 if(tmp
[0] == '0' && tmp
[1] != '.')
645 r
= (double) mpdm_ival(v
);
648 /* set locale to C for non locale-dependent
649 floating point conversion */
650 prev_locale
= setlocale(LC_NUMERIC
, "C");
653 sscanf(tmp
, "%lf", &r
);
655 /* set previous locale */
656 setlocale(LC_NUMERIC
, prev_locale
);
661 v
->flags
|= MPDM_RVAL
;
669 * mpdm_gettext - Translates a string to the current language.
672 * Translates the @str string to the current language.
674 * This function can still be used even if there is no real gettext
675 * support() by manually filling the __I18N__ hash.
677 * If the string is found in the current table, the translation is
678 * returned; otherwise, the same @str value is returned.
682 mpdm_t
mpdm_gettext(mpdm_t str
)
687 /* gets the cache, if any */
688 if((i18n
= mpdm_hget_s(mpdm_root(), L
"__I18N__")) == NULL
)
691 /* try first the cache */
692 if((v
= mpdm_hget(i18n
, str
)) == NULL
)
694 #ifdef CONFOPT_GETTEXT
698 v
= MPDM_2MBS(str
->data
);
700 /* ask gettext for it */
701 s
= gettext((char *)v
->data
);
703 /* create new value only if it's different */
708 /* store in the cache */
709 mpdm_hset(i18n
, str
, v
);
713 #endif /* CONFOPT_GETTEXT */
723 * mpdm_gettext_domain - Sets domain and data directory for translations.
724 * @dom: the domain (application name)
725 * @data: directory contaning the .mo files
727 * Sets the domain (application name) and translation data for translating
728 * strings that will be returned by mpdm_gettext().@data must point to a
729 * directory containing the .mo (compiled .po) files.
731 * If there is no gettext support, returns 0, or 1 otherwise.
735 int mpdm_gettext_domain(mpdm_t dom
, mpdm_t data
)
739 #ifdef CONFOPT_GETTEXT
741 /* convert both to mbs,s */
742 dom
= MPDM_2MBS(dom
->data
);
743 data
= MPDM_2MBS(data
->data
);
745 /* bind and set domain */
746 bindtextdomain((char *)dom
->data
, (char *)data
->data
);
747 textdomain((char *)dom
->data
);
749 mpdm_hset_s(mpdm_root(), L
"__I18N__", MPDM_H(0));
753 #endif /* CONFOPT_GETTEXT */
759 #ifdef CONFOPT_WCWIDTH
761 int wcwidth(wchar_t);
763 int mpdm_wcwidth(wchar_t c
) { return(wcwidth(c
)); }
765 #else /* CONFOPT_WCWIDTH */
769 int mpdm_wcwidth(wchar_t c
) { return(mk_wcwidth(c
)); }
771 #endif /* CONFOPT_WCWIDTH */
775 * mpdm_sprintf - Formats a sprintf()-like string
776 * @fmt: the string format
777 * @args: an array of values
779 * Formats a string using the sprintf() format taking the values from @args.
782 mpdm_t
mpdm_sprintf(mpdm_t fmt
, mpdm_t args
)
784 wchar_t * i
= fmt
->data
;
789 /* loop all characters */
790 while((c
= *i
++) != L
'\0')
793 wchar_t * tptr
= NULL
;
794 wchar_t * wptr
= NULL
;
798 /* format directive */
807 /* transform the format to mbs */
808 while(*i
!= L
'\0' && m
< sizeof(t_fmt
) - MB_CUR_MAX
- 1 &&
809 wcschr(L
"-.0123456789", *i
) != NULL
)
810 m
+= wctomb(&t_fmt
[m
], *i
++);
812 /* transfer the directive */
813 m
+= wctomb(&t_fmt
[m
], *i
++);
817 /* by default, copies the format */
820 /* any values left? */
821 if(n
< mpdm_size(args
) && (v
= mpdm_aget(args
, n
++)) != NULL
)
832 snprintf(tmp
, sizeof(tmp
) - 1, t_fmt
, mpdm_ival(v
));
837 /* float (real) value */
838 snprintf(tmp
, sizeof(tmp
) - 1, t_fmt
, mpdm_rval(v
));
844 ptr
= mpdm_wcstombs(mpdm_string(v
), NULL
);
845 snprintf(tmp
, sizeof(tmp
) - 1, t_fmt
, ptr
);
862 wptr
= tptr
= mpdm_mbstowcs(tmp
, &m
, -1);
872 o
= mpdm_poke(o
, &l
, wptr
, m
, sizeof(wchar_t));
874 /* free the temporary buffer, if any */
875 if(tptr
!= NULL
) free(tptr
);
882 o
= mpdm_poke(o
, &l
, L
"", 1, sizeof(wchar_t));
884 return(MPDM_ENS(o
, l
- 1));
889 * mpdm_ulc - Converts a string to uppercase or lowecase
891 * @u: convert to uppercase (1) or to lowercase (0).
893 * Converts @s to uppercase (for @u == 1) or to lowercase (@u == 0).
896 mpdm_t
mpdm_ulc(mpdm_t s
, int u
)
900 int i
= mpdm_size(s
);
902 if((optr
= malloc((i
+ 1) * sizeof(wchar_t))) != NULL
)
904 wchar_t * iptr
= mpdm_string(s
);
908 optr
[n
] = u
? towupper(iptr
[n
]) : towlower(iptr
[n
]);
911 r
= MPDM_ENS(optr
, i
);