Edit

kc3-lang/libxkbcommon/test/utils-text.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2024-10-09 07:11:13
    Hash : 3ed763c3
    Message : test: Add strip_lines and uncomment to text utils These allow us to store comments in files (especially keymaps), that can then be removed (e.g. to compare with output) or uncommented (e.g. to activate an optional line).

  • test/utils-text.c
  • /*
     * Copyright © 2024 Pierre Le Marre <dev@wismill.eu>
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice (including the next
     * paragraph) shall be included in all copies or substantial portions of the
     * Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     * DEALINGS IN THE SOFTWARE.
     */
    
    #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;
        }
        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 = (size_t)(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);
    }