Code:
#include <stdio.h>
#include <ctype.h>
struct menuitem {
const char *text; /* Text to display */
int key; /* Value that matches this selection */
int value;
int casesensitive;
};
struct menudef {
const struct menuitem *items; /* Pointer to array of menu items */
size_t nitems; /* Number of items in the array */
int invalidvalue; /* Value to return if a selection is invalid */
int inputerrvalue; /* Value to return on input (stream) error or eof */
int numerickeys; /* If 0, menu keys are char, otherwise int */
const char *title; /* Displayed at the top of the menu */
const char *prompt; /* Prompt for input */
const char *errormsg; /* Error message to display if selection invalid */
const char *iprologue; /* String to print before each menu item's key */
const char *iepilogue; /* String to print after each menu item's key */
};
enum {
MENU_CASENOTSENSITIVE = 0,
MENU_CASESENSITIVE = 1,
MENU_NUMERICKEYS = 0,
MENU_CHARKEYS = 1,
MENU_REPEATONERROR = 1
};
int menu(const struct menudef *m, int repeat, FILE *in, FILE *out);
static void menu_display(const struct menudef *m, FILE *out);
static int menu_getsel(const struct menudef *m, FILE *in);
static int menu_keyvalue(const struct menudef *m, int key);
int menu(const struct menudef *m, int repeat, FILE *in, FILE *out)
{
int selection;
int again;
do {
menu_display(m, out);
selection = menu_getsel(m, in);
if (selection == m->inputerrvalue)
break;
again = selection == m->invalidvalue && repeat == MENU_REPEATONERROR;
if (again && m->errormsg)
fputs(m->errormsg, out);
} while (again);
return selection;
}
static void menu_display(const struct menudef *m, FILE *out)
{
size_t i;
if (m->title)
fputs(m->title, out);
for (i = 0; i < m->nitems; i++) {
if (m->iprologue)
fputs(m->iprologue, out);
if (m->numerickeys == MENU_NUMERICKEYS)
fprintf(out, "%d", m->items[i].key);
else
fputc(m->items[i].key, out);
if (m->iepilogue)
fputs(m->iepilogue, out);
if (m->items[i].text)
fputs(m->items[i].text, out);
fputs("\n", out);
}
if (m->prompt)
fputs(m->prompt, out);
}
static int menu_getsel(const struct menudef *m, FILE *in)
{
int n;
char c;
int key;
int ns;
if (m->numerickeys == MENU_NUMERICKEYS) {
ns = fscanf(in, "%d", &n);
if (ns != 1)
fscanf(in, "%*s");
}
else
ns = fscanf(in, " %c", &c);
if (ns != 1)
return ferror(in) || feof(in) ? m->inputerrvalue : m->invalidvalue;
key = m->numerickeys == MENU_NUMERICKEYS ? n : c;
return menu_keyvalue(m, key);
}
static int menu_keyvalue(const struct menudef *m, int key)
{
size_t i;
for (i = 0; i < m->nitems; i++) {
int search;
if (m->numerickeys == MENU_CHARKEYS) {
search = m->items[i].casesensitive == MENU_CASENOTSENSITIVE
? toupper(key)
: key;
} else
search = key;
if (m->items[i].key == search)
break;
}
return i == m->nitems ? m->invalidvalue : m->items[i].value;
}
/*********************************************************************/
enum {
SEL_ERROR = -2,
SEL_INVALID = -1,
SEL_ORANGES,
SEL_APPLES,
SEL_PEARS
};
int main(void)
{
static const struct menuitem mitems[] = {
{ "Oranges", 1, SEL_ORANGES, MENU_CASENOTSENSITIVE },
{ "Apples", 2, SEL_APPLES, MENU_CASENOTSENSITIVE },
{ "Pears", 3, SEL_PEARS, MENU_CASENOTSENSITIVE }
};
static const struct menudef mainmenu = {
mitems,
sizeof mitems / sizeof mitems[0],
SEL_INVALID,
SEL_ERROR,
MENU_NUMERICKEYS,
"Dispense cash as:\n",
"Select> ",
"\nInvalid selection, try again\n",
" (",
") "
};
int selection;
puts("\n/\\/\\/\\/\\/\\/\\ Welcome to your ATM /\\/\\/\\/\\/\\\n");
selection = menu(&mainmenu, 1, stdin, stdout);
switch (selection)
{
case SEL_ORANGES:
case SEL_APPLES:
case SEL_PEARS:
printf("Hodor gives you your %s.\n", mitems[selection].text);
break;
default:
printf("Hodor, Hodor, Hodor!\n");
break;
}
return 0;
}