If one has POSIX extensions available, then it seems that defining _POSIX_C_SOURCE and just using getline or detdelim is the way to go. However, for source that is just C, here is a solution I’ve found from various sources, mainly here. I’ve also tried to make it more safe.

// Read line from stdin

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

#define CHUNK_SIZE 16 // Allocate memory in chunks of this size

// Safe self-realloc
void *xrealloc(void *ptr, size_t size)
{
    void *tmp = realloc(ptr, size);
    if (!tmp) free(ptr);
    return tmp;
}

// Dynamically allocate memory for char pointer from stdin
char *readline(void)
{
    size_t len = 0, chunk = CHUNK_SIZE;
    char *str = (char *)malloc(CHUNK_SIZE);
    if (!str) return NULL;
    int ch;
    while((ch = getchar()) != EOF && ch != '\n' && ch != '\r') {
        str[len++] = ch;
        if (len == chunk) {
            str = (char *)xrealloc(str, chunk+=CHUNK_SIZE);
            if (!str) return NULL;
        }
    }
    str[len++] = '\0'; // Ensure str is null-terminated
    return (char *)xrealloc(str, len);
}

int main(void)
{
    setbuf(stdout, NULL); // Ensure environment doesn't buffer stdout
    printf("Enter name: ");
    char *userName = readline();
    if (!userName) return 1;
    printf("Hello, %s!\n", userName);
    free(userName);
    return 0;
}

The idea is that we allocate in chunks of 16, or whichever size. xrealloc is handy when reallocating a block of memory to itself.

int *ptr = malloc(sizeof(int) * 4);
ptr = (int *)realloc(ptr, sizeof(int) * 8);

If realloc fails, ptr gets assigned NULL, and we lose the address that we need to free, causing a memory leak. So, xrealloc allows for safe self re-allocation.