Skip to content

List Converter — C source

Transform a list between separators (newline, comma, space, pipe, semicolon, tab) with trim, dedupe, sort, and empty-removal options. Runs entirely in your browser, with a shareable link.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/* List Converter — split/trim/dedup/sort/join between separators — C (C11) port of the list-converter tool. */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { int trim, remove_empty, unique, sort, case_insensitive; } Opts;

static const char *SEP_NAMES[] = { "newline", "comma", "space", "pipe", "semicolon", "tab" };
static const char SEP_CHARS[] = { '\n', ',', ' ', '|', ';', '\t' };

/* Separators are all single characters, so one char is the whole glue.
 * NULL falls back to the TS default (from '\n', to ','). */
static char sep_char(const char *name, char fallback) {
    if (name == NULL) return fallback;
    for (int i = 0; i < 6; i++)
        if (strcmp(name, SEP_NAMES[i]) == 0) return SEP_CHARS[i];
    return name[0]; /* a literal custom separator */
}

/* strcmp over optionally-lowercased bytes; the unique/sort comparison key. */
static int key_cmp(const char *a, const char *b, int ci) {
    while (*a != '\0' && *b != '\0') {
        int ca = ci ? tolower((unsigned char)*a) : (unsigned char)*a;
        int cb = ci ? tolower((unsigned char)*b) : (unsigned char)*b;
        if (ca != cb) return ca - cb;
        a++;
        b++;
    }
    return (unsigned char)*a - (unsigned char)*b;
}

/* Convert a list between separators: split -> (trim) -> (drop empties) ->
 * (dedup, first occurrence wins) -> (stable sort) -> join. malloc'd result. */
static char *convert(const char *input, const char *from, const char *to, Opts o) {
    char fs = sep_char(from, '\n'), ts = sep_char(to, ',');
    size_t len = strlen(input), n = 0;
    char *buf = malloc(len + 1);
    char **items = malloc((len + 2) * sizeof *items); /* at most one item per byte */
    memcpy(buf, input, len + 1);

    for (char *p = buf, *start = buf;;) { /* split in place: sep -> '\0' */
        if (*p == fs || *p == '\0') {
            int last = (*p == '\0');
            *p = '\0';
            char *s = start;
            if (o.trim) { /* strip both ends */
                while (*s != '\0' && isspace((unsigned char)*s)) s++;
                char *e = s + strlen(s);
                while (e > s && isspace((unsigned char)e[-1])) *--e = '\0';
            }
            if (!(o.remove_empty && *s == '\0')) items[n++] = s;
            if (last) break;
            start = p + 1;
        }
        p++;
    }
    if (o.unique) { /* keep the first occurrence of each key */
        size_t m = 0;
        for (size_t i = 0; i < n; i++) {
            size_t j = 0;
            while (j < m && key_cmp(items[i], items[j], o.case_insensitive) != 0) j++;
            if (j == m) items[m++] = items[i];
        }
        n = m;
    }
    if (o.sort) /* insertion sort: stable, plenty for list-sized inputs */
        for (size_t i = 1; i < n; i++) {
            char *key = items[i];
            size_t j = i;
            while (j > 0 && key_cmp(items[j - 1], key, o.case_insensitive) > 0) {
                items[j] = items[j - 1];
                j--;
            }
            items[j] = key;
        }
    size_t total = 1;
    for (size_t i = 0; i < n; i++) total += strlen(items[i]) + 1;
    char *out = malloc(total), *w = out;
    for (size_t i = 0; i < n; i++) {
        if (i != 0) *w++ = ts;
        size_t l = strlen(items[i]);
        memcpy(w, items[i], l);
        w += l;
    }
    *w = '\0';
    free(items);
    free(buf);
    return out;
}

int main(void) {
    /* "b\n a \nB\na\n\nc" -> trim, drop empties, case-insensitive dedup+sort
     * -> "a,b,c" (default from=newline, to=comma). */
    Opts all = { 1, 1, 1, 1, 1 };
    char *out = convert("b\n a \nB\na\n\nc", NULL, NULL, all);
    printf("%s\n", out);
    free(out);
    return 0;
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →