Cleanup: Interface for Unique Strings
Of course a unique string should not be mutable. Hence function UStrAdd and UStrAdd_ should of course return a const pointer. You should therefore update the header to:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #ifndef UTILS_USTR_H
#define UTILS_USTR_H
#include <stdbool.h>
#include <stddef.h>
struct UStr
{
    size_t len;
    char cstr[];
};
const struct UStr *UStrAdd_(const char *s, bool *added);
const struct UStr *UStrAdd(const char *s);
void UStrPrintPool(void);
#endif // UTILS_USTR_H
 | 
Here the corresponding source file and the updated test program:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ustr.h"
struct Node
{
    struct Node *next;
    struct UStr ustr;
};
static struct Node *node;
const struct UStr *
UStrAdd_(const char *s, bool *added)
{
    size_t len = strlen(s);
    if (added) {
        *added = true;
    }
    for (struct Node *n = node; n; n = n->next) {
        if (len == n->ustr.len && !strcmp(s, n->ustr.cstr)) {
            if (added) {
                *added = false;
            }
            return &n->ustr;
        }
    }
    struct Node *n = malloc(len + 1 + sizeof(size_t) + sizeof(struct Node *));
    if (!n) {
        fprintf(stderr, "makeUStr: out of memory\n");
        abort();
    }
    n->next = node;
    n->ustr.len = len;
    strcpy(n->ustr.cstr, s);
    node = n;
    return &node->ustr;
}
const struct UStr *
UStrAdd(const char *s)
{
    return UStrAdd_(s, 0);
}
void
UStrPrintPool(void)
{
    for (const struct Node *n = node; n; n = n->next) {
        printf("%s\n", n->ustr.cstr);
    }
}
 | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include <stdio.h>
#include "ustr.h"
int
main(void)
{
    const struct UStr *kwIf = UStrAdd("if");
    const struct UStr *kwWhile = UStrAdd("while");
    char *line = 0;
    size_t capacity = 0;
    ssize_t len;
    while ((len = getline(&line, &capacity, stdin)) > 0) {
        line[len - 1] = 0;
        bool added = false;
        const struct UStr *ident = UStrAdd_(line, &added);
        if (ident == kwIf) {
            printf("keyword 'if'\n");
        } else if (ident == kwWhile) {
            printf("keyword 'while'\n");
        } else {
            printf("identifier '%s'\n", ident->cstr);
            if (added) {
                printf("this is new\n");
            }
        }
    }
    printf("Pool of UStr:\n");
    UStrPrintPool();
}
 |