/*
* Copyright © 2024 Pierre Le Marre <dev@wismill.eu>
* SPDX-License-Identifier: MIT
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "src/darray.h"
#include "src/utils.h"
#include "test/utils-text.h"
/* For each line, drop substring starting from a given needle, then drop
* the line if the rest are only whitespaces. The needle must not contain
* "\n". */
char *
strip_lines(const char *input, size_t input_length, const char *prefix)
{
darray_char buf = darray_new();
const size_t prefix_len = strlen(prefix);
const char *start = input;
const char *end = input + input_length;
const char *next = strstr(start, prefix);
size_t count;
while (start < end && next != NULL) {
count = (size_t)(next - start);
next = start + count + prefix_len;
/* Find previous non-space */
size_t i;
for (i = count; i > 0; i--) {
if (start[i - 1] != ' ' && start[i - 1] != '\t')
break;
}
bool dropped = false;
/* Drop line if only whitespaces */
if (i == 0 || start[i - 1] == '\n') {
count = i;
dropped = true;
}
/* Append string */
darray_append_items(buf, start, count);
/* Find end of line */
if (next >= end) {
start = end;
break;
}
start = strchr(next, 0x0a);
if (start == NULL) {
start = end;
break;
}
if (dropped)
start++;
next = strstr(start, prefix);
}
/* Append remaining */
if (start < end) {
count = (size_t)(end - start);
darray_append_items(buf, start, count);
}
darray_append(buf, '\0');
return buf.item;
}
char *
uncomment(const char *input, size_t input_length, const char *prefix)
{
darray_char buf = darray_new();
const size_t prefix_len = strlen(prefix);
const char *start = input;
const char *end = input + input_length;
char *next = strstr(start, prefix);
size_t count;
while (start < end && next != NULL) {
count = (size_t)(next - start);
darray_append_items(buf, start, count);
/* Skip prefix */
start += count + prefix_len;
/* Find end of line */
if (start >= end)
break;
next = strchr(start, 0x0a);
if (next == NULL)
break;
next = strstr(next, prefix);
}
/* Append remaining */
if (start < end) {
count = (size_t)(end - start);
darray_append_items(buf, start, count);
}
darray_append(buf, '\0');
return buf.item;
}
/* Split string into lines */
size_t
split_lines(const char *input, size_t input_length,
struct text_line *output, size_t output_length)
{
const char *start = input;
char *next;
size_t l;
size_t i = 0;
for (l = 0; i < input_length && l < output_length && *start != '\0'; l++) {
/* Look for newline character */
next = strchr(start, 0x0a);
output[l].start = start;
if (next == NULL) {
/* Not found: add the rest of the string */
output[l++].length = strlen(start);
break;
}
output[l].length = (size_t)(next - start) + 1;
start = next + 1;
i += output[l].length;
}
return l;
}
size_t
concat_lines(struct text_line *lines, size_t length,
const char *sep, char *output)
{
char *out = output;
size_t sep_len = strlen(sep);
for (size_t i = 0; i < length; i++) {
if (i > 0) {
memcpy(out, sep, sep_len);
out += sep_len;
}
memcpy(out, lines[i].start, lines[i].length);
out += lines[i].length;
}
*out = '\0';
return (size_t)(out - output);
}
size_t
shuffle_lines(struct text_line *lines, size_t length, char *output)
{
/* Shuffle lines in-place using Fisher–Yates algorithm.
* See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle */
assert(length < RAND_MAX);
char *out = output;
if (length > 1) {
/* 1. Set the current i to the last line.
* 2. Take a random line j before the current line i.
* 3. Swap the lines i and j.
* 4. Append line i to the output.
* 5. If i is the first line, stop. Else decrease i and go to 2).
*/
for (size_t i = length - 1; i > 0; i--) {
/* Swap current line with random line before it */
size_t j = rand() % (i+1);
struct text_line tmp = lines[j];
lines[j] = lines[i];
lines[i] = tmp;
/* Append current line */
memcpy(out, lines[i].start, lines[i].length);
out += lines[i].length;
/* Ensure line ends with newline */
if (out[-1] != '\n') {
out[0] = '\n';
out++;
}
}
}
return (size_t)(out - output);
}