1 /* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info)
2 The author disclaims copyright to this source code. */
4 /* The most basic things, including string handling functions */
7 #include "str_strsafe.h"
9 /* TODO: should probably be based on MSVC version */
10 #if defined(__GNUC__) || !defined(_WIN32) || (_MSC_VER < 1400)
11 void strcpy_s(char *dst
, size_t dstLen
, const char *src
)
19 if (!dst
|| !src
|| dstLen
<= 0)
23 if (toCopy
> (dstLen
-1))
26 strncpy(dst
, src
, toCopy
);
33 /* This really is a no-op, just to silence the compiler */
36 int char_is_ws_or_zero(char c
)
49 int char_is_ws(char c
)
61 int char_is_digit(char c
)
63 if ((c
>= '0') && (c
<= '9'))
68 /* Concatenate 4 strings. Any string can be NULL.
69 Caller needs to free() memory. */
70 char *str_cat4(const char *str1
, const char *str2
, const char *str3
, const char *str4
)
80 str1_len
= strlen(str1
);
82 str2_len
= strlen(str2
);
84 str3_len
= strlen(str3
);
86 str4_len
= strlen(str4
);
88 str
= (char*)zmalloc(str1_len
+ str2_len
+ str3_len
+ str4_len
+ 1);
94 memcpy(tmp
, str1
, str1_len
);
98 memcpy(tmp
, str2
, str2_len
);
102 memcpy(tmp
, str3
, str3_len
);
106 memcpy(tmp
, str4
, str1_len
);
111 /* Concatenate 3 strings. Any string can be NULL.
112 Caller needs to free() memory. */
113 char *str_cat3(const char *str1
, const char *str2
, const char *str3
)
115 return str_cat4(str1
, str2
, str3
, NULL
);
118 /* Concatenate 2 strings. Any string can be NULL.
119 Caller needs to free() memory. */
120 char *str_cat(const char *str1
, const char *str2
)
122 return str_cat4(str1
, str2
, NULL
, NULL
);
125 char *str_dup(const char *str
)
127 return str_cat4(str
, NULL
, NULL
, NULL
);
130 char *str_dupn(const char *str
, size_t str_len_cch
)
136 copy
= (char*)malloc(str_len_cch
+1);
139 memcpy(copy
, str
, str_len_cch
);
140 copy
[str_len_cch
] = 0;
144 int str_copyn(char *dst
, size_t dst_cch_size
, const char *src
, size_t src_cch_size
)
146 char *end
= dst
+ dst_cch_size
- 1;
147 if (0 == dst_cch_size
) {
148 if (0 == src_cch_size
)
154 while ((dst
< end
) && (src_cch_size
> 0)) {
159 if (0 == src_cch_size
)
165 int str_copy(char *dst
, size_t dst_cch_size
, const char *src
)
167 char *end
= dst
+ dst_cch_size
- 1;
168 if (0 == dst_cch_size
)
171 while ((dst
< end
) && *src
) {
181 int str_eq(const char *str1
, const char *str2
)
187 if (0 == strcmp(str1
, str2
))
192 int str_ieq(const char *str1
, const char *str2
)
198 if (0 == _stricmp(str1
, str2
))
203 int str_eqn(const char *str1
, const char *str2
, int len
)
209 if (0 == strncmp(str1
, str2
, len
))
214 /* return true if 'str' starts with 'txt', case-sensitive */
215 int str_startswith(const char *str
, const char *txt
)
222 if (0 == strncmp(str
, txt
, strlen(txt
)))
227 /* return true if 'str' starts with 'txt', NOT case-sensitive */
228 int str_startswithi(const char *str
, const char *txt
)
235 if (0 == _strnicmp(str
, txt
, strlen(txt
)))
240 int str_endswith(const char *txt
, const char *end
)
248 txt_len
= strlen(txt
);
249 end_len
= strlen(end
);
250 if (end_len
> txt_len
)
252 if (str_eq(txt
+txt_len
-end_len
, end
))
257 int str_endswithi(const char *txt
, const char *end
)
265 txt_len
= strlen(txt
);
266 end_len
= strlen(end
);
267 if (end_len
> txt_len
)
269 if (str_ieq(txt
+txt_len
-end_len
, end
))
274 int str_endswith_char(const char *str
, char c
)
279 return str_endswith(str
, end
);
282 int str_empty(const char *str
)
291 /* Find character 'c' in string 'txt'.
292 Return pointer to this character or NULL if not found */
293 const char *str_find_char(const char *txt
, char c
)
303 /* split a string '*txt' at the border character 'c'. Something like python's
304 string.split() except called iteratively.
305 Returns a copy of the string (must be free()d by the caller).
306 Returns NULL to indicate there's no more items. */
307 char *str_split_iter(char **txt
, char c
)
313 tmp
= (const char*)*txt
;
317 pos
= str_find_char(tmp
, c
);
319 result
= str_dupn(tmp
, (int)(pos
-tmp
));
322 result
= str_dup(tmp
);
323 *txt
= NULL
; /* next iteration will return NULL */
328 /* Replace all posible versions (Unix, Windows, Mac) of newline character
329 with 'replace'. Returns newly allocated string with normalized newlines
331 Caller needs to free() the result */
332 char *str_normalize_newline(const char *txt
, const char *replace
)
339 size_t result_len
= 0;
341 replace_len
= strlen(replace
);
348 /* a single 0xa => Unix */
349 result_len
+= replace_len
;
350 } else if (0xd == c
) {
353 result_len
+= replace_len
;
357 /* just 0xd => Mac */
358 result_len
+= replace_len
;
367 result
= (char*)malloc(result_len
+1);
376 /* a single 0xa => Unix */
377 memcpy(tmp_out
, replace
, replace_len
);
378 tmp_out
+= replace_len
;
379 } else if (0xd == c
) {
382 memcpy(tmp_out
, replace
, replace_len
);
383 tmp_out
+= replace_len
;
387 /* just 0xd => Mac */
388 memcpy(tmp_out
, replace
, replace_len
);
389 tmp_out
+= replace_len
;
399 #define WHITE_SPACE_CHARS " \n\t\r"
401 /* Strip all 'to_strip' characters from the beginning of the string.
402 Does stripping in-place */
403 void str_strip_left(char *txt
, const char *to_strip
)
405 char *new_start
= txt
;
407 if (!txt
|| !to_strip
)
413 if (!str_contains(to_strip
, c
))
418 if (new_start
!= txt
) {
419 memmove(txt
, new_start
, strlen(new_start
)+1);
423 /* Strip white-space characters from the beginning of the string.
424 Does stripping in-place */
425 void str_strip_ws_left(char *txt
)
427 str_strip_left(txt
, WHITE_SPACE_CHARS
);
430 void str_strip_right(char *txt
, const char *to_strip
)
434 if (!txt
|| !to_strip
)
438 /* point at the last character in the string */
439 new_end
= txt
+ strlen(txt
) - 1;
442 if (!str_contains(to_strip
, c
))
448 if (str_contains(to_strip
, *new_end
))
454 void str_strip_ws_right(char *txt
)
456 str_strip_right(txt
, WHITE_SPACE_CHARS
);
459 void str_strip_both(char *txt
, const char *to_strip
)
461 str_strip_left(txt
, to_strip
);
462 str_strip_right(txt
, to_strip
);
465 void str_strip_ws_both(char *txt
)
467 str_strip_ws_left(txt
);
468 str_strip_ws_right(txt
);
472 int utf8_eq(const utf8
* str1
, const utf8
* str2
)
474 return str_eq(str1
, str2
);
477 int utf8_eqn(const utf8
* str1
, const utf8
* str2
, int len
)
479 return str_eqn(str1
, str2
, len
);
482 int utf8_copy(utf8
*dst
, int dst_size_bytes
, utf8
* src
)
484 return str_copy(dst
, dst_size_bytes
, src
);
487 utf8
*utf8_dup(const utf8
*str
)
492 utf8
*utf8_cat4(const utf8
*str1
, const utf8
*str2
, const utf8
*str3
, const utf8
*str4
)
494 return str_cat4(str1
, str2
, str3
, str4
);
497 utf8
*utf8_cat3(const utf8
*str1
, const utf8
*str2
, const utf8
*str3
)
499 return str_cat4(str1
, str2
, str3
, NULL
);
502 utf8
*utf8_cat(const utf8
*str1
, const utf8
*str2
)
504 return str_cat4(str1
, str2
, NULL
, NULL
);
507 int utf8_endswith(const utf8
*str
, const utf8
*end
)
509 return str_endswith(str
, end
);
513 #define HEX_NUMBERS "0123456789ABCDEF"
514 static void char_to_hex(unsigned char c
, char* buffer
)
516 buffer
[0] = HEX_NUMBERS
[c
/ 16];
517 buffer
[1] = HEX_NUMBERS
[c
% 16];
520 int str_contains(const char *str
, char c
)
522 const char *pos
= str_find_char(str
, c
);
528 #define CHAR_URL_DONT_ENCODE "-_.!~*'()"
530 int char_needs_url_encode(char c
)
532 if ((c
>= 'a') && (c
<= 'z'))
534 if ((c
>= 'A') && (c
<= 'Z'))
536 if ((c
>= '0') && (c
<= '9'))
538 if (str_contains(CHAR_URL_DONT_ENCODE
, c
))
543 /* url-encode 'str'. Returns NULL in case of error. Caller needs to free()
545 char *str_url_encode(const char *str
)
550 const char * tmp
= str
;
552 /* calc the size of the string after url encoding */
554 if (char_needs_url_encode(*tmp
))
563 encoded
= (char*)malloc(res_len
+1);
570 if (char_needs_url_encode(*tmp
)) {
572 char_to_hex(*tmp
, encoded
);
586 char *str_escape(const char *txt
)
592 char *str_printf(const char *format
, ...)
596 va_start(args
, format
);
597 result
= str_printf_args(format
, args
);
602 char *str_printf_args(const char *format
, va_list args
)
609 char * result
= NULL
;
612 bufCchSize
= sizeof(message
);
616 /* TODO: this only works on windows with recent C library */
617 hr
= StringCchVPrintfA(buf
, bufCchSize
, format
, args
);
620 if (STRSAFE_E_INSUFFICIENT_BUFFER
!= hr
)
622 /* any error other than buffer not big enough:
624 b) means we give up */
628 /* we have to make the buffer bigger. The algorithm used to calculate
629 the new size is arbitrary (aka. educated guess) */
630 if (buf
!= &(message
[0]))
632 if (bufCchSize
< 4*1024)
633 bufCchSize
+= bufCchSize
;
636 buf
= (char *)malloc(bufCchSize
*sizeof(char));
641 /* free the buffer if it was dynamically allocated */
642 if (buf
== &(message
[0]))
647 if (buf
!= &(message
[0]))
653 int len
= vasprintf(&buf
, format
, args
);
659 void win32_dbg_out(const char *format
, ...)
666 va_start(args
, format
);
667 written
= _vsnprintf(p
,sizeof(buf
), format
, args
);
670 OutputDebugStringA(buf
);
674 void win32_dbg_out_hex(const char *dsc
, const unsigned char *data
, int dataLen
)
676 unsigned char buf
[64+1];
677 unsigned char * curPos
;
680 if (dsc
) win32_dbg_out(dsc
); /* a bit dangerous if contains formatting codes */
683 bufCharsLeft
= sizeof(buf
)-1;
685 while (dataLen
> 0) {
686 if (bufCharsLeft
<= 1) {
688 win32_dbg_out((char*)buf
);
689 bufCharsLeft
= sizeof(buf
)-1;
692 char_to_hex(*data
, curPos
);
707 /* Given a pointer to a string in '*txt', skip past whitespace in the string
708 and put the result in '*txt' */
709 void str_skip_ws(char **txtInOut
)
717 while (char_is_ws(*cur
)) {
723 char *str_parse_quoted(char **txt
)
733 if (!txt
) return NULL
;
736 if (!strStart
) return NULL
;
738 assert('"' == *strStart
);
739 /* TODO: rewrite as 2-phase logic so that counting and copying are always in sync */
745 if ((0 == c
) || ('"' == c
))
748 /* TODO: should I un-escape more than '"' ?
749 I used to un-escape '\' as well, but it wasn't right and
750 files with UNC path like "\\foo\file.pdf" failed to load */
760 strCopy
= (char*)malloc(len
+1);
775 /* TODO: should I un-escape more than '"' ?
776 I used to un-escape '\' as well, but it wasn't right and
777 files with UNC path like "\\foo\file.pdf" failed to load */
791 char *str_parse_non_quoted(char **txt
)
801 if (!strStart
) return NULL
;
802 assert('"' != *strStart
);
806 if (char_is_ws_or_zero(c
))
811 strLen
= cur
- strStart
;
813 strCopy
= str_dupn(strStart
, strLen
);
818 /* 'txt' is path that can be:
819 - escaped, in which case it starts with '"', ends with '"' and each '"' that is part of the name is escaped
821 - unescaped, in which case it start with != '"' and ends with ' ' or eol (0)
822 This function extracts escaped or unescaped path from 'txt'. Returns NULL in case of error.
823 Caller needs to free() the result. */
824 char *str_parse_possibly_quoted(char **txt
)
839 str_copy
= str_parse_quoted(&cur
);
841 str_copy
= str_parse_non_quoted(&cur
);
846 void str_array_init(str_array
*str_arr
)
849 if (!str_arr
) return;
850 memzero(str_arr
, sizeof(str_array
));
853 void str_array_free(str_array
*str_arr
)
858 if (!str_arr
) return;
860 for (i
= 0; i
< str_arr
->items_count
; i
++)
861 free(str_arr
->items
[i
]);
862 free(str_arr
->items
);
863 str_array_init(str_arr
);
866 void str_array_delete(str_array
*str_arr
)
869 if (!str_arr
) return;
870 str_array_free(str_arr
);
871 free((void*)str_arr
);
874 str_item
*str_array_get(str_array
*str_arr
, int index
)
877 if (!str_arr
) return NULL
;
879 assert(index
< str_arr
->items_count
);
880 if ((index
< 0) || (index
>= str_arr
->items_count
))
882 return str_arr
->items
[index
];
885 int str_array_get_count(str_array
*str_arr
)
888 if (!str_arr
) return 0;
889 return str_arr
->items_count
;
892 /* Set one string at position 'index' in 'str_arr'. Space for the item
893 must already be allocated. */
894 str_item
*str_array_set(str_array
*str_arr
, int index
, const char *str
)
900 if (!str_arr
) return NULL
;
902 if (index
>= str_arr
->items_count
)
905 str_len_cch
= str_len(str
);
906 new_item
= (str_item
*)malloc(sizeof(str_item
) + str_len_cch
*sizeof(char));
909 str_copy(new_item
->str
, str_len_cch
+1, str
);
910 if (str_arr
->items
[index
])
911 free(str_arr
->items
[index
]);
912 str_arr
->items
[index
] = new_item
;
916 #define STR_ARR_GROW_VALUE 32
918 /* make a generic array alloc */
919 str_item
*str_array_add(str_array
*str_arr
, const char *str
)
926 if (str_arr
->items_count
>= str_arr
->items_allocated
) {
927 /* increase memory for items if necessary */
928 n
= str_arr
->items_allocated
+ STR_ARR_GROW_VALUE
;
929 tmp
= (str_item
**)realloc(str_arr
->items
, n
* sizeof(str_item
*));
932 str_arr
->items
= tmp
;
933 data
= &(str_arr
->items
[str_arr
->items_count
]);
934 memzero(data
, STR_ARR_GROW_VALUE
* sizeof(str_item
*));
935 str_arr
->items_allocated
= n
;
937 str_arr
->items_count
++;
938 new_item
= str_array_set(str_arr
, str_arr
->items_count
- 1, str
);
940 --str_arr
->items_count
;
944 int str_array_exists_no_case(str_array
*str_arr
, const char *str
)
950 if (!str_arr
|| !str
)
953 count
= str_arr
->items_count
;
954 for (i
= 0; i
< count
; i
++)
956 item
= str_arr
->items
[i
];
957 item_str
= item
->str
;
958 if (str_ieq(str
, item_str
))
964 str_item
*str_array_add_no_dups(str_array
*str_arr
, const char *str
)
966 if (str_array_exists_no_case(str_arr
, str
))
969 return str_array_add(str_arr
, str
);