utils-text.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright © 2024 Pierre Le Marre <dev@wismill.eu>
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include "test-config.h"
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include "src/darray.h"
  10. #include "src/utils.h"
  11. #include "src/utils-random.h"
  12. #include "test/utils-text.h"
  13. /* For each line, drop substring starting from a given needle, then drop
  14. * the line if the rest are only whitespaces. The needle must not contain
  15. * "\n". */
  16. char *
  17. strip_lines(const char *input, size_t input_length, const char *prefix)
  18. {
  19. darray_char buf = darray_new();
  20. const size_t prefix_len = strlen(prefix);
  21. const char *start = input;
  22. const char *end = input + input_length;
  23. const char *next = strstr(start, prefix);
  24. size_t count;
  25. while (start < end && next != NULL) {
  26. count = (size_t)(next - start);
  27. next = start + count + prefix_len;
  28. /* Find previous non-space */
  29. size_t i;
  30. for (i = count; i > 0; i--) {
  31. if (start[i - 1] != ' ' && start[i - 1] != '\t')
  32. break;
  33. }
  34. bool dropped = false;
  35. /* Drop line if only whitespaces */
  36. if (i == 0 || start[i - 1] == '\n') {
  37. count = i;
  38. dropped = true;
  39. }
  40. /* Append string */
  41. darray_append_items(buf, start, (darray_size_t) count);
  42. /* Find end of line */
  43. if (next >= end) {
  44. start = end;
  45. break;
  46. }
  47. start = strchr(next, 0x0a);
  48. if (start == NULL) {
  49. start = end;
  50. break;
  51. }
  52. if (dropped)
  53. start++;
  54. next = strstr(start, prefix);
  55. }
  56. /* Append remaining */
  57. if (start < end) {
  58. count = (size_t)(end - start);
  59. darray_append_items(buf, start, (darray_size_t) count);
  60. }
  61. darray_append(buf, '\0');
  62. return darray_items(buf);
  63. }
  64. char *
  65. uncomment(const char *input, size_t input_length, const char *prefix)
  66. {
  67. darray_char buf = darray_new();
  68. const size_t prefix_len = strlen(prefix);
  69. const char *start = input;
  70. const char *end = input + input_length;
  71. const char *next = strstr(start, prefix);
  72. size_t count;
  73. while (start < end && next != NULL) {
  74. count = (size_t)(next - start);
  75. darray_append_items(buf, start, (darray_size_t) count);
  76. /* Skip prefix */
  77. start += count + prefix_len;
  78. /* Find end of line */
  79. if (start >= end)
  80. break;
  81. next = strchr(start, 0x0a);
  82. if (next == NULL)
  83. break;
  84. next = strstr(next, prefix);
  85. }
  86. /* Append remaining */
  87. if (start < end) {
  88. count = (size_t)(end - start);
  89. darray_append_items(buf, start, (darray_size_t) count);
  90. }
  91. darray_append(buf, '\0');
  92. return darray_items(buf);
  93. }
  94. /* Split string into lines */
  95. size_t
  96. split_lines(const char *input, size_t input_length,
  97. struct text_line *output, size_t output_length)
  98. {
  99. const char *start = input;
  100. const char *next;
  101. size_t l;
  102. size_t i = 0;
  103. for (l = 0; i < input_length && l < output_length && *start != '\0'; l++) {
  104. /* Look for newline character */
  105. next = strchr(start, 0x0a);
  106. output[l].start = start;
  107. if (next == NULL) {
  108. /* Not found: add the rest of the string */
  109. output[l++].length = strlen(start);
  110. break;
  111. }
  112. output[l].length = (size_t)(next - start) + 1;
  113. start = next + 1;
  114. i += output[l].length;
  115. }
  116. return l;
  117. }
  118. size_t
  119. concat_lines(struct text_line *lines, size_t length,
  120. const char *sep, char *output)
  121. {
  122. char *out = output;
  123. size_t sep_len = strlen(sep);
  124. for (size_t i = 0; i < length; i++) {
  125. if (i > 0) {
  126. memcpy(out, sep, sep_len);
  127. out += sep_len;
  128. }
  129. memcpy(out, lines[i].start, lines[i].length);
  130. out += lines[i].length;
  131. }
  132. *out = '\0';
  133. return (size_t)(out - output);
  134. }
  135. size_t
  136. shuffle_lines(struct text_line *lines, size_t length, char *output)
  137. {
  138. /* Shuffle lines in-place using Fisher–Yates algorithm.
  139. * See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle */
  140. assert(length < RAND_MAX);
  141. char *out = output;
  142. if (length > 1) {
  143. /* 1. Set the current i to the last line.
  144. * 2. Take a random line j before the current line i.
  145. * 3. Swap the lines i and j.
  146. * 4. Append line i to the output.
  147. * 5. If i is the first line, stop. Else decrease i and go to 2).
  148. */
  149. for (size_t i = length - 1; i > 0; i--) {
  150. /* Swap current line with random line before it */
  151. size_t j = random() % (i+1);
  152. struct text_line tmp = lines[j];
  153. lines[j] = lines[i];
  154. lines[i] = tmp;
  155. /* Append current line */
  156. memcpy(out, lines[i].start, lines[i].length);
  157. out += lines[i].length;
  158. /* Ensure line ends with newline */
  159. if (out[-1] != '\n') {
  160. out[0] = '\n';
  161. out++;
  162. }
  163. }
  164. }
  165. return (size_t)(out - output);
  166. }