c

examples

examples.c🔧
/**
 * Command Line Arguments in C - Examples
 * 
 * This file contains comprehensive examples demonstrating
 * how to work with command line arguments in C programs.
 * 
 * Compilation:
 *   gcc -Wall -o examples examples.c
 * 
 * For getopt_long examples:
 *   gcc -Wall -D_GNU_SOURCE -o examples examples.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>  /* For getopt */
#include <getopt.h>  /* For getopt_long */

/* ============================================================
 * EXAMPLE 1: Basic argc and argv
 * ============================================================ */

/**
 * Demonstrates the basic structure of command line arguments
 * 
 * Usage: ./program arg1 arg2 arg3
 */
void example1_basic_argc_argv(int argc, char *argv[]) {
    printf("=== Example 1: Basic argc and argv ===\n\n");
    
    /* Print argument count */
    printf("Number of arguments (argc): %d\n", argc);
    printf("\n");
    
    /* Print program name */
    printf("Program name (argv[0]): %s\n", argv[0]);
    printf("\n");
    
    /* Print all arguments using array notation */
    printf("All arguments (using array notation):\n");
    for (int i = 0; i < argc; i++) {
        printf("  argv[%d] = \"%s\"\n", i, argv[i]);
    }
    printf("\n");
    
    /* Verify NULL terminator */
    printf("argv[%d] is %s (should be NULL)\n", 
           argc, argv[argc] == NULL ? "NULL" : "NOT NULL");
    printf("\n");
}

/* ============================================================
 * EXAMPLE 2: Using Pointer Notation
 * ============================================================ */

/**
 * Demonstrates accessing arguments using pointer arithmetic
 */
void example2_pointer_notation(int argc, char **argv) {
    printf("=== Example 2: Pointer Notation ===\n\n");
    
    /* Method 1: Using pointer arithmetic */
    printf("Using pointer arithmetic:\n");
    char **ptr = argv;
    int index = 0;
    while (*ptr != NULL) {
        printf("  *(argv + %d) = \"%s\"\n", index, *ptr);
        ptr++;
        index++;
    }
    printf("\n");
    
    /* Method 2: Direct pointer increments */
    printf("Using pointer increments:\n");
    for (char **p = argv; *p; p++) {
        printf("  %s\n", *p);
    }
    printf("\n");
    
    /* Accessing individual characters */
    if (argc > 1) {
        printf("First argument character by character:\n");
        char *arg = argv[1];
        for (int i = 0; arg[i] != '\0'; i++) {
            printf("  argv[1][%d] = '%c'\n", i, arg[i]);
        }
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 3: Converting String Arguments
 * ============================================================ */

/**
 * Demonstrates converting command line strings to numbers
 * 
 * Usage: ./program 42 3.14 -100
 */
void example3_string_conversion(int argc, char *argv[]) {
    printf("=== Example 3: String Conversion ===\n\n");
    
    /* atoi - simple integer conversion (no error checking) */
    printf("Using atoi() - Simple but unsafe:\n");
    if (argc > 1) {
        int val = atoi(argv[1]);
        printf("  atoi(\"%s\") = %d\n", argv[1], val);
        printf("  Note: atoi returns 0 for invalid input\n");
    }
    printf("\n");
    
    /* strtol - integer conversion with error checking */
    printf("Using strtol() - Safe with error checking:\n");
    if (argc > 1) {
        char *endptr;
        errno = 0;
        long val = strtol(argv[1], &endptr, 10);
        
        if (errno == ERANGE) {
            printf("  Error: Number out of range\n");
        } else if (endptr == argv[1]) {
            printf("  Error: No digits found\n");
        } else if (*endptr != '\0') {
            printf("  Warning: Extra characters after number: \"%s\"\n", endptr);
            printf("  Parsed value: %ld\n", val);
        } else {
            printf("  strtol(\"%s\", ..., 10) = %ld\n", argv[1], val);
        }
    }
    printf("\n");
    
    /* strtod - floating point conversion */
    printf("Using strtod() - Double conversion:\n");
    if (argc > 2) {
        char *endptr;
        errno = 0;
        double val = strtod(argv[2], &endptr);
        
        if (errno == ERANGE) {
            printf("  Error: Number out of range\n");
        } else if (*endptr != '\0') {
            printf("  Warning: Extra characters: \"%s\"\n", endptr);
        } else {
            printf("  strtod(\"%s\", ...) = %f\n", argv[2], val);
        }
    } else {
        printf("  (Need second argument for this demo)\n");
    }
    printf("\n");
    
    /* Hexadecimal conversion */
    printf("Hexadecimal conversion:\n");
    if (argc > 1) {
        char *endptr;
        long val = strtol(argv[1], &endptr, 16);
        printf("  As hex: strtol(\"%s\", ..., 16) = %ld (0x%lx)\n", 
               argv[1], val, val);
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 4: Simple Calculator
 * ============================================================ */

/**
 * A simple calculator using command line arguments
 * 
 * Usage: ./program 10 + 5
 *        ./program 20 - 3
 *        ./program 7 x 6  (use 'x' for multiply, shell may interpret '*')
 *        ./program 100 / 5
 */
void example4_calculator(int argc, char *argv[]) {
    printf("=== Example 4: Simple Calculator ===\n\n");
    
    if (argc != 4) {
        printf("Usage: program <num1> <operator> <num2>\n");
        printf("Operators: +, -, x (multiply), /\n");
        printf("Example: program 10 + 5\n");
        return;
    }
    
    double num1 = atof(argv[1]);
    double num2 = atof(argv[3]);
    char op = argv[2][0];
    double result;
    int valid = 1;
    
    switch (op) {
        case '+':
            result = num1 + num2;
            break;
        case '-':
            result = num1 - num2;
            break;
        case 'x':
        case 'X':
        case '*':
            result = num1 * num2;
            op = '*';  /* Normalize for display */
            break;
        case '/':
            if (num2 == 0) {
                printf("Error: Division by zero!\n");
                valid = 0;
            } else {
                result = num1 / num2;
            }
            break;
        case '%':
            if (num2 == 0) {
                printf("Error: Modulo by zero!\n");
                valid = 0;
            } else {
                result = (int)num1 % (int)num2;
            }
            break;
        default:
            printf("Error: Unknown operator '%c'\n", op);
            valid = 0;
    }
    
    if (valid) {
        printf("%.2f %c %.2f = %.2f\n", num1, op, num2, result);
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 5: Manual Argument Parsing
 * ============================================================ */

/**
 * Demonstrates manual parsing of command line options
 * 
 * Usage: ./program -v -f filename -n 10 file1 file2
 */
void example5_manual_parsing(int argc, char *argv[]) {
    printf("=== Example 5: Manual Argument Parsing ===\n\n");
    
    /* Configuration */
    int verbose = 0;
    int number = 0;
    char *filename = NULL;
    char *files[10];
    int file_count = 0;
    
    /* Parse arguments */
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
            verbose = 1;
        }
        else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            printf("Usage: %s [options] [files...]\n", argv[0]);
            printf("Options:\n");
            printf("  -v, --verbose   Enable verbose mode\n");
            printf("  -f, --file      Specify a file\n");
            printf("  -n, --number    Specify a number\n");
            printf("  -h, --help      Show this help\n");
            return;
        }
        else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--file") == 0) {
            if (i + 1 < argc) {
                filename = argv[++i];
            } else {
                printf("Error: -f requires a filename\n");
                return;
            }
        }
        else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) {
            if (i + 1 < argc) {
                number = atoi(argv[++i]);
            } else {
                printf("Error: -n requires a number\n");
                return;
            }
        }
        else if (argv[i][0] == '-') {
            printf("Unknown option: %s\n", argv[i]);
        }
        else {
            /* Positional argument (file) */
            if (file_count < 10) {
                files[file_count++] = argv[i];
            }
        }
    }
    
    /* Display parsed options */
    printf("Parsed options:\n");
    printf("  Verbose: %s\n", verbose ? "yes" : "no");
    printf("  Number: %d\n", number);
    printf("  Filename: %s\n", filename ? filename : "(none)");
    printf("  File count: %d\n", file_count);
    
    if (file_count > 0) {
        printf("  Files:\n");
        for (int i = 0; i < file_count; i++) {
            printf("    [%d] %s\n", i, files[i]);
        }
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 6: Using getopt()
 * ============================================================ */

/**
 * Demonstrates POSIX getopt() function
 * 
 * Usage: ./program -v -d -f filename -c 5
 */
void example6_getopt(int argc, char *argv[]) {
    printf("=== Example 6: Using getopt() ===\n\n");
    
    /* Reset getopt for reuse */
    optind = 1;
    opterr = 1;
    
    int opt;
    int verbose = 0;
    int debug = 0;
    char *filename = NULL;
    int count = 1;
    
    /*
     * Option string format:
     * - Single letter: flag (no argument)
     * - Letter followed by ':': requires argument
     * - Letter followed by '::': optional argument (GNU extension)
     */
    while ((opt = getopt(argc, argv, "vdhf:c:")) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                printf("  Found -v (verbose)\n");
                break;
            case 'd':
                debug = 1;
                printf("  Found -d (debug)\n");
                break;
            case 'f':
                filename = optarg;
                printf("  Found -f with argument: %s\n", optarg);
                break;
            case 'c':
                count = atoi(optarg);
                printf("  Found -c with argument: %s\n", optarg);
                break;
            case 'h':
                printf("Usage: %s [-v] [-d] [-f file] [-c count]\n", argv[0]);
                return;
            case '?':
                printf("  Unknown option or missing argument\n");
                break;
        }
    }
    
    printf("\nParsed values:\n");
    printf("  Verbose: %d\n", verbose);
    printf("  Debug: %d\n", debug);
    printf("  Filename: %s\n", filename ? filename : "(none)");
    printf("  Count: %d\n", count);
    
    /* Process remaining (non-option) arguments */
    printf("\nRemaining arguments:\n");
    for (int i = optind; i < argc; i++) {
        printf("  argv[%d] = %s\n", i, argv[i]);
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 7: Using getopt_long()
 * ============================================================ */

/**
 * Demonstrates GNU getopt_long() for long options
 * 
 * Usage: ./program --verbose --config=file.cfg --port 8080
 */
void example7_getopt_long(int argc, char *argv[]) {
    printf("=== Example 7: Using getopt_long() ===\n\n");
    
    /* Reset getopt for reuse */
    optind = 1;
    opterr = 1;
    
    int opt;
    int verbose = 0;
    int debug = 0;
    char *config = NULL;
    int port = 8080;
    
    /* Define long options */
    static struct option long_options[] = {
        {"verbose", no_argument,       NULL, 'v'},
        {"debug",   no_argument,       NULL, 'd'},
        {"config",  required_argument, NULL, 'c'},
        {"port",    required_argument, NULL, 'p'},
        {"help",    no_argument,       NULL, 'h'},
        {"version", no_argument,       NULL, 'V'},
        {NULL,      0,                 NULL, 0}
    };
    
    int option_index = 0;
    
    while ((opt = getopt_long(argc, argv, "vdc:p:hV",
                               long_options, &option_index)) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                printf("  Found --verbose/-v\n");
                break;
            case 'd':
                debug = 1;
                printf("  Found --debug/-d\n");
                break;
            case 'c':
                config = optarg;
                printf("  Found --config/-c: %s\n", optarg);
                break;
            case 'p':
                port = atoi(optarg);
                printf("  Found --port/-p: %s\n", optarg);
                break;
            case 'h':
                printf("Usage: %s [OPTIONS]\n\n", argv[0]);
                printf("Options:\n");
                printf("  -v, --verbose     Verbose output\n");
                printf("  -d, --debug       Debug mode\n");
                printf("  -c, --config=FILE Config file\n");
                printf("  -p, --port=PORT   Port number\n");
                printf("  -h, --help        This help\n");
                printf("  -V, --version     Version info\n");
                return;
            case 'V':
                printf("Version 1.0.0\n");
                return;
            case '?':
                break;
        }
    }
    
    printf("\nConfiguration:\n");
    printf("  Verbose: %s\n", verbose ? "enabled" : "disabled");
    printf("  Debug: %s\n", debug ? "enabled" : "disabled");
    printf("  Config: %s\n", config ? config : "(default)");
    printf("  Port: %d\n", port);
    printf("\n");
}

/* ============================================================
 * EXAMPLE 8: Long Options with Flag
 * ============================================================ */

/**
 * Demonstrates using flag pointers with getopt_long
 */
void example8_flag_options(int argc, char *argv[]) {
    printf("=== Example 8: Long Options with Flags ===\n\n");
    
    /* Reset getopt */
    optind = 1;
    
    /* Flag variables that will be set directly */
    static int verbose_flag = 0;
    static int debug_flag = 0;
    static int quiet_flag = 0;
    
    static struct option long_options[] = {
        /* These set flags directly (return 0) */
        {"verbose", no_argument, &verbose_flag, 1},
        {"debug",   no_argument, &debug_flag,   1},
        {"quiet",   no_argument, &quiet_flag,   1},
        /* These return the val character */
        {"output",  required_argument, NULL, 'o'},
        {"help",    no_argument,       NULL, 'h'},
        {NULL,      0,                 NULL, 0}
    };
    
    char *output = NULL;
    int opt;
    int option_index = 0;
    
    while ((opt = getopt_long(argc, argv, "o:h",
                               long_options, &option_index)) != -1) {
        switch (opt) {
            case 0:
                /* Flag was set automatically */
                printf("  Option '%s' set flag\n",
                       long_options[option_index].name);
                break;
            case 'o':
                output = optarg;
                printf("  Output file: %s\n", optarg);
                break;
            case 'h':
                printf("Help: Use --verbose, --debug, --quiet, --output=FILE\n");
                return;
            case '?':
                break;
        }
    }
    
    printf("\nFlag values:\n");
    printf("  verbose_flag = %d\n", verbose_flag);
    printf("  debug_flag = %d\n", debug_flag);
    printf("  quiet_flag = %d\n", quiet_flag);
    printf("  output = %s\n", output ? output : "(none)");
    
    /* Reset flags for next call */
    verbose_flag = debug_flag = quiet_flag = 0;
    printf("\n");
}

/* ============================================================
 * EXAMPLE 9: Error Handling with getopt
 * ============================================================ */

/**
 * Demonstrates proper error handling with getopt
 */
void example9_error_handling(int argc, char *argv[]) {
    printf("=== Example 9: Error Handling ===\n\n");
    
    /* Reset getopt */
    optind = 1;
    opterr = 0;  /* Suppress automatic error messages */
    
    int opt;
    
    /* Leading ':' makes getopt return ':' for missing argument */
    while ((opt = getopt(argc, argv, ":a:bc:")) != -1) {
        switch (opt) {
            case 'a':
                printf("  Option -a with value: %s\n", optarg);
                break;
            case 'b':
                printf("  Option -b\n");
                break;
            case 'c':
                printf("  Option -c with value: %s\n", optarg);
                break;
            case ':':
                /* Missing required argument */
                fprintf(stderr, "  Error: Option '-%c' requires an argument\n", 
                        optopt);
                break;
            case '?':
                /* Unknown option */
                if (isprint(optopt)) {
                    fprintf(stderr, "  Error: Unknown option '-%c'\n", optopt);
                } else {
                    fprintf(stderr, "  Error: Unknown option character '\\x%x'\n",
                            optopt);
                }
                break;
        }
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 10: Environment Variables
 * ============================================================ */

/**
 * Demonstrates accessing environment variables
 */
void example10_environment_variables(void) {
    printf("=== Example 10: Environment Variables ===\n\n");
    
    /* Get specific environment variables */
    printf("Common environment variables:\n");
    
    const char *vars[] = {"HOME", "USER", "PATH", "SHELL", "PWD", 
                          "TERM", "LANG", "EDITOR", NULL};
    
    for (int i = 0; vars[i] != NULL; i++) {
        char *value = getenv(vars[i]);
        if (value) {
            if (strlen(value) > 60) {
                /* Truncate long values for display */
                printf("  %s = %.57s...\n", vars[i], value);
            } else {
                printf("  %s = %s\n", vars[i], value);
            }
        } else {
            printf("  %s = (not set)\n", vars[i]);
        }
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 11: Complete CLI Application Structure
 * ============================================================ */

typedef struct {
    int verbose;
    int force;
    int recursive;
    char *input_file;
    char *output_file;
    int count;
} AppConfig;

void init_config(AppConfig *config) {
    config->verbose = 0;
    config->force = 0;
    config->recursive = 0;
    config->input_file = NULL;
    config->output_file = NULL;
    config->count = 1;
}

void print_app_usage(const char *program) {
    printf("Usage: %s [OPTIONS] INPUT [OUTPUT]\n\n", program);
    printf("A demo command line application.\n\n");
    printf("Options:\n");
    printf("  -v, --verbose     Enable verbose output\n");
    printf("  -f, --force       Force operation\n");
    printf("  -r, --recursive   Recursive mode\n");
    printf("  -c, --count=N     Set count value\n");
    printf("  -o, --output=FILE Output file\n");
    printf("  -h, --help        Show this help\n");
    printf("\nExamples:\n");
    printf("  %s -v input.txt\n", program);
    printf("  %s --count=5 --output=out.txt input.txt\n", program);
}

int parse_config(int argc, char *argv[], AppConfig *config) {
    /* Reset getopt */
    optind = 1;
    
    static struct option long_options[] = {
        {"verbose",   no_argument,       NULL, 'v'},
        {"force",     no_argument,       NULL, 'f'},
        {"recursive", no_argument,       NULL, 'r'},
        {"count",     required_argument, NULL, 'c'},
        {"output",    required_argument, NULL, 'o'},
        {"help",      no_argument,       NULL, 'h'},
        {NULL,        0,                 NULL, 0}
    };
    
    int opt;
    
    while ((opt = getopt_long(argc, argv, "vfrc:o:h",
                               long_options, NULL)) != -1) {
        switch (opt) {
            case 'v':
                config->verbose = 1;
                break;
            case 'f':
                config->force = 1;
                break;
            case 'r':
                config->recursive = 1;
                break;
            case 'c':
                config->count = atoi(optarg);
                break;
            case 'o':
                config->output_file = optarg;
                break;
            case 'h':
                return -1;  /* Signal to show help */
            case '?':
                return -2;  /* Error */
        }
    }
    
    /* Get positional arguments */
    if (optind < argc) {
        config->input_file = argv[optind++];
    }
    if (optind < argc && config->output_file == NULL) {
        config->output_file = argv[optind++];
    }
    
    return 0;
}

void example11_complete_application(int argc, char *argv[]) {
    printf("=== Example 11: Complete CLI Application ===\n\n");
    
    AppConfig config;
    init_config(&config);
    
    int result = parse_config(argc, argv, &config);
    
    if (result == -1) {
        print_app_usage(argv[0]);
        return;
    }
    
    if (result == -2) {
        printf("Error parsing arguments\n");
        return;
    }
    
    /* Display configuration */
    printf("Configuration:\n");
    printf("  Verbose: %s\n", config.verbose ? "yes" : "no");
    printf("  Force: %s\n", config.force ? "yes" : "no");
    printf("  Recursive: %s\n", config.recursive ? "yes" : "no");
    printf("  Count: %d\n", config.count);
    printf("  Input: %s\n", config.input_file ? config.input_file : "(none)");
    printf("  Output: %s\n", config.output_file ? config.output_file : "(stdout)");
    printf("\n");
}

/* ============================================================
 * EXAMPLE 12: Subcommands Pattern
 * ============================================================ */

/**
 * Demonstrates git-like subcommand pattern
 * 
 * Usage: ./program init [options]
 *        ./program add <file>
 *        ./program status
 */
void example12_subcommands(int argc, char *argv[]) {
    printf("=== Example 12: Subcommands Pattern ===\n\n");
    
    if (argc < 2) {
        printf("Usage: %s <command> [options]\n\n", argv[0]);
        printf("Commands:\n");
        printf("  init     Initialize something\n");
        printf("  add      Add something\n");
        printf("  remove   Remove something\n");
        printf("  status   Show status\n");
        printf("  help     Show help\n");
        return;
    }
    
    const char *command = argv[1];
    
    if (strcmp(command, "init") == 0) {
        printf("Executing 'init' command\n");
        if (argc > 2) {
            printf("  With options: ");
            for (int i = 2; i < argc; i++) {
                printf("%s ", argv[i]);
            }
            printf("\n");
        }
    }
    else if (strcmp(command, "add") == 0) {
        if (argc < 3) {
            printf("Usage: %s add <item>\n", argv[0]);
            return;
        }
        printf("Adding: %s\n", argv[2]);
    }
    else if (strcmp(command, "remove") == 0 || strcmp(command, "rm") == 0) {
        if (argc < 3) {
            printf("Usage: %s remove <item>\n", argv[0]);
            return;
        }
        printf("Removing: %s\n", argv[2]);
    }
    else if (strcmp(command, "status") == 0 || strcmp(command, "st") == 0) {
        printf("Showing status...\n");
        printf("  Everything is OK!\n");
    }
    else if (strcmp(command, "help") == 0) {
        printf("Showing help for ");
        if (argc > 2) {
            printf("'%s' command\n", argv[2]);
        } else {
            printf("all commands\n");
        }
    }
    else {
        printf("Unknown command: %s\n", command);
        printf("Try '%s help' for usage information\n", argv[0]);
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 13: Argument Validation
 * ============================================================ */

typedef enum {
    VALIDATE_OK,
    VALIDATE_ERROR_EMPTY,
    VALIDATE_ERROR_RANGE,
    VALIDATE_ERROR_FORMAT,
    VALIDATE_ERROR_FILE
} ValidationResult;

ValidationResult validate_integer(const char *str, long min, long max, long *result) {
    if (str == NULL || *str == '\0') {
        return VALIDATE_ERROR_EMPTY;
    }
    
    char *endptr;
    errno = 0;
    long val = strtol(str, &endptr, 10);
    
    if (errno == ERANGE || *endptr != '\0') {
        return VALIDATE_ERROR_FORMAT;
    }
    
    if (val < min || val > max) {
        return VALIDATE_ERROR_RANGE;
    }
    
    *result = val;
    return VALIDATE_OK;
}

ValidationResult validate_file_exists(const char *filename) {
    if (filename == NULL || *filename == '\0') {
        return VALIDATE_ERROR_EMPTY;
    }
    
    FILE *f = fopen(filename, "r");
    if (f == NULL) {
        return VALIDATE_ERROR_FILE;
    }
    fclose(f);
    return VALIDATE_OK;
}

void example13_validation(int argc, char *argv[]) {
    printf("=== Example 13: Argument Validation ===\n\n");
    
    /* Validate integer argument */
    if (argc > 1) {
        long value;
        ValidationResult result = validate_integer(argv[1], 1, 100, &value);
        
        switch (result) {
            case VALIDATE_OK:
                printf("Integer validation: OK (value = %ld)\n", value);
                break;
            case VALIDATE_ERROR_EMPTY:
                printf("Integer validation: Empty string\n");
                break;
            case VALIDATE_ERROR_RANGE:
                printf("Integer validation: Out of range (1-100)\n");
                break;
            case VALIDATE_ERROR_FORMAT:
                printf("Integer validation: Invalid format\n");
                break;
            default:
                break;
        }
    } else {
        printf("Provide a number argument to test validation\n");
    }
    
    /* Validate file argument */
    if (argc > 2) {
        ValidationResult result = validate_file_exists(argv[2]);
        
        switch (result) {
            case VALIDATE_OK:
                printf("File validation: '%s' exists\n", argv[2]);
                break;
            case VALIDATE_ERROR_FILE:
                printf("File validation: '%s' not found\n", argv[2]);
                break;
            default:
                printf("File validation: Invalid filename\n");
        }
    }
    printf("\n");
}

/* ============================================================
 * EXAMPLE 14: Reading from stdin When No File Given
 * ============================================================ */

/**
 * Demonstrates the common pattern of reading from stdin
 * when no filename is provided (like cat, grep, etc.)
 */
void example14_stdin_fallback(int argc, char *argv[]) {
    printf("=== Example 14: stdin Fallback ===\n\n");
    
    printf("This example shows how to read from stdin when no file is given.\n");
    printf("Common in tools like cat, grep, sort, etc.\n\n");
    
    printf("Pattern:\n");
    printf("  FILE *input = stdin;\n");
    printf("  if (argc > 1 && strcmp(argv[1], \"-\") != 0) {\n");
    printf("      input = fopen(argv[1], \"r\");\n");
    printf("      if (!input) { perror(argv[1]); return 1; }\n");
    printf("  }\n");
    printf("  // Process input...\n");
    printf("  if (input != stdin) fclose(input);\n\n");
    
    /* Actual demonstration with simulated input */
    printf("Try running: echo 'hello world' | ./program\n");
    printf("Or: ./program filename.txt\n");
    printf("\n");
}

/* ============================================================
 * EXAMPLE 15: Multiple Input Files
 * ============================================================ */

/**
 * Demonstrates processing multiple input files
 * 
 * Usage: ./program file1.txt file2.txt file3.txt
 */
void example15_multiple_files(int argc, char *argv[]) {
    printf("=== Example 15: Multiple Input Files ===\n\n");
    
    if (argc < 2) {
        printf("Usage: %s file1 [file2] [file3] ...\n", argv[0]);
        printf("Process multiple files like: cat, grep, wc\n");
        return;
    }
    
    int total_lines = 0;
    int total_files = 0;
    
    for (int i = 1; i < argc; i++) {
        const char *filename = argv[i];
        
        /* Handle "-" as stdin */
        if (strcmp(filename, "-") == 0) {
            printf("Would read from stdin\n");
            continue;
        }
        
        /* Try to open file */
        FILE *f = fopen(filename, "r");
        if (!f) {
            fprintf(stderr, "%s: %s: %s\n", argv[0], filename, strerror(errno));
            continue;
        }
        
        /* Count lines */
        int lines = 0;
        int c;
        while ((c = fgetc(f)) != EOF) {
            if (c == '\n') lines++;
        }
        
        printf("%s: %d lines\n", filename, lines);
        total_lines += lines;
        total_files++;
        
        fclose(f);
    }
    
    if (total_files > 1) {
        printf("Total: %d files, %d lines\n", total_files, total_lines);
    }
    printf("\n");
}

/* ============================================================
 * MAIN - Run Selected Example
 * ============================================================ */

void print_menu(void) {
    printf("Command Line Arguments Examples\n");
    printf("================================\n\n");
    printf("Select an example to run:\n\n");
    printf("  1.  Basic argc and argv\n");
    printf("  2.  Pointer notation\n");
    printf("  3.  String conversion\n");
    printf("  4.  Simple calculator\n");
    printf("  5.  Manual argument parsing\n");
    printf("  6.  Using getopt()\n");
    printf("  7.  Using getopt_long()\n");
    printf("  8.  Long options with flags\n");
    printf("  9.  Error handling\n");
    printf("  10. Environment variables\n");
    printf("  11. Complete CLI application\n");
    printf("  12. Subcommands pattern\n");
    printf("  13. Argument validation\n");
    printf("  14. stdin fallback\n");
    printf("  15. Multiple input files\n");
    printf("  0.  Run all examples\n");
    printf("\n");
}

int main(int argc, char *argv[]) {
    int choice = -1;
    
    /* If no arguments, show menu and read choice */
    if (argc < 2) {
        print_menu();
        printf("Enter example number: ");
        if (scanf("%d", &choice) != 1) {
            printf("Invalid input\n");
            return 1;
        }
        printf("\n");
    } else {
        choice = atoi(argv[1]);
    }
    
    /* Create sample arguments for demos */
    char *sample_args[] = {
        "./program", "-v", "--verbose", "-f", "file.txt",
        "-n", "42", "extra1", "extra2", "3.14",
        "--config=test.cfg", "--port", "8080", NULL
    };
    int sample_argc = 13;
    
    switch (choice) {
        case 0:
            /* Run all */
            example1_basic_argc_argv(argc > 2 ? argc - 1 : sample_argc, 
                                     argc > 2 ? argv + 1 : sample_args);
            example2_pointer_notation(argc > 2 ? argc - 1 : sample_argc,
                                      argc > 2 ? argv + 1 : sample_args);
            example3_string_conversion(argc > 2 ? argc - 1 : sample_argc,
                                       argc > 2 ? argv + 1 : sample_args);
            example4_calculator(5, (char*[]){"calc", "10", "+", "5", NULL});
            example5_manual_parsing(sample_argc, sample_args);
            example6_getopt(sample_argc, sample_args);
            example7_getopt_long(sample_argc, sample_args);
            example8_flag_options(5, (char*[]){"prog", "--verbose", 
                                               "--debug", "-o", "out.txt", NULL});
            example9_error_handling(sample_argc, sample_args);
            example10_environment_variables();
            example11_complete_application(sample_argc, sample_args);
            example12_subcommands(3, (char*[]){"prog", "add", "item", NULL});
            example13_validation(3, (char*[]){"prog", "50", "examples.c", NULL});
            example14_stdin_fallback(argc, argv);
            example15_multiple_files(2, (char*[]){"prog", "examples.c", NULL});
            break;
            
        case 1:
            example1_basic_argc_argv(argc > 2 ? argc - 1 : sample_argc,
                                     argc > 2 ? argv + 1 : sample_args);
            break;
        case 2:
            example2_pointer_notation(argc > 2 ? argc - 1 : sample_argc,
                                      argc > 2 ? argv + 1 : sample_args);
            break;
        case 3:
            example3_string_conversion(argc > 2 ? argc - 1 : sample_argc,
                                       argc > 2 ? argv + 1 : sample_args);
            break;
        case 4:
            example4_calculator(5, (char*[]){"calc", "10", "+", "5", NULL});
            break;
        case 5:
            example5_manual_parsing(sample_argc, sample_args);
            break;
        case 6:
            example6_getopt(sample_argc, sample_args);
            break;
        case 7:
            example7_getopt_long(sample_argc, sample_args);
            break;
        case 8:
            example8_flag_options(5, (char*[]){"prog", "--verbose",
                                               "--debug", "-o", "out.txt", NULL});
            break;
        case 9:
            example9_error_handling(sample_argc, sample_args);
            break;
        case 10:
            example10_environment_variables();
            break;
        case 11:
            example11_complete_application(sample_argc, sample_args);
            break;
        case 12:
            example12_subcommands(3, (char*[]){"prog", "add", "item", NULL});
            break;
        case 13:
            example13_validation(3, (char*[]){"prog", "50", "examples.c", NULL});
            break;
        case 14:
            example14_stdin_fallback(argc, argv);
            break;
        case 15:
            example15_multiple_files(2, (char*[]){"prog", "examples.c", NULL});
            break;
        default:
            printf("Invalid choice. Run without arguments to see menu.\n");
            return 1;
    }
    
    return 0;
}
Examples - C Programming Tutorial | DeepML