split_string.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #ifndef BADVPN_SPLIT_STRING_H
  2. #define BADVPN_SPLIT_STRING_H
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <stdint.h>
  6. #include <limits.h>
  7. #include <misc/balloc.h>
  8. #include <misc/debug.h>
  9. #include <misc/expstring.h>
  10. /**
  11. * Splits the given string by a character, and returns the result
  12. * as a malloc'd array of char pointers, each malloc'd. The array
  13. * is terminated by a NULL pointer.
  14. *
  15. * @param str string to split
  16. * @param del delimiter character
  17. * @return pointer to array of strings, or NULL on failure. On success,
  18. * at least one string will always be returned.
  19. */
  20. static char ** split_string (const char *str, char del);
  21. /**
  22. * Counts the number of strings in an array (same format as used by
  23. * {@link split_string}).
  24. *
  25. * @param names pointer to array of strings; must not be NULL.
  26. * @return number of strings
  27. */
  28. static size_t count_strings (char **names);
  29. /**
  30. * Frees an array of strings (same format as used by
  31. * {@link split_string}). This first frees the individual strings,
  32. * then the whole array.
  33. *
  34. * @param names pointer to array of strings; must not be NULL.
  35. */
  36. static void free_strings (char **names);
  37. /**
  38. * Concatenates the given strings, inserting a delimiter character
  39. * in between. The result is returned as a newly malloc'd string.
  40. * If there are no strings, an empty string is produced.
  41. * If there is just one string, this string is copied.
  42. *
  43. * @param names pointer to array of strings; must not be NULL.
  44. * Format is as returned by {@link split_string}.
  45. * @param del delimiter to insert between strings
  46. * @return resulting malloc'd string, or NULL on failure.
  47. */
  48. static char * implode_strings (char **names, char del);
  49. /**
  50. * Splits the given string by a character in-place by replacing
  51. * all delimiting characters with nulls, and returns the number
  52. * of such replacements.
  53. *
  54. * @param str string to split in-place; must not be NULL.
  55. * @param del delimiter character
  56. * @return number of replaced characters, or equivalently, number
  57. * of resulting strings minus one
  58. */
  59. static size_t split_string_inplace2 (char *str, char del);
  60. /**
  61. * Concatenates the given strings, inserting a delimiter character
  62. * in between. The result is returned as a newly malloc'd string.
  63. * If there are no strings, an empty string is produced.
  64. * If there is just one string, this string is copied.
  65. *
  66. * @param names pointer to a character array containing the
  67. * null-terinated strings one after another, i.e.
  68. * as produced by {@link split_string_inplace2}.
  69. * Must not be NULL.
  70. * @param num_names number of strings, e.g. the return value of
  71. * {@link split_string_inplace2} plus one
  72. * @param del delimiter to insert between strings
  73. * @return resulting malloc'd string, or NULL on failure.
  74. */
  75. static char * implode_compact_strings (const char *names, size_t num_names, char del);
  76. static char ** split_string (const char *str, char del)
  77. {
  78. size_t len = strlen(str);
  79. // count parts
  80. size_t num_parts = 0;
  81. size_t i = 0;
  82. while (1) {
  83. size_t j = i;
  84. while (j < len && str[j] != del) j++;
  85. num_parts++;
  86. if (num_parts == SIZE_MAX) { // need to allocate +1 pointer
  87. goto fail0;
  88. }
  89. if (j == len) {
  90. break;
  91. }
  92. i = j + 1;
  93. }
  94. // allocate array for part pointers
  95. char **result = BAllocArray(num_parts + 1, sizeof(*result));
  96. if (!result) {
  97. goto fail0;
  98. }
  99. num_parts = 0;
  100. i = 0;
  101. while (1) {
  102. size_t j = i;
  103. while (j < len && str[j] != del) j++;
  104. if (!(result[num_parts] = malloc(j - i + 1))) {
  105. goto fail1;
  106. }
  107. memcpy(result[num_parts], str + i, j - i);
  108. result[num_parts][j - i] = '\0';
  109. num_parts++;
  110. if (j == len) {
  111. break;
  112. }
  113. i = j + 1;
  114. }
  115. result[num_parts] = NULL;
  116. return result;
  117. fail1:
  118. while (num_parts-- > 0) {
  119. free(result[num_parts]);
  120. }
  121. BFree(result);
  122. fail0:
  123. return NULL;
  124. }
  125. static size_t count_strings (char **names)
  126. {
  127. ASSERT(names)
  128. size_t i;
  129. for (i = 0; names[i]; i++);
  130. return i;
  131. }
  132. static void free_strings (char **names)
  133. {
  134. ASSERT(names)
  135. size_t i = count_strings(names);
  136. while (i-- > 0) {
  137. free(names[i]);
  138. }
  139. BFree(names);
  140. }
  141. static char * implode_strings (char **names, char del)
  142. {
  143. ASSERT(names)
  144. ExpString str;
  145. if (!ExpString_Init(&str)) {
  146. goto fail0;
  147. }
  148. for (size_t i = 0; names[i]; i++) {
  149. if (i > 0 && !ExpString_AppendChar(&str, del)) {
  150. goto fail1;
  151. }
  152. if (!ExpString_Append(&str, names[i])) {
  153. goto fail1;
  154. }
  155. }
  156. return ExpString_Get(&str);
  157. fail1:
  158. ExpString_Free(&str);
  159. fail0:
  160. return NULL;
  161. }
  162. static size_t split_string_inplace2 (char *str, char del)
  163. {
  164. ASSERT(str)
  165. size_t num_extra_parts = 0;
  166. while (*str) {
  167. if (*str == del) {
  168. *str = '\0';
  169. num_extra_parts++;
  170. }
  171. str++;
  172. }
  173. return num_extra_parts;
  174. }
  175. static char * implode_compact_strings (const char *names, size_t num_names, char del)
  176. {
  177. ASSERT(names)
  178. ExpString str;
  179. if (!ExpString_Init(&str)) {
  180. goto fail0;
  181. }
  182. int is_first = 1;
  183. while (num_names > 0) {
  184. if (!is_first && !ExpString_AppendChar(&str, del)) {
  185. goto fail1;
  186. }
  187. if (!ExpString_Append(&str, names)) {
  188. goto fail1;
  189. }
  190. names += strlen(names) + 1;
  191. num_names--;
  192. is_first = 0;
  193. }
  194. return ExpString_Get(&str);
  195. fail1:
  196. ExpString_Free(&str);
  197. fail0:
  198. return NULL;
  199. }
  200. #endif