Command Line Menu in C: Building Interactive User Interfaces

Command line menus are fundamental to many C applications, providing a simple yet effective way for users to interact with programs. From system utilities to configuration tools, text-based menus offer a portable, lightweight interface that works across all platforms. For C programmers, building robust command line menus involves handling user input, validating choices, managing state, and creating intuitive navigation systems.

What is a Command Line Menu?

A command line menu is a text-based user interface that presents options to users and processes their selections. It typically displays numbered or lettered choices, prompts for input, and executes corresponding actions. Command line menus range from simple single-level menus to complex hierarchical systems with submenus, configuration screens, and data entry forms.

Why Build Command Line Menus in C

  1. Portability: Works on any platform with a terminal
  2. Simplicity: Easy to implement and understand
  3. Scriptability: Can be automated and tested
  4. Resource Efficiency: Minimal system requirements
  5. Remote Access: Works over SSH and serial connections
  6. Rapid Development: Quick to prototype and iterate

Basic Single-Level Menu

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
// ============================================================
// BASIC SINGLE-LEVEL MENU
// ============================================================
void clearScreen() {
// Cross-platform screen clearing
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
void printHeader(const char *title) {
printf("\n========================================\n");
printf("          %s\n", title);
printf("========================================\n");
}
void waitForKey() {
printf("\nPress Enter to continue...");
while (getchar() != '\n');
getchar();  // Wait for Enter
}
// Menu functions
void option1() {
printf("\nπŸ“ You selected Option 1: File Operations\n");
printf("   This would handle file operations here.\n");
}
void option2() {
printf("\nπŸ”§ You selected Option 2: Configuration\n");
printf("   This would handle configuration settings.\n");
}
void option3() {
printf("\nπŸ“Š You selected Option 3: View Statistics\n");
printf("   This would display statistics.\n");
}
void option4() {
printf("\nπŸ” You selected Option 4: Search\n");
printf("   This would perform searches.\n");
}
void option5() {
printf("\nπŸ“ You selected Option 5: Help\n");
printf("   This would show help information.\n");
}
void exitProgram() {
printf("\nπŸ‘‹ Goodbye!\n");
}
int main() {
int choice;
do {
clearScreen();
printHeader("MAIN MENU");
printf("\n");
printf("   1. File Operations\n");
printf("   2. Configuration\n");
printf("   3. View Statistics\n");
printf("   4. Search\n");
printf("   5. Help\n");
printf("   0. Exit\n");
printf("\n   Enter your choice: ");
scanf("%d", &choice);
getchar();  // Clear newline
switch (choice) {
case 1:
option1();
waitForKey();
break;
case 2:
option2();
waitForKey();
break;
case 3:
option3();
waitForKey();
break;
case 4:
option4();
waitForKey();
break;
case 5:
option5();
waitForKey();
break;
case 0:
exitProgram();
break;
default:
printf("\n❌ Invalid choice! Please try again.\n");
waitForKey();
}
} while (choice != 0);
return 0;
}

Menu with Input Validation

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
// ============================================================
// MENU WITH INPUT VALIDATION
// ============================================================
typedef struct {
int id;
char name[50];
float value;
} Item;
Item items[100];
int itemCount = 0;
// Clear input buffer
void clearInputBuffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
// Get validated integer input
int getValidInt(const char *prompt, int min, int max) {
int value;
int result;
char buffer[100];
while (1) {
printf("%s [%d-%d]: ", prompt, min, max);
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
// Check if input is a valid integer
char *endptr;
value = strtol(buffer, &endptr, 10);
// Check if conversion succeeded and no extra characters
if (endptr != buffer && (*endptr == '\n' || *endptr == '\0')) {
if (value >= min && value <= max) {
return value;
}
}
}
printf("❌ Invalid input. Please enter a number between %d and %d.\n", 
min, max);
}
}
// Get validated float input
float getValidFloat(const char *prompt, float min, float max) {
float value;
char buffer[100];
while (1) {
printf("%s [%.2f-%.2f]: ", prompt, min, max);
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
char *endptr;
value = strtof(buffer, &endptr);
if (endptr != buffer && (*endptr == '\n' || *endptr == '\0')) {
if (value >= min && value <= max) {
return value;
}
}
}
printf("❌ Invalid input. Please enter a number between %.2f and %.2f.\n", 
min, max);
}
}
// Get validated string input
void getValidString(const char *prompt, char *buffer, int maxLen) {
while (1) {
printf("%s: ", prompt);
if (fgets(buffer, maxLen, stdin) != NULL) {
// Remove trailing newline
buffer[strcspn(buffer, "\n")] = 0;
// Check if not empty
if (strlen(buffer) > 0) {
return;
}
}
printf("❌ Input cannot be empty. Please try again.\n");
}
}
// Get yes/no input
bool getYesNo(const char *prompt) {
char response[10];
while (1) {
printf("%s (y/n): ", prompt);
if (fgets(response, sizeof(response), stdin) != NULL) {
response[strcspn(response, "\n")] = 0;
if (strcasecmp(response, "y") == 0 || 
strcasecmp(response, "yes") == 0) {
return true;
}
if (strcasecmp(response, "n") == 0 || 
strcasecmp(response, "no") == 0) {
return false;
}
}
printf("❌ Please enter 'y' or 'n'.\n");
}
}
// Menu options
void addItem() {
printf("\n--- Add New Item ---\n");
Item newItem;
newItem.id = itemCount + 1;
getValidString("Enter item name", newItem.name, sizeof(newItem.name));
newItem.value = getValidFloat("Enter value", 0, 10000);
items[itemCount++] = newItem;
printf("\nβœ… Item added successfully! (ID: %d)\n", newItem.id);
}
void viewItems() {
printf("\n--- Item List ---\n");
if (itemCount == 0) {
printf("No items found.\n");
return;
}
printf("%-5s %-20s %-10s\n", "ID", "Name", "Value");
printf("----------------------------------------\n");
for (int i = 0; i < itemCount; i++) {
printf("%-5d %-20s $%-9.2f\n", 
items[i].id, items[i].name, items[i].value);
}
printf("----------------------------------------\n");
printf("Total items: %d\n", itemCount);
}
void editItem() {
if (itemCount == 0) {
printf("\n❌ No items to edit.\n");
return;
}
viewItems();
int id = getValidInt("Enter item ID to edit", 1, itemCount);
int index = id - 1;
printf("\nEditing Item %d:\n", id);
printf("Current name: %s\n", items[index].name);
printf("Current value: $%.2f\n", items[index].value);
char newName[50];
getValidString("Enter new name (or Enter to keep)", newName, sizeof(newName));
if (strlen(newName) > 0) {
strcpy(items[index].name, newName);
}
float newValue = getValidFloat("Enter new value (or -1 to keep)", -1, 10000);
if (newValue >= 0) {
items[index].value = newValue;
}
printf("\nβœ… Item updated successfully!\n");
}
void deleteItem() {
if (itemCount == 0) {
printf("\n❌ No items to delete.\n");
return;
}
viewItems();
int id = getValidInt("Enter item ID to delete", 1, itemCount);
if (getYesNo("Are you sure you want to delete this item?")) {
// Shift remaining items
for (int i = id - 1; i < itemCount - 1; i++) {
items[i] = items[i + 1];
}
itemCount--;
printf("\nβœ… Item deleted successfully!\n");
} else {
printf("\nDeletion cancelled.\n");
}
}
void searchItems() {
if (itemCount == 0) {
printf("\n❌ No items to search.\n");
return;
}
char searchTerm[50];
getValidString("Enter search term", searchTerm, sizeof(searchTerm));
printf("\n--- Search Results ---\n");
int found = 0;
for (int i = 0; i < itemCount; i++) {
if (strstr(items[i].name, searchTerm) != NULL) {
printf("ID %d: %s - $%.2f\n", 
items[i].id, items[i].name, items[i].value);
found++;
}
}
printf("Found %d matching item(s).\n", found);
}
int main() {
int choice;
do {
clearScreen();
printHeader("ITEM MANAGEMENT SYSTEM");
printf("\n");
printf("   πŸ“‹ Main Menu\n");
printf("   ==========\n");
printf("   1. Add Item\n");
printf("   2. View All Items\n");
printf("   3. Edit Item\n");
printf("   4. Delete Item\n");
printf("   5. Search Items\n");
printf("   0. Exit\n");
printf("\n   Current items: %d\n", itemCount);
choice = getValidInt("\n   Enter your choice", 0, 5);
switch (choice) {
case 1: addItem(); break;
case 2: viewItems(); break;
case 3: editItem(); break;
case 4: deleteItem(); break;
case 5: searchItems(); break;
case 0: 
if (getYesNo("Exit program?")) {
printf("\nπŸ‘‹ Goodbye!\n");
} else {
choice = -1;  // Continue loop
}
break;
}
if (choice != 0) {
waitForKey();
}
} while (choice != 0);
return 0;
}

Hierarchical Menu System

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// ============================================================
// HIERARCHICAL MENU SYSTEM
// ============================================================
// Menu item structure
typedef struct MenuItem {
int id;
char title[50];
void (*function)(void);
struct MenuItem *submenu;
int submenuSize;
} MenuItem;
// Forward declarations
void mainMenu();
void fileMenu();
void editMenu();
void viewMenu();
void toolsMenu();
void helpMenu();
void fileNew();
void fileOpen();
fileSave();
void fileSaveAs();
void fileExit();
void editCut();
void editCopy();
void editPaste();
void editFind();
void viewZoomIn();
void viewZoomOut();
void viewFullScreen();
void toolsOptions();
void toolsPlugins();
void helpAbout();
void helpHelp();
// ============================================================
// MENU DEFINITIONS
// ============================================================
// File submenu
MenuItem fileSubmenu[] = {
{1, "New", fileNew, NULL, 0},
{2, "Open...", fileOpen, NULL, 0},
{3, "Save", fileSave, NULL, 0},
{4, "Save As...", fileSaveAs, NULL, 0},
{5, "Exit", fileExit, NULL, 0},
{0, "", NULL, NULL, 0}  // Sentinel
};
// Edit submenu
MenuItem editSubmenu[] = {
{1, "Cut", editCut, NULL, 0},
{2, "Copy", editCopy, NULL, 0},
{3, "Paste", editPaste, NULL, 0},
{4, "Find...", editFind, NULL, 0},
{0, "", NULL, NULL, 0}
};
// View submenu
MenuItem viewSubmenu[] = {
{1, "Zoom In", viewZoomIn, NULL, 0},
{2, "Zoom Out", viewZoomOut, NULL, 0},
{3, "Full Screen", viewFullScreen, NULL, 0},
{0, "", NULL, NULL, 0}
};
// Tools submenu
MenuItem toolsSubmenu[] = {
{1, "Options...", toolsOptions, NULL, 0},
{2, "Plugins...", toolsPlugins, NULL, 0},
{0, "", NULL, NULL, 0}
};
// Help submenu
MenuItem helpSubmenu[] = {
{1, "Help Topics", helpHelp, NULL, 0},
{2, "About", helpAbout, NULL, 0},
{0, "", NULL, NULL, 0}
};
// Main menu
MenuItem mainMenuItems[] = {
{1, "File", fileMenu, fileSubmenu, 
sizeof(fileSubmenu) / sizeof(MenuItem) - 1},
{2, "Edit", editMenu, editSubmenu, 
sizeof(editSubmenu) / sizeof(MenuItem) - 1},
{3, "View", viewMenu, viewSubmenu, 
sizeof(viewSubmenu) / sizeof(MenuItem) - 1},
{4, "Tools", toolsMenu, toolsSubmenu, 
sizeof(toolsSubmenu) / sizeof(MenuItem) - 1},
{5, "Help", helpMenu, helpSubmenu, 
sizeof(helpSubmenu) / sizeof(MenuItem) - 1},
{0, "Exit", NULL, NULL, 0}
};
// ============================================================
// MENU DISPLAY FUNCTIONS
// ============================================================
void displayMenu(MenuItem *items, const char *title) {
clearScreen();
printHeader(title);
printf("\n");
for (int i = 0; items[i].id != 0; i++) {
if (items[i].submenu != NULL) {
printf("   %d. %s >\n", items[i].id, items[i].title);
} else {
printf("   %d. %s\n", items[i].id, items[i].title);
}
}
printf("   0. Back/Exit\n");
}
int getMenuChoice(MenuItem *items) {
char input[10];
int choice;
while (1) {
printf("\n   Enter choice: ");
if (fgets(input, sizeof(input), stdin) != NULL) {
char *endptr;
choice = strtol(input, &endptr, 10);
if (endptr != input && (*endptr == '\n' || *endptr == '\0')) {
// Check if choice is valid
int maxChoice = 0;
for (int i = 0; items[i].id != 0; i++) {
if (items[i].id > maxChoice) maxChoice = items[i].id;
}
if (choice == 0 || (choice >= 1 && choice <= maxChoice)) {
return choice;
}
}
}
printf("❌ Invalid choice. Please try again.\n");
}
}
void runMenu(MenuItem *items, const char *title) {
int choice;
do {
displayMenu(items, title);
choice = getMenuChoice(items);
if (choice > 0) {
// Find selected menu item
MenuItem *selected = NULL;
for (int i = 0; items[i].id != 0; i++) {
if (items[i].id == choice) {
selected = &items[i];
break;
}
}
if (selected) {
if (selected->submenu != NULL) {
// Run submenu
runMenu(selected->submenu, selected->title);
} else if (selected->function != NULL) {
// Execute function
clearScreen();
selected->function();
waitForKey();
}
}
}
} while (choice != 0);
}
// ============================================================
// MENU FUNCTION IMPLEMENTATIONS
// ============================================================
void fileMenu() {
// This is just a placeholder - runMenu handles submenus directly
}
void editMenu() {}
void viewMenu() {}
void toolsMenu() {}
void helpMenu() {}
void fileNew() {
printf("πŸ“„ File > New\n");
printf("Creating new file...\n");
// File creation logic
}
void fileOpen() {
printf("πŸ“‚ File > Open\n");
printf("Opening file dialog...\n");
// File open logic
}
void fileSave() {
printf("πŸ’Ύ File > Save\n");
printf("Saving file...\n");
}
void fileSaveAs() {
printf("πŸ“ File > Save As\n");
printf("Save as dialog...\n");
}
void fileExit() {
printf("πŸ‘‹ File > Exit\n");
printf("Exiting program...\n");
exit(0);
}
void editCut() {
printf("βœ‚οΈ Edit > Cut\n");
}
void editCopy() {
printf("πŸ“‹ Edit > Copy\n");
}
void editPaste() {
printf("πŸ“Œ Edit > Paste\n");
}
void editFind() {
printf("πŸ” Edit > Find\n");
}
void viewZoomIn() {
printf("πŸ” View > Zoom In\n");
}
void viewZoomOut() {
printf("πŸ” View > Zoom Out\n");
}
void viewFullScreen() {
printf("πŸ–₯️ View > Full Screen\n");
}
void toolsOptions() {
printf("βš™οΈ Tools > Options\n");
printf("Configuration dialog...\n");
}
void toolsPlugins() {
printf("πŸ”Œ Tools > Plugins\n");
printf("Plugin manager...\n");
}
void helpHelp() {
printf("❓ Help > Help Topics\n");
printf("Help system...\n");
}
void helpAbout() {
printf("ℹ️ Help > About\n");
printf("My Application v1.0\n");
printf("Copyright Β© 2024\n");
}
int main() {
runMenu(mainMenuItems, "MAIN MENU");
return 0;
}

Dynamic Menu with Configuration

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// ============================================================
// DYNAMIC MENU WITH CONFIGURATION
// ============================================================
typedef struct {
int id;
char name[50];
char command[200];
bool enabled;
int permissions;  // 0=user, 1=admin
} MenuOption;
typedef struct {
MenuOption *options;
int count;
int capacity;
bool adminMode;
} MenuSystem;
MenuSystem* createMenuSystem(int initialCapacity) {
MenuSystem *menu = (MenuSystem*)malloc(sizeof(MenuSystem));
menu->options = (MenuOption*)malloc(initialCapacity * sizeof(MenuOption));
menu->count = 0;
menu->capacity = initialCapacity;
menu->adminMode = false;
return menu;
}
void addMenuOption(MenuSystem *menu, const char *name, 
const char *command, bool enabled, int permissions) {
if (menu->count >= menu->capacity) {
menu->capacity *= 2;
menu->options = realloc(menu->options, 
menu->capacity * sizeof(MenuOption));
}
MenuOption *opt = &menu->options[menu->count];
opt->id = menu->count + 1;
strcpy(opt->name, name);
strcpy(opt->command, command);
opt->enabled = enabled;
opt->permissions = permissions;
menu->count++;
}
void loadMenuFromFile(MenuSystem *menu, const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
printf("No menu configuration found. Using defaults.\n");
// Default menu
addMenuOption(menu, "View Files", "ls -la", true, 0);
addMenuOption(menu, "Show Date", "date", true, 0);
addMenuOption(menu, "System Info", "uname -a", true, 0);
addMenuOption(menu, "Disk Usage", "df -h", true, 0);
addMenuOption(menu, "Process List", "ps aux", true, 0);
addMenuOption(menu, "Network Config", "ifconfig", false, 1);
addMenuOption(menu, "User Management", "useradd", false, 1);
return;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
// Format: name|command|enabled|permissions
char name[50];
char command[200];
int enabled, permissions;
if (sscanf(line, "%49[^|]|%199[^|]|%d|%d", 
name, command, &enabled, &permissions) == 4) {
addMenuOption(menu, name, command, enabled, permissions);
}
}
fclose(file);
}
void saveMenuToFile(MenuSystem *menu, const char *filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) return;
for (int i = 0; i < menu->count; i++) {
fprintf(file, "%s|%s|%d|%d\n",
menu->options[i].name,
menu->options[i].command,
menu->options[i].enabled,
menu->options[i].permissions);
}
fclose(file);
}
void executeCommand(const char *command) {
printf("\nπŸ“Ÿ Executing: %s\n", command);
printf("----------------------------------------\n");
int result = system(command);
printf("----------------------------------------\n");
printf("Command exited with code: %d\n", result);
}
void toggleAdminMode(MenuSystem *menu) {
menu->adminMode = !menu->adminMode;
printf("\n%s mode activated.\n", 
menu->adminMode ? "πŸ‘‘ Admin" : "πŸ‘€ User");
}
void configureMenu(MenuSystem *menu) {
printf("\n=== Menu Configuration ===\n");
for (int i = 0; i < menu->count; i++) {
MenuOption *opt = &menu->options[i];
printf("\n%d. %s\n", i + 1, opt->name);
printf("   Command: %s\n", opt->command);
printf("   Enabled: %s\n", opt->enabled ? "βœ…" : "❌");
printf("   Access:  %s\n", opt->permissions == 0 ? "User" : "Admin");
printf("   Options: (E)nable/Disable, (C)hange command, (D)elete\n");
char choice = getchar();
getchar();  // Clear newline
switch (toupper(choice)) {
case 'E':
opt->enabled = !opt->enabled;
break;
case 'C': {
printf("Enter new command: ");
fgets(opt->command, sizeof(opt->command), stdin);
opt->command[strcspn(opt->command, "\n")] = 0;
break;
}
case 'D':
// Shift remaining options
for (int j = i; j < menu->count - 1; j++) {
menu->options[j] = menu->options[j + 1];
}
menu->count--;
i--;  // Adjust index
break;
}
}
}
void displayMenu(MenuSystem *menu) {
clearScreen();
printHeader(menu->adminMode ? "ADMIN MENU" : "USER MENU");
printf("\n");
printf("   Available options:\n");
printf("   =================\n\n");
for (int i = 0; i < menu->count; i++) {
MenuOption *opt = &menu->options[i];
// Skip if not enabled or wrong permission
if (!opt->enabled) continue;
if (opt->permissions == 1 && !menu->adminMode) continue;
printf("   %2d. %s\n", opt->id, opt->name);
}
printf("\n");
printf("    A. %s mode\n", menu->adminMode ? "πŸ‘€ Switch to User" : "πŸ‘‘ Switch to Admin");
if (menu->adminMode) {
printf("    C. Configure Menu\n");
}
printf("    0. Exit\n");
}
int main() {
MenuSystem *menu = createMenuSystem(10);
// Load menu from file or create default
loadMenuFromFile(menu, "menu.cfg");
int choice;
char input[10];
do {
displayMenu(menu);
printf("\n   Enter choice: ");
if (fgets(input, sizeof(input), stdin) == NULL) break;
// Remove newline
input[strcspn(input, "\n")] = 0;
if (strcasecmp(input, "A") == 0) {
toggleAdminMode(menu);
waitForKey();
continue;
}
if (menu->adminMode && strcasecmp(input, "C") == 0) {
configureMenu(menu);
saveMenuToFile(menu, "menu.cfg");
waitForKey();
continue;
}
if (strcmp(input, "0") == 0) {
printf("\nπŸ‘‹ Goodbye!\n");
break;
}
// Try to parse as number
char *endptr;
choice = strtol(input, &endptr, 10);
if (endptr != input && *endptr == '\0') {
// Find and execute selected option
int executed = 0;
for (int i = 0; i < menu->count; i++) {
if (menu->options[i].id == choice && 
menu->options[i].enabled &&
(menu->options[i].permissions == 0 || menu->adminMode)) {
executeCommand(menu->options[i].command);
executed = 1;
break;
}
}
if (!executed) {
printf("❌ Invalid option or insufficient permissions.\n");
}
waitForKey();
} else {
printf("❌ Invalid input.\n");
waitForKey();
}
} while (1);
free(menu->options);
free(menu);
return 0;
}

Colorful Menu with ANSI Codes

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ============================================================
// COLORFUL MENU WITH ANSI CODES
// ============================================================
// ANSI color codes
#define RESET   "\033[0m"
#define BLACK   "\033[30m"
#define RED     "\033[31m"
#define GREEN   "\033[32m"
#define YELLOW  "\033[33m"
#define BLUE    "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN    "\033[36m"
#define WHITE   "\033[37m"
#define BOLD    "\033[1m"
#define UNDERLINE "\033[4m"
// Background colors
#define BG_BLACK   "\033[40m"
#define BG_RED     "\033[41m"
#define BG_GREEN   "\033[42m"
#define BG_YELLOW  "\033[43m"
#define BG_BLUE    "\033[44m"
#define BG_MAGENTA "\033[45m"
#define BG_CYAN    "\033[46m"
#define BG_WHITE   "\033[47m"
void printColored(const char *color, const char *text) {
printf("%s%s%s", color, text, RESET);
}
void printBanner() {
printf("%s", CYAN);
printf("╔════════════════════════════════════════╗\n");
printf("β•‘         COMMAND CENTER v1.0            β•‘\n");
printf("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n");
printf("%s", RESET);
}
void printMenuOption(int number, const char *description, const char *color) {
printf("  %s[%d]%s %s", 
BOLD, number, RESET, color);
printf("%-30s%s\n", description, RESET);
}
void showSystemStatus() {
printf("\n%sπŸ“Š System Status:%s\n", BOLD, RESET);
printf("  CPU:  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘ 80%%\n");
printf("  MEM:  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘ 70%%\n");
printf("  DISK: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘ 90%%\n");
}
int main() {
int choice;
do {
clearScreen();
printBanner();
showSystemStatus();
printf("\n%sMAIN MENU%s\n", BOLD, RESET);
printf("════════════════════════════════════════\n\n");
printMenuOption(1, "System Information", GREEN);
printMenuOption(2, "Process Manager", YELLOW);
printMenuOption(3, "Disk Analyzer", CYAN);
printMenuOption(4, "Network Tools", MAGENTA);
printMenuOption(5, "User Management", BLUE);
printMenuOption(6, "Security Scan", RED);
printMenuOption(0, "Exit", WHITE);
printf("\n%sEnter choice:%s ", BOLD, RESET);
if (scanf("%d", &choice) != 1) {
while (getchar() != '\n');
choice = -1;
}
getchar();  // Clear newline
clearScreen();
switch (choice) {
case 1:
printf("%sπŸ“‹ System Information%s\n", BOLD, RESET);
printf("OS: %s\n", "Linux 5.10");
printf("Kernel: %s\n", "5.10.0-generic");
printf("Uptime: %s\n", "2 days, 4 hours");
printf("Users: %d\n", 3);
printf("Load average: 0.45, 0.62, 0.78\n");
break;
case 2:
printf("%sπŸ“Š Process Manager%s\n", BOLD, RESET);
printf("%-10s %-20s %-10s %-10s\n", 
"PID", "NAME", "CPU%", "MEM%");
printf("----------------------------------------\n");
printf("%-10d %-20s %-10.1f %-10.1f\n", 1234, "systemd", 0.5, 1.2);
printf("%-10d %-20s %-10.1f %-10.1f\n", 5678, "firefox", 15.3, 12.7);
printf("%-10d %-20s %-10.1f %-10.1f\n", 9012, "terminal", 2.1, 3.4);
break;
case 3:
printf("%sπŸ’Ύ Disk Analyzer%s\n", BOLD, RESET);
printf("%-20s %-15s %-10s\n", "Mount", "Used", "Available");
printf("----------------------------------------\n");
printf("%-20s %-15s %-10s\n", "/", "45G (60%)", "30G");
printf("%-20s %-15s %-10s\n", "/home", "120G (45%)", "150G");
printf("%-20s %-15s %-10s\n", "/var", "8G (35%)", "15G");
break;
case 4:
printf("%s🌐 Network Tools%s\n", BOLD, RESET);
printf("Interface: eth0\n");
printf("IP Address: 192.168.1.100\n");
printf("MAC Address: 00:1A:2B:3C:4D:5E\n");
printf("RX bytes: 1.2 GB\n");
printf("TX bytes: 850 MB\n");
break;
case 5:
printf("%sπŸ‘₯ User Management%s\n", BOLD, RESET);
printf("Active users:\n");
printf("  β€’ alice (admin)\n");
printf("  β€’ bob (user)\n");
printf("  β€’ charlie (user)\n");
break;
case 6:
printf("%sπŸ”’ Security Scan%s\n", BOLD, RESET);
printf("βœ“ Firewall active\n");
printf("βœ“ SELinux enforcing\n");
printf("⚠  Failed logins: 3\n");
printf("βœ“ All services secure\n");
break;
case 0:
printf("%sπŸ‘‹ Goodbye!%s\n", BOLD, RESET);
break;
default:
printf("%s❌ Invalid choice%s\n", RED, RESET);
}
if (choice != 0) {
printf("\n%sPress Enter to continue...%s", BOLD, RESET);
while (getchar() != '\n');
}
} while (choice != 0);
return 0;
}

Menu with Tab Completion

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// ============================================================
// MENU WITH TAB COMPLETION
// ============================================================
#ifdef _WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
int getch() {
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
#endif
typedef struct {
char *commands[50];
int count;
} CommandList;
CommandList commands = {
.commands = {
"help", "exit", "clear", "list", "add", 
"remove", "edit", "search", "sort", "filter",
"save", "load", "export", "import", "config",
"status", "info", "version", "about", "quit"
},
.count = 20
};
void displayPrompt() {
printf("\n%s>%s ", GREEN, RESET);
fflush(stdout);
}
void clearInput(char *buffer, int *pos) {
memset(buffer, 0, 100);
*pos = 0;
}
void findCompletions(const char *partial, char matches[][50], int *matchCount) {
*matchCount = 0;
int len = strlen(partial);
for (int i = 0; i < commands.count; i++) {
if (strncmp(partial, commands.commands[i], len) == 0) {
strcpy(matches[*matchCount], commands.commands[i]);
(*matchCount)++;
}
}
}
void showCompletions(char matches[][50], int matchCount) {
printf("\n");
for (int i = 0; i < matchCount; i++) {
printf("  %s", matches[i]);
if ((i + 1) % 5 == 0) printf("\n");
}
if (matchCount > 0) printf("\n");
displayPrompt();
}
void processCommand(const char *cmd) {
if (strcmp(cmd, "help") == 0) {
printf("\nπŸ“– Available commands:\n");
for (int i = 0; i < commands.count; i++) {
printf("  %s", commands.commands[i]);
if ((i + 1) % 5 == 0) printf("\n");
}
printf("\n");
}
else if (strcmp(cmd, "clear") == 0) {
clearScreen();
}
else if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "quit") == 0) {
printf("\nπŸ‘‹ Goodbye!\n");
exit(0);
}
else if (strcmp(cmd, "list") == 0) {
printf("\nπŸ“‹ Listing items...\n");
for (int i = 1; i <= 5; i++) {
printf("  Item %d\n", i);
}
}
else if (strcmp(cmd, "status") == 0) {
printf("\nπŸ“Š System status: OK\n");
}
else if (strcmp(cmd, "info") == 0) {
printf("\nℹ️  Command Line Interface v1.0\n");
}
else {
printf("\n❌ Unknown command: %s\n", cmd);
}
}
int main() {
char buffer[100];
int pos = 0;
int ch;
clearScreen();
printHeader("TAB-COMPLETION MENU");
printf("\nType a command and press TAB for completion.\n");
printf("Type 'help' for available commands.\n");
while (1) {
displayPrompt();
pos = 0;
memset(buffer, 0, sizeof(buffer));
while (1) {
ch = getch();
if (ch == '\n') {  // Enter
printf("\n");
if (pos > 0) {
processCommand(buffer);
}
break;
}
else if (ch == '\t') {  // Tab
if (pos > 0) {
char matches[10][50];
int matchCount;
findCompletions(buffer, matches, &matchCount);
if (matchCount == 1) {
// Unique match - complete it
strcpy(buffer, matches[0]);
pos = strlen(buffer);
printf("\r%s>%s %s", GREEN, RESET, buffer);
fflush(stdout);
}
else if (matchCount > 1) {
// Multiple matches - show them
showCompletions(matches, matchCount);
}
}
}
else if (ch == 127 || ch == 8) {  // Backspace
if (pos > 0) {
pos--;
buffer[pos] = '\0';
printf("\b \b");
fflush(stdout);
}
}
else if (isprint(ch)) {  // Printable character
if (pos < sizeof(buffer) - 1) {
buffer[pos++] = ch;
buffer[pos] = '\0';
putchar(ch);
fflush(stdout);
}
}
}
}
return 0;
}

Menu Design Patterns

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// ============================================================
// MENU DESIGN PATTERNS
// ============================================================
// 1. Wizard Pattern (step-by-step)
void wizardPattern() {
printf("=== Installation Wizard ===\n\n");
char name[50];
int option;
bool confirm;
// Step 1
printf("Step 1/4: Enter your name: ");
fgets(name, sizeof(name), stdin);
name[strcspn(name, "\n")] = 0;
// Step 2
printf("\nStep 2/4: Choose installation type:\n");
printf("  1. Typical\n");
printf("  2. Custom\n");
printf("  3. Minimal\n");
option = getValidInt("Choice", 1, 3);
// Step 3
printf("\nStep 3/4: Select components:\n");
printf("  [x] Core files\n");
printf("  [ ] Documentation\n");
printf("  [ ] Development headers\n");
// Step 4
printf("\nStep 4/4: Confirm installation\n");
printf("  Name: %s\n", name);
printf("  Type: %s\n", 
option == 1 ? "Typical" : option == 2 ? "Custom" : "Minimal");
confirm = getYesNo("Proceed with installation?");
if (confirm) {
printf("\nβœ… Installation complete!\n");
} else {
printf("\n❌ Installation cancelled.\n");
}
}
// 2. Dashboard Pattern (information + actions)
void dashboardPattern() {
typedef struct {
char name[20];
int value;
int max;
char status[10];
} Metric;
Metric metrics[] = {
{"CPU", 45, 100, "OK"},
{"MEM", 2, 8, "OK"},
{"DISK", 120, 250, "OK"},
{"NET", 500, 1000, "WARN"}
};
while (1) {
clearScreen();
printHeader("SYSTEM DASHBOARD");
// Metrics display
printf("\n%sSystem Metrics:%s\n", BOLD, RESET);
printf("β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n");
printf("β”‚ Metric   β”‚ Value      β”‚ Max        β”‚ Status   β”‚\n");
printf("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€\n");
for (int i = 0; i < 4; i++) {
printf("β”‚ %-8s β”‚ %-10d β”‚ %-10d β”‚ %s%-8s%s β”‚\n",
metrics[i].name,
metrics[i].value,
metrics[i].max,
strcmp(metrics[i].status, "OK") == 0 ? GREEN : RED,
metrics[i].status,
RESET);
}
printf("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜\n");
// Quick actions
printf("\n%sQuick Actions:%s\n", BOLD, RESET);
printf("  1. Refresh Metrics\n");
printf("  2. Clear Warnings\n");
printf("  3. System Info\n");
printf("  4. Settings\n");
printf("  0. Back\n");
int choice = getValidInt("\nAction", 0, 4);
switch (choice) {
case 1:
// Refresh logic
for (int i = 0; i < 4; i++) {
metrics[i].value = rand() % metrics[i].max;
}
break;
case 2:
strcpy(metrics[3].status, "OK");
break;
case 3:
printf("\nSystem Information:\n");
printf("  Hostname: server-01\n");
printf("  OS: Linux 5.10\n");
printf("  Uptime: 15 days\n");
waitForKey();
break;
case 4:
printf("\nSettings (not implemented)\n");
waitForKey();
break;
case 0:
return;
}
}
}
// 3. TUI (Text User Interface) Pattern
void tuiPattern() {
int selected = 0;
const char *options[] = {
"New File",
"Open File",
"Save File",
"Save As...",
"Print...",
"Exit"
};
int optionCount = 6;
int ch;
while (1) {
clearScreen();
printHeader("TEXT EDITOR");
printf("\nUse ↑/↓ arrows to navigate, Enter to select\n\n");
for (int i = 0; i < optionCount; i++) {
if (i == selected) {
printf("%s→ %s%s\n", GREEN, options[i], RESET);
} else {
printf("  %s\n", options[i]);
}
}
ch = getch();
if (ch == 27) {  // ESC sequence
getch();  // Skip [
ch = getch();
if (ch == 'A') {  // Up arrow
selected = (selected - 1 + optionCount) % optionCount;
} else if (ch == 'B') {  // Down arrow
selected = (selected + 1) % optionCount;
}
} else if (ch == '\n') {  // Enter
printf("\nSelected: %s\n", options[selected]);
waitForKey();
if (selected == optionCount - 1) break;
}
}
}
// 4. Multi-page Menu Pattern
void multiPageMenu() {
const char *items[] = {
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5",
"Item 6", "Item 7", "Item 8", "Item 9", "Item 10",
"Item 11", "Item 12", "Item 13", "Item 14", "Item 15"
};
int totalItems = 15;
int itemsPerPage = 5;
int currentPage = 0;
int totalPages = (totalItems + itemsPerPage - 1) / itemsPerPage;
while (1) {
clearScreen();
printHeader("MULTI-PAGE MENU");
printf("\nPage %d of %d\n", currentPage + 1, totalPages);
printf("═══════════════════════════════\n\n");
int start = currentPage * itemsPerPage;
int end = start + itemsPerPage;
if (end > totalItems) end = totalItems;
for (int i = start; i < end; i++) {
printf("  %2d. %s\n", i + 1, items[i]);
}
printf("\n");
if (currentPage > 0) {
printf("  ← (P)revious page\n");
}
if (currentPage < totalPages - 1) {
printf("  β†’ (N)ext page\n");
}
printf("  (0) Exit\n");
char input[10];
printf("\nChoice (item number or command): ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = 0;
if (strcasecmp(input, "P") == 0 && currentPage > 0) {
currentPage--;
} else if (strcasecmp(input, "N") == 0 && currentPage < totalPages - 1) {
currentPage++;
} else if (strcmp(input, "0") == 0) {
break;
} else {
int item = atoi(input);
if (item >= 1 && item <= totalItems) {
printf("\nSelected: %s\n", items[item - 1]);
waitForKey();
}
}
}
}
int main() {
int choice;
do {
clearScreen();
printHeader("MENU DESIGN PATTERNS");
printf("\n");
printf("  1. Wizard Pattern\n");
printf("  2. Dashboard Pattern\n");
printf("  3. TUI Pattern\n");
printf("  4. Multi-page Pattern\n");
printf("  0. Exit\n");
choice = getValidInt("\nSelect pattern", 0, 4);
switch (choice) {
case 1: wizardPattern(); break;
case 2: dashboardPattern(); break;
case 3: tuiPattern(); break;
case 4: multiPageMenu(); break;
case 0: break;
}
} while (choice != 0);
return 0;
}

Best Practices Summary

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// MENU BEST PRACTICES
// ============================================================
void menuBestPractices() {
printf("=== Command Line Menu Best Practices ===\n\n");
printf("1. DESIGN PRINCIPLES\n");
printf("   βœ“ Keep menus shallow (max 3-4 levels)\n");
printf("   βœ“ Use consistent navigation (0=back, 0=exit at top)\n");
printf("   βœ“ Show current context/location\n");
printf("   βœ“ Provide clear exit options\n\n");
printf("2. INPUT HANDLING\n");
printf("   βœ“ Always validate user input\n");
printf("   βœ“ Clear input buffer after scanf\n");
printf("   βœ“ Handle both upper/lower case\n");
printf("   βœ“ Provide default values when appropriate\n\n");
printf("3. FEEDBACK\n");
printf("   βœ“ Show confirmation for destructive actions\n");
printf("   βœ“ Display success/error messages\n");
printf("   βœ“ Wait for user to read messages\n");
printf("   βœ“ Use colors consistently (if used)\n\n");
printf("4. NAVIGATION\n");
printf("   βœ“ Indicate submenus (> symbol)\n");
printf("   βœ“ Show current menu title\n");
printf("   βœ“ Provide breadcrumbs for deep menus\n");
printf("   βœ“ Allow quick exit (q, quit, 0)\n\n");
printf("5. ROBUSTNESS\n");
printf("   βœ“ Handle Ctrl+C gracefully\n");
printf("   βœ“ Save state before exit\n");
printf("   βœ“ Validate all file operations\n");
printf("   βœ“ Test edge cases (empty input, long input)\n\n");
printf("6. ACCESSIBILITY\n");
printf("   βœ“ Support both numbered and named commands\n");
printf("   βœ“ Provide help option\n");
printf("   βœ“ Show available commands\n");
printf("   βœ“ Consider screen readers (avoid flashing)\n");
}
int main() {
menuBestPractices();
return 0;
}

Conclusion

Command line menus are essential for creating interactive C applications. Key takeaways:

  1. Basic Structure:
  • Clear screen, display options, get input, process choice
  • Loop until exit condition
  1. Input Validation:
  • Always validate user input
  • Handle invalid choices gracefully
  • Clear input buffer to prevent issues
  1. Navigation:
  • Consistent navigation patterns
  • Indicate current location
  • Provide easy way to go back/exit
  1. Advanced Features:
  • Hierarchical submenus
  • Color and formatting
  • Tab completion
  • Arrow key navigation
  • Configuration persistence
  1. Design Patterns:
  • Wizard (step-by-step)
  • Dashboard (information + actions)
  • TUI (text user interface)
  • Multi-page menus
  1. Best Practices:
  • Clear feedback
  • Confirmation for destructive actions
  • Error handling
  • Consistent interface
  • Accessibility considerations

Command line menus provide a lightweight, portable, and effective user interface for C applications, from simple utilities to complex system management tools.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper