Before the era of ubiquitous electronic gadgets, one thing that passed for fun o
ID: 3598809 • Letter: B
Question
Before the era of ubiquitous electronic gadgets, one thing that passed for fun on lo what were known as "Mad-Libs." These consisted of a story with several blanks that were labelled with part of speech or a vague description of the type of word that was to be filled in that blank. One siblin would ask the other to provide the words. After all the words were provided, the story would be read aloud, and much hilarity would ensue. ng road trips was For this project we will be providing a system that processes "Mad-Libs in the form of text files into another text file which contains the resulting story. The program will ask the user for two files, one input file with the Mad-Lib template, and an output filename in which it will write the final story. See the sample run for an example. Mad-Lib Template File The template file is a normal text file that contains both the text of the story and "tokens" that will be replaced by words entered by the user. All of the words, punctuation, and tokens are separated by spaces. (This makes the output a little awkward, particularly around periods, but we'll let it go). A token is a word that starts with the character and contains no spaces within it. It may contain hyphen (-) characters which will get replaced by spaces when prompting the user for that particular word. Algorithm The program will open both files and the repeatedly process each word in the template file. If it is not a token word, it will simply copy it to the output file. If it is a token word, it will convert the description tween the characters into human-readable form, use it to prompt the user for a replacement worcd and then write the replacement word to the output file. Project Your program must have a good design with little duplicate code and good use of functions. I hav provided a description of the functions that I used in my solution. I have not described which function call which other functions. You do not have to follow this design exactly (although it will probably mak things easier if you do...) istoken(char word ) Returns true if word is a token.Explanation / Answer
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define err(...) fprintf(stderr, ## __VA_ARGS__), exit(1)
/* We create a dynamic string with a few functions which make modifying
* the string and growing a bit easier */
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));
/* Remember to null terminate the data so we can utilize it
* as we normally would */
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");
/* One extra for current char and another for nul byte */
strncpy(replace, start, end - start + 1);
replace[end - start + 1] = '';
printf("Enter value for field %s: ", replace);
fgets(insert, buffer_size, stdin);
const size_t il = strlen(insert) - 1;
if (insert[il] == ' ')
insert[il] = '';
dstr_replace_all(story, replace, insert);
}
printf(" ");
}
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 ", argv[1]);
dstr *story = readinput(fd); fclose(fd);
if (!story) err("Failed to allocate memory");
madlibs(story);
printf("%s ", story->data);
dstr_delete(story);
return 0;
}
Output:
./a.out madlibs.txt
Enter value for field <name>: John
Enter value for field <he or she>: he
Enter value for field <noun>: Flamingo
John went for a walk in the park. he
found a Flamingo. John decided to take it home.
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.