3 Copyright (C) 2005 and later, Cockos Incorporated
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
24 C string manipulation utilities -- [v]snprintf for Win32, also snprintf_append, lstrcatn, etc
26 #ifndef _WDL_CSTRING_H_
27 #define _WDL_CSTRING_H_
34 #include "utf8_extended.h"
36 #ifdef _WDL_CSTRING_IMPL_ONLY_
37 #ifdef _WDL_CSTRING_IF_ONLY_
38 #undef _WDL_CSTRING_IF_ONLY_
40 #define _WDL_CSTRING_PREFIX
42 #define _WDL_CSTRING_PREFIX static WDL_STATICFUNC_UNUSED
47 #if defined(_WIN32) && defined(_MSC_VER)
48 // provide snprintf()/vsnprintf() for win32 -- note that these have no way of knowing
49 // what the amount written was, code should(must) be written to not depend on this.
53 #define snprintf WDL_snprintf
58 #define vsnprintf WDL_vsnprintf
60 #endif // win32 snprintf/vsnprintf
62 // use wdlcstring.h's lstrcpyn_safe rather than the real lstrcpyn.
67 #define lstrcpyn lstrcpyn_safe
74 #ifndef WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT
75 #define WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT 1
77 #ifndef WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT
78 #define WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT 2
81 #ifdef _WDL_CSTRING_IF_ONLY_
83 void lstrcpyn_safe(char *o
, const char *in
, INT_PTR count
);
84 void lstrcatn(char *o
, const char *in
, INT_PTR count
);
85 void WDL_VARARG_WARN(printf
,3,4) snprintf_append(char *o
, INT_PTR count
, const char *format
, ...);
86 void vsnprintf_append(char *o
, INT_PTR count
, const char *format
, va_list va
);
88 const char *WDL_get_filepart(const char *str
); // returns whole string if no dir chars
89 const char *WDL_get_fileext(const char *str
); // returns ".ext" or end of string "" if no extension
90 char *WDL_remove_fileext(char *str
); // returns pointer to "ext" if ".ext" was removed (zero-d dot), or NULL
91 char WDL_remove_filepart(char *str
); // returns dir character that was zeroed, or 0 if new string is empty
92 int WDL_remove_trailing_dirchars(char *str
); // returns trailing dirchar count removed, will not convert "/" into ""
93 size_t WDL_remove_trailing_crlf(char *str
); // returns new length
94 size_t WDL_remove_trailing_whitespace(char *str
); // returns new length, removes crlf space tab
95 const char *WDL_sanitize_ini_key_start(const char *p
); // used for sanitizing the start of the "key" parameter to Write/GetPrivateProfile*. note does not fully santiize
96 char *WDL_sanitize_ini_key_full(const char *p
, char *buf
, int bufsz
, int extra_filters
); // converts = and leading [ to _. removes leading/trailing whitespace. if extra_filters&1, converts all [] to _.
98 char *WDL_remove_trailing_decimal_zeros(char *str
, unsigned int keep
); // returns pointer to decimal point or end of string. removes final zeros after final decimal point only, keep=0 makes min length X, keep=1 X., keep=2 X.0, keep=3 X.00 etc, and also treats commas as decimal points
100 #if defined(_WIN32) && defined(_MSC_VER)
101 void WDL_vsnprintf(char *o
, size_t count
, const char *format
, va_list args
);
102 void WDL_VARARG_WARN(printf
,3,4) WDL_snprintf(char *o
, size_t count
, const char *format
, ...);
105 int WDL_strcmp_logical(const char *s1
, const char *s2
, int case_sensitive
);
106 int WDL_strcmp_logical_ex(const char *s1
, const char *s2
, int case_sensitive
, int flags
); // flags: WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT
107 const char *WDL_stristr(const char* a
, const char* b
);
111 #if defined(_WIN32) && defined(_MSC_VER)
113 _WDL_CSTRING_PREFIX
void WDL_vsnprintf(char *o
, size_t count
, const char *format
, va_list args
)
119 rv
=_vsnprintf(o
,count
,format
,args
); // returns -1 if over, and does not null terminate, ugh
120 if (rv
< 0 || rv
>=(int)count
-1) o
[count
-1]=0;
123 _WDL_CSTRING_PREFIX
void WDL_VARARG_WARN(printf
,3,4) WDL_snprintf(char *o
, size_t count
, const char *format
, ...)
131 rv
=_vsnprintf(o
,count
,format
,va
); // returns -1 if over, and does not null terminate, ugh
134 if (rv
< 0 || rv
>=(int)count
-1) o
[count
-1]=0;
139 _WDL_CSTRING_PREFIX
void lstrcpyn_safe(char *o
, const char *in
, INT_PTR count
)
143 while (--count
>0 && *in
) *o
++ = *in
++;
148 _WDL_CSTRING_PREFIX
void lstrcatn(char *o
, const char *in
, INT_PTR count
)
152 while (*o
) { if (--count
< 1) return; o
++; }
153 while (--count
>0 && *in
) *o
++ = *in
++;
158 _WDL_CSTRING_PREFIX
const char *WDL_get_filepart(const char *str
) // returns whole string if no dir chars
162 while (p
>= str
&& !WDL_IS_DIRCHAR(*p
)) --p
;
165 _WDL_CSTRING_PREFIX
const char *WDL_get_fileext(const char *str
) // returns ".ext" or end of string "" if no extension
167 const char *p
=str
, *ep
;
170 while (p
>= str
&& !WDL_IS_DIRCHAR(*p
))
172 if (*p
== '.') return p
;
178 _WDL_CSTRING_PREFIX
char *WDL_remove_fileext(char *str
) // returns pointer to "ext" if ".ext" was removed (zero-d dot), or NULL
182 while (p
>= str
&& !WDL_IS_DIRCHAR(*p
))
194 _WDL_CSTRING_PREFIX
char WDL_remove_filepart(char *str
) // returns dir character that was zeroed, or 0 if new string is empty
201 if (WDL_IS_DIRCHAR(c
))
212 _WDL_CSTRING_PREFIX
int WDL_remove_trailing_dirchars(char *str
) // returns trailing dirchar count removed
217 while (p
> str
+1 && WDL_IS_DIRCHAR(p
[-1]))
226 _WDL_CSTRING_PREFIX
size_t WDL_remove_trailing_crlf(char *str
) // returns new length
230 while (p
> str
&& (p
[-1] == '\r' || p
[-1] == '\n')) p
--;
235 _WDL_CSTRING_PREFIX
size_t WDL_remove_trailing_whitespace(char *str
) // returns new length
239 while (p
> str
&& (p
[-1] == '\r' || p
[-1] == '\n' || p
[-1] == ' '|| p
[-1] == '\t')) p
--;
244 _WDL_CSTRING_PREFIX
char *WDL_remove_trailing_decimal_zeros(char *str
, unsigned int keep
)
245 // returns pointer to decimal point or end of string. removes final zeros after final decimal point only, keep=0 makes min length X, keep=1 X., keep=2 X.0, keep=3 X.00 etc
246 // treats commas as decimal points
248 char *end
= str
, *decimal
, *last_z
=NULL
;
251 while (--decimal
>= str
&& *decimal
>= '0' && *decimal
<= '9') if (!last_z
&& *decimal
!= '0') last_z
= decimal
+1;
252 if (decimal
< str
|| (*decimal
!= '.' && *decimal
!= ',')) return end
;
253 if (!last_z
|| last_z
< decimal
+keep
) last_z
= decimal
+keep
;
257 if (!str
[0] || ((str
[0] == '.' || str
[0] == ',') && !str
[1]))
267 _WDL_CSTRING_PREFIX
const char *WDL_sanitize_ini_key_start(const char *p
) // used for sanitizing the beginning of "key" parameter to Write/GetPrivateProfile*. does not fully sanitize
269 while (*p
== ' ' || *p
== '\t' || *p
== '[') p
++;
273 _WDL_CSTRING_PREFIX
char *WDL_sanitize_ini_key_full(const char *p
, char *buf
, int bufsz
, int extra_filters
)
275 // converts = and leading [ to _. removes leading/trailing whitespace. if extra_filters&1, converts all [] to _.
277 lstrcpyn_safe(buf
,WDL_sanitize_ini_key_start(p
),bufsz
);
280 if (*w
== '=' || ((extra_filters
&1) && (*w
=='[' || *w
== ']'))) *w
= '_';
283 while (w
> buf
&& (w
[-1] == ' ' || w
[-1] == '\t' || w
[-1] == '\r' || w
[-1] == '\n')) *--w
= 0;
284 while (--w
>= buf
) if (*w
== '\r' || *w
== '\n' || *w
== '\t') *w
= '_';
288 _WDL_CSTRING_PREFIX
void WDL_VARARG_WARN(printf
,3,4) snprintf_append(char *o
, INT_PTR count
, const char *format
, ...)
293 while (*o
) { if (--count
< 1) return; o
++; }
295 vsnprintf(o
,count
,format
,va
);
300 _WDL_CSTRING_PREFIX
void vsnprintf_append(char *o
, INT_PTR count
, const char *format
, va_list va
)
304 while (*o
) { if (--count
< 1) return; o
++; }
305 vsnprintf(o
,count
,format
,va
);
309 static WDL_STATICFUNC_UNUSED
int logical_char_order(int ch
, int case_sensitive
, int flags
, const unsigned char **ptr
)
311 // _-<>etc, numbers, utf-8 chars, alpha chars
314 if (!(flags
& WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT
)) return ch
+ 384;
319 skip
= WDL_IS_UTF8_SKIPPABLE(ch
, (*ptr
)[1]);
325 const int ccf
= (*ptr
)[1];
326 const int cc
= ccf
& ~0x20;
327 if (WDL_IS_UTF8_BYTE2_LATIN1S_A(cc
,ccf
)) ch
= (ccf
&0x20) ? 'a' : 'A';
328 else if (WDL_IS_UTF8_BYTE2_LATIN1S_C(cc
,ccf
)) ch
= (ccf
&0x20) ? 'c' : 'C';
329 else if (WDL_IS_UTF8_BYTE2_LATIN1S_E(cc
,ccf
)) ch
= (ccf
&0x20) ? 'e' : 'E';
330 else if (WDL_IS_UTF8_BYTE2_LATIN1S_I(cc
,ccf
)) ch
= (ccf
&0x20) ? 'i' : 'I';
331 else if (WDL_IS_UTF8_BYTE2_LATIN1S_N(cc
,ccf
)) ch
= (ccf
&0x20) ? 'n' : 'N';
332 else if (WDL_IS_UTF8_BYTE2_LATIN1S_O(cc
,ccf
)) ch
= (ccf
&0x20) ? 'o' : 'O';
333 else if (WDL_IS_UTF8_BYTE2_LATIN1S_U(cc
,ccf
)) ch
= (ccf
&0x20) ? 'u' : 'U';
334 else if (WDL_IS_UTF8_BYTE2_LATIN1S_Y(cc
,ccf
)) ch
= (ccf
!=0x9d) ? 'y' : 'Y';
336 else if (ch
== 0xc4 || ch
== 0xc5)
338 const int nc
= (*ptr
)[1];
339 if (WDL_IS_UTF8_EXT1A_A(ch
,nc
)) ch
= (nc
&1) ? 'a' : 'A';
340 else if (WDL_IS_UTF8_EXT1A_C(ch
,nc
)) ch
= (nc
&1) ? 'c' : 'C';
341 else if (WDL_IS_UTF8_EXT1A_D(ch
,nc
)) ch
= (nc
&1) ? 'd' : 'D';
342 else if (WDL_IS_UTF8_EXT1A_E(ch
,nc
)) ch
= (nc
&1) ? 'e' : 'E';
343 else if (WDL_IS_UTF8_EXT1A_G(ch
,nc
)) ch
= (nc
&1) ? 'g' : 'G';
344 else if (WDL_IS_UTF8_EXT1A_H(ch
,nc
)) ch
= (nc
&1) ? 'h' : 'H';
345 else if (WDL_IS_UTF8_EXT1A_I(ch
,nc
)) ch
= (nc
&1) ? 'i' : 'I';
346 else if (WDL_IS_UTF8_EXT1A_J(ch
,nc
)) ch
= (nc
&1) ? 'j' : 'J';
347 else if (WDL_IS_UTF8_EXT1A_K(ch
,nc
)) ch
= nc
!= 0xb6 ? 'k' : 'K';
348 else if (WDL_IS_UTF8_EXT1A_L(ch
,nc
)) ch
= (nc
&1) ? 'L' : 'l';
349 else if (WDL_IS_UTF8_EXT1A_N(ch
,nc
)) ch
= (nc
< 0x89 ? (nc
&1) : !(nc
&1)) ? 'N' : 'n';
350 else if (WDL_IS_UTF8_EXT1A_O(ch
,nc
)) ch
= (nc
&1) ? 'o' : 'O';
351 else if (WDL_IS_UTF8_EXT1A_R(ch
,nc
)) ch
= (nc
&1) ? 'r' : 'R';
352 else if (WDL_IS_UTF8_EXT1A_S(ch
,nc
)) ch
= (nc
&1) ? 's' : 'S';
353 else if (WDL_IS_UTF8_EXT1A_T(ch
,nc
)) ch
= (nc
&1) ? 't' : 'T';
354 else if (WDL_IS_UTF8_EXT1A_U(ch
,nc
)) ch
= (nc
&1) ? 'u' : 'U';
355 else if (WDL_IS_UTF8_EXT1A_W(ch
,nc
)) ch
= (nc
&1) ? 'w' : 'W';
356 else if (WDL_IS_UTF8_EXT1A_Y(ch
,nc
)) ch
= (nc
&1) ? 'y' : 'Y';
357 else if (WDL_IS_UTF8_EXT1A_Z(ch
,nc
)) ch
= (nc
&1) ? 'Z' : 'z';
359 if (ch
>= 0x80) return ch
+ 128; // unknown utf-8 maps to 256..383
364 if (ch
>= '0' && ch
<= '9') return ch
+ 128; // numbers map to 128+'0' etc
365 if (ch
>= 'A' && ch
<= 'Z') return ch
+ 384; // alpha goes to 384+'A' or 384+'a' if not ignoring case
366 if (ch
>= 'a' && ch
<= 'z') return case_sensitive
? (ch
+ 384) : (ch
+ 'A' - 'a' + 384);
370 // flags: WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT
371 _WDL_CSTRING_PREFIX
int WDL_strcmp_logical_ex(const char *s1
, const char *s2
, int case_sensitive
, int flags
)
375 if (*s1
>= '0' && *s1
<= '9' && *s2
>= '0' && *s2
<= '9')
377 int lzdiff
=0, len1
=0, len2
=0;
379 while (*s1
== '0') { s1
++; lzdiff
--; }
380 while (*s2
== '0') { s2
++; lzdiff
++; }
382 while (s1
[len1
] >= '0' && s1
[len1
] <= '9') len1
++;
383 while (s2
[len2
] >= '0' && s2
[len2
] <= '9') len2
++;
385 if (len1
!= len2
) return len1
-len2
;
389 const int d
= *s1
++ - *s2
++;
393 if (lzdiff
) return lzdiff
;
397 int c1
= *s1
, c2
= *s2
;
400 if (flags
& WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT
)
404 if (c1
>='a' && c1
<='z') c1
+='A'-'a';
405 if (c2
>='a' && c2
<='z') c2
+='A'-'a';
410 c1
= logical_char_order(c1
, case_sensitive
, flags
, (const unsigned char **)&s1
);
411 c2
= logical_char_order(c2
, case_sensitive
, flags
, (const unsigned char **)&s2
);
412 if (!c1
) return c1
-c2
;
414 if (c1
!= c2
) return c1
-c2
;
416 else if (!c1
) return 0;
423 _WDL_CSTRING_PREFIX
int WDL_strcmp_logical(const char *s1
, const char *s2
, int case_sensitive
)
425 return WDL_strcmp_logical_ex(s1
,s2
,case_sensitive
,0);
427 _WDL_CSTRING_PREFIX
const char *WDL_stristr(const char* a
, const char* b
)
429 const size_t blen
= strlen(b
);
432 if (!strnicmp(a
, b
, blen
)) return a
;
446 #undef _WDL_CSTRING_PREFIX