Task: Mad Libs

C: #include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define err(...) fprintf(stderr, ## __VA_ARGS__), exit(1)


typedef struct {
    char *data;
    size_t alloc;
    size_t length;
} dstr;

inline int dstr_space(dstr *s, size_t grow_amount)
{
    return s->length + grow_amount < s->alloc;
}

int dstr_grow(dstr *s)
{
    s->alloc *= 2;
    char *attempt = realloc(s->data, s->alloc);

    if (!attempt) return 0;
    else s->data = attempt;

    return 1;
}

dstr* dstr_init(const size_t to_allocate)
{
    dstr *s = malloc(sizeof(dstr));
    if (!s) goto failure;

    s->length = 0;
    s->alloc  = to_allocate;
    s->data   = malloc(s->alloc);

    if (!s->data) goto failure;

    return s;

failure:
    if (s->data) free(s->data);
    if (s) free(s);
    return NULL;
}

void dstr_delete(dstr *s)
{
    if (s->data) free(s->data);
    if (s) free(s);
}

dstr* readinput(FILE *fd)
{
    static const size_t buffer_size = 4096;
    char buffer[buffer_size];

    dstr *s = dstr_init(buffer_size);
    if (!s) goto failure;

    while (fgets(buffer, buffer_size, fd)) {
        while (!dstr_space(s, buffer_size))
            if (!dstr_grow(s)) goto failure;

        strncpy(s->data + s->length, buffer, buffer_size);
        s->length += strlen(buffer);
    }

    return s;

failure:
    dstr_delete(s);
    return NULL;
}

void dstr_replace_all(dstr *story, const char *replace, const char *insert)
{
    const size_t replace_l = strlen(replace);
    const size_t insert_l  = strlen(insert);
    char *start = story->data;

    while ((start = strstr(start, replace))) {
        if (!dstr_space(story, insert_l - replace_l))
            if (!dstr_grow(story)) err("Failed to allocate memory");

        if (insert_l != replace_l) {
            memmove(start + insert_l, start + replace_l, story->length -
                    (start + replace_l - story->data));

            
            story->length += insert_l - replace_l;
            story->data[story->length] = 0;
        }

        memmove(start, insert, insert_l);
    }
}

void madlibs(dstr *story)
{
    static const size_t buffer_size = 128;
    char insert[buffer_size];
    char replace[buffer_size];

    char *start,
         *end = story->data;

    while (start = strchr(end, '<')) {
        if (!(end = strchr(start, '>'))) err("Malformed brackets in input");

        
        strncpy(replace, start, end - start + 1);
        replace[end - start + 1] = '\0';

        printf("Enter value for field %s: ", replace);

        fgets(insert, buffer_size, stdin);
        const size_t il = strlen(insert) - 1;
        if (insert[il] == '\n')
            insert[il] = '\0';

        dstr_replace_all(story, replace, insert);
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    if (argc < 2) return 0;

    FILE *fd = fopen(argv[1], "r");
    if (!fd) err("Could not open file: '%s\n", argv[1]);

    dstr *story = readinput(fd); fclose(fd);
    if (!story) err("Failed to allocate memory");

    madlibs(story);
    printf("%s\n", story->data);
    dstr_delete(story);
    return 0;
}



C#: using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace MadLibs_RosettaCode
{
	class Program
	{
		static void Main(string[] args)
		{
			string madLibs = 
@"Write a program to create a Mad Libs like story.
The program should read an arbitrary multiline story from input.
The story will be terminated with a blank line.
Then, find each replacement to be made within the story, 
ask the user for a word to replace it with, and make all the replacements.
Stop when there are none left and print the final story.
The input should be an arbitrary story in the form:
<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.
Given this example, it should then ask for a name, 
a he or she and a noun (<name> gets replaced both times with the same value).";

			StringBuilder sb = new StringBuilder();
			Regex pattern = new Regex(@"\<(.*?)\>");
			string storyLine;
			string replacement;

			Console.WriteLine(madLibs + Environment.NewLine + Environment.NewLine);
			Console.WriteLine("Enter a story: ");

			
			do
			{
				storyLine = Console.ReadLine();
				sb.Append(storyLine + Environment.NewLine);
			} while (!string.IsNullOrEmpty(storyLine) && !string.IsNullOrWhiteSpace(storyLine));

			
			Match nameMatch = pattern.Matches(sb.ToString()).OfType<Match>().Where(x => x.Value.Equals("<name>")).Select(x => x.Value).Distinct() as Match;
			if(nameMatch != null)
			{
				do
				{
					Console.WriteLine("Enter value for: " + nameMatch.Value);
					replacement = Console.ReadLine();
				} while (string.IsNullOrEmpty(replacement) || string.IsNullOrWhiteSpace(replacement));
				sb.Replace(nameMatch.Value, replacement);
			}

			foreach (Match match in pattern.Matches(sb.ToString()))
			{
				replacement = string.Empty;
				
				do
				{
					Console.WriteLine("Enter value for: " + match.Value);
					replacement = Console.ReadLine();
				} while (string.IsNullOrEmpty(replacement) || string.IsNullOrWhiteSpace(replacement));

				int location = sb.ToString().IndexOf(match.Value);
				sb.Remove(location, match.Value.Length).Insert(location, replacement);
			}

			Console.WriteLine(Environment.NewLine + Environment.NewLine + "--[ Here's your story! ]--");
			Console.WriteLine(sb.ToString());
		}
	}
}