langpackedit: sorting fixes, 0.015 -- from 8f06f769
[wdl.git] / WDL / wdlcstring.h
blob46ceee45be8b86b9b1d2d41192a526808565a1ad
1 /*
2 WDL - wdlcstring.h
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_
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <ctype.h>
33 #include "wdltypes.h"
34 #include "utf8_extended.h"
36 #ifdef _WDL_CSTRING_IMPL_ONLY_
37 #ifdef _WDL_CSTRING_IF_ONLY_
38 #undef _WDL_CSTRING_IF_ONLY_
39 #endif
40 #define _WDL_CSTRING_PREFIX
41 #else
42 #define _WDL_CSTRING_PREFIX static WDL_STATICFUNC_UNUSED
43 #endif
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.
50 #ifdef snprintf
51 #undef snprintf
52 #endif
53 #define snprintf WDL_snprintf
55 #ifdef vsnprintf
56 #undef vsnprintf
57 #endif
58 #define vsnprintf WDL_vsnprintf
60 #endif // win32 snprintf/vsnprintf
62 // use wdlcstring.h's lstrcpyn_safe rather than the real lstrcpyn.
63 #ifdef _WIN32
64 #ifdef lstrcpyn
65 #undef lstrcpyn
66 #endif
67 #define lstrcpyn lstrcpyn_safe
68 #endif
70 #ifdef __cplusplus
71 extern "C" {
72 #endif
74 #ifndef WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT
75 #define WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT 1
76 #endif
77 #ifndef WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT
78 #define WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT 2
79 #endif
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, ...);
103 #endif
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);
108 #else
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)
115 if (count>0)
117 int rv;
118 o[0]=0;
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, ...)
125 if (count>0)
127 int rv;
128 va_list va;
129 va_start(va,format);
130 o[0]=0;
131 rv=_vsnprintf(o,count,format,va); // returns -1 if over, and does not null terminate, ugh
132 va_end(va);
134 if (rv < 0 || rv>=(int)count-1) o[count-1]=0;
137 #endif
139 _WDL_CSTRING_PREFIX void lstrcpyn_safe(char *o, const char *in, INT_PTR count)
141 if (count>0)
143 while (--count>0 && *in) *o++ = *in++;
144 *o=0;
148 _WDL_CSTRING_PREFIX void lstrcatn(char *o, const char *in, INT_PTR count)
150 if (count>0)
152 while (*o) { if (--count < 1) return; o++; }
153 while (--count>0 && *in) *o++ = *in++;
154 *o=0;
158 _WDL_CSTRING_PREFIX const char *WDL_get_filepart(const char *str) // returns whole string if no dir chars
160 const char *p = str;
161 while (*p) p++;
162 while (p >= str && !WDL_IS_DIRCHAR(*p)) --p;
163 return p + 1;
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;
168 while (*p) p++;
169 ep = p;
170 while (p >= str && !WDL_IS_DIRCHAR(*p))
172 if (*p == '.') return p;
173 --p;
175 return ep;
178 _WDL_CSTRING_PREFIX char *WDL_remove_fileext(char *str) // returns pointer to "ext" if ".ext" was removed (zero-d dot), or NULL
180 char *p=str;
181 while (*p) p++;
182 while (p >= str && !WDL_IS_DIRCHAR(*p))
184 if (*p == '.')
186 *p = 0;
187 return p+1;
189 --p;
191 return NULL;
194 _WDL_CSTRING_PREFIX char WDL_remove_filepart(char *str) // returns dir character that was zeroed, or 0 if new string is empty
196 char *p=str;
197 while (*p) p++;
198 while (p >= str)
200 char c = *p;
201 if (WDL_IS_DIRCHAR(c))
203 *p = 0;
204 return c;
206 --p;
208 str[0] = 0;
209 return 0;
212 _WDL_CSTRING_PREFIX int WDL_remove_trailing_dirchars(char *str) // returns trailing dirchar count removed
214 int cnt = 0;
215 char *p=str;
216 while (*p) p++;
217 while (p > str+1 && WDL_IS_DIRCHAR(p[-1]))
219 cnt++;
220 p--;
222 *p = 0;
223 return cnt;
226 _WDL_CSTRING_PREFIX size_t WDL_remove_trailing_crlf(char *str) // returns new length
228 char *p=str;
229 while (*p) p++;
230 while (p > str && (p[-1] == '\r' || p[-1] == '\n')) p--;
231 *p = 0;
232 return p-str;
235 _WDL_CSTRING_PREFIX size_t WDL_remove_trailing_whitespace(char *str) // returns new length
237 char *p=str;
238 while (*p) p++;
239 while (p > str && (p[-1] == '\r' || p[-1] == '\n' || p[-1] == ' '|| p[-1] == '\t')) p--;
240 *p = 0;
241 return p-str;
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;
249 while (*end) end++;
250 decimal = end;
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;
254 if (last_z < end)
256 *last_z=0;
257 if (!str[0] || ((str[0] == '.' || str[0] == ',') && !str[1]))
259 str[0]='0';
260 str[1]=0;
261 return str+1;
264 return decimal;
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++;
270 return 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 _.
276 char *w=buf;
277 lstrcpyn_safe(buf,WDL_sanitize_ini_key_start(p),bufsz);
278 while (*w)
280 if (*w == '=' || ((extra_filters&1) && (*w=='[' || *w == ']'))) *w = '_';
281 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 = '_';
285 return buf;
288 _WDL_CSTRING_PREFIX void WDL_VARARG_WARN(printf,3,4) snprintf_append(char *o, INT_PTR count, const char *format, ...)
290 if (count>0)
292 va_list va;
293 while (*o) { if (--count < 1) return; o++; }
294 va_start(va,format);
295 vsnprintf(o,count,format,va);
296 va_end(va);
300 _WDL_CSTRING_PREFIX void vsnprintf_append(char *o, INT_PTR count, const char *format, va_list va)
302 if (count>0)
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
312 if (ch<0)
314 if (!(flags & WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT)) return ch + 384;
315 for (;;)
317 int skip;
318 ch = **ptr;
319 skip = WDL_IS_UTF8_SKIPPABLE(ch, (*ptr)[1]);
320 if (!skip) break;
321 *ptr += skip;
323 if (ch == 0xc3)
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
360 if (!ch) return 0;
362 *ptr += 1;
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);
367 return ch;
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)
373 for (;;)
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;
387 while (len1--)
389 const int d = *s1++ - *s2++;
390 if (d) return d;
393 if (lzdiff) return lzdiff;
395 else
397 int c1 = *s1, c2 = *s2;
398 if (c1 != c2)
400 if (flags & WDL_STRCMP_LOGICAL_EX_FLAG_OLDSORT)
402 if (!case_sensitive)
404 if (c1>='a' && c1<='z') c1+='A'-'a';
405 if (c2>='a' && c2<='z') c2+='A'-'a';
408 else
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;
417 s1++;
418 s2++;
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);
430 while (*a)
432 if (!strnicmp(a, b, blen)) return a;
433 a++;
435 return NULL;
439 #endif
442 #ifdef __cplusplus
444 #endif
446 #undef _WDL_CSTRING_PREFIX
448 #endif