clay/docs/23_command_line_arguments.md
Claude faea55a9b9
Add comprehensive C learning materials with specialized topic tutorials
Complete learning guide covering ALL C concepts from beginner to advanced:

Main Guide (LEARNING_C_WITH_CLAY.md):
- Added Chapters 3-5: Operators, Control Flow, Loops
- Enhanced Chapters 6-13: Functions, Pointers, Structs, Arrays, Strings,
  Type Casting, Storage Classes, Recursion
- All chapters include extensive Clay library examples
- Progressive difficulty from basics to advanced topics

Specialized Tutorial Files (docs/):
- 14_bit_manipulation.md: Comprehensive bit operations guide
  * Bitwise operators, masks, flags
  * Counting bits, power of 2 operations
  * Practical applications with Clay examples

- 21_standard_library.md: C Standard Library overview
  * stdio.h, stdlib.h, string.h, math.h
  * Clay's zero-dependency approach
  * Custom implementations vs stdlib

- 22_file_io.md: Complete File I/O guide
  * Text and binary file operations
  * Configuration files for Clay apps
  * Error handling and best practices

- 23_command_line_arguments.md: CLI argument parsing
  * argc/argv basics
  * Flag parsing and subcommands
  * Clay application configuration examples

- README_C_TUTORIALS.md: Master index and learning guide
  * Complete chapter overview
  * Learning path recommendations
  * Progress tracking checklist
  * Prerequisites and setup instructions

Features:
- 24 comprehensive chapters covering all C concepts
- 100+ code examples with detailed explanations
- Real-world patterns from Clay library throughout
- Practice exercises for each chapter
- Modular organization for easy navigation
- Zero-dependency programming concepts
- Professional C development practices

Total content: ~70,000+ words of detailed tutorials
2025-11-13 20:32:16 +00:00

17 KiB

Chapter 23: Command-Line Arguments in C

Complete Guide with Clay Library Examples


23.1 Introduction to Command-Line Arguments

When you run a program from the terminal:

./program arg1 arg2 arg3

C provides access to these arguments through main:

int main(int argc, char *argv[]) {
    // argc = argument count
    // argv = argument vector (array of strings)
    return 0;
}

Parts:

  • argc - Number of arguments (including program name)
  • argv - Array of argument strings
  • argv[0] - Program name
  • argv[1] to argv[argc-1] - Actual arguments
  • argv[argc] - NULL pointer

23.2 Basic Example

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Program name: %s\n", argv[0]);
    printf("Argument count: %d\n", argc);

    printf("Arguments:\n");
    for (int i = 0; i < argc; i++) {
        printf("  argv[%d] = %s\n", i, argv[i]);
    }

    return 0;
}

Running:

$ ./program hello world 123
Program name: ./program
Argument count: 4
Arguments:
  argv[0] = ./program
  argv[1] = hello
  argv[2] = world
  argv[3] = 123

23.3 Processing Arguments

Check Argument Count

#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("Usage: %s <name> <age>\n", argv[0]);
        return 1;
    }

    char *name = argv[1];
    int age = atoi(argv[2]);  // Convert string to int

    printf("Name: %s, Age: %d\n", name, age);
    return 0;
}

Running:

$ ./program John 25
Name: John, Age: 25

$ ./program John
Usage: ./program <name> <age>

23.4 Parsing Numeric Arguments

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc != 4) {
        printf("Usage: %s <a> <b> <c>\n", argv[0]);
        return 1;
    }

    // Convert arguments to numbers
    int a = atoi(argv[1]);           // Integer
    long b = atol(argv[2]);          // Long
    double c = atof(argv[3]);        // Double

    printf("a = %d\n", a);
    printf("b = %ld\n", b);
    printf("c = %.2f\n", c);

    return 0;
}

Running:

$ ./program 10 1000000 3.14
a = 10
b = 1000000
c = 3.14

23.5 Command-Line Flags

Simple Flags

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    int verbose = 0;
    int debug = 0;

    // Check for flags
    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], "-d") == 0 ||
                 strcmp(argv[i], "--debug") == 0) {
            debug = 1;
        }
    }

    printf("Verbose: %s\n", verbose ? "ON" : "OFF");
    printf("Debug: %s\n", debug ? "ON" : "OFF");

    return 0;
}

Running:

$ ./program -v
Verbose: ON
Debug: OFF

$ ./program -v -d
Verbose: ON
Debug: ON

$ ./program --verbose --debug
Verbose: ON
Debug: ON

23.6 Flags with Values

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

int main(int argc, char *argv[]) {
    char *output = "output.txt";  // Default
    int level = 1;                // Default

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
            output = argv[++i];  // Next argument is the value
        }
        else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
            level = atoi(argv[++i]);
        }
    }

    printf("Output: %s\n", output);
    printf("Level: %d\n", level);

    return 0;
}

Running:

$ ./program -o result.txt -l 5
Output: result.txt
Level: 5

$ ./program
Output: output.txt
Level: 1

23.7 getopt for Option Parsing

POSIX provides getopt for standard option parsing:

#include <stdio.h>
#include <unistd.h>  // POSIX

int main(int argc, char *argv[]) {
    int opt;
    int verbose = 0;
    char *output = NULL;
    int level = 1;

    // Parse options
    while ((opt = getopt(argc, argv, "vo:l:")) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                break;
            case 'o':
                output = optarg;  // Option argument
                break;
            case 'l':
                level = atoi(optarg);
                break;
            case '?':
                printf("Unknown option: %c\n", optopt);
                return 1;
        }
    }

    // Process remaining arguments
    for (int i = optind; i < argc; i++) {
        printf("Non-option arg: %s\n", argv[i]);
    }

    printf("Verbose: %d\n", verbose);
    printf("Output: %s\n", output ? output : "none");
    printf("Level: %d\n", level);

    return 0;
}

Running:

$ ./program -v -o test.txt -l 3 file1 file2
Non-option arg: file1
Non-option arg: file2
Verbose: 1
Output: test.txt
Level: 3

23.8 Clay Example: UI Configuration

Imagine a Clay-based application with command-line options:

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

typedef struct {
    int width;
    int height;
    int fullscreen;
    char *title;
} AppConfig;

void printUsage(const char *programName) {
    printf("Usage: %s [options]\n", programName);
    printf("Options:\n");
    printf("  -w <width>     Window width (default: 800)\n");
    printf("  -h <height>    Window height (default: 600)\n");
    printf("  -f             Fullscreen mode\n");
    printf("  -t <title>     Window title\n");
    printf("  --help         Show this help\n");
}

int parseArgs(int argc, char *argv[], AppConfig *config) {
    // Defaults
    config->width = 800;
    config->height = 600;
    config->fullscreen = 0;
    config->title = "Clay Application";

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-w") == 0 && i + 1 < argc) {
            config->width = atoi(argv[++i]);
        }
        else if (strcmp(argv[i], "-h") == 0 && i + 1 < argc) {
            config->height = atoi(argv[++i]);
        }
        else if (strcmp(argv[i], "-f") == 0) {
            config->fullscreen = 1;
        }
        else if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
            config->title = argv[++i];
        }
        else if (strcmp(argv[i], "--help") == 0) {
            return 0;  // Show help
        }
        else {
            printf("Unknown option: %s\n", argv[i]);
            return 0;
        }
    }

    return 1;
}

int main(int argc, char *argv[]) {
    AppConfig config;

    if (!parseArgs(argc, argv, &config)) {
        printUsage(argv[0]);
        return 1;
    }

    printf("Starting Clay application:\n");
    printf("  Size: %dx%d\n", config.width, config.height);
    printf("  Fullscreen: %s\n", config.fullscreen ? "Yes" : "No");
    printf("  Title: %s\n", config.title);

    // Initialize Clay with config
    // Clay_Dimensions screenSize = {config.width, config.height};
    // Clay_Initialize(...);

    return 0;
}

Running:

$ ./clay_app -w 1024 -h 768 -f -t "My UI"
Starting Clay application:
  Size: 1024x768
  Fullscreen: Yes
  Title: My UI

$ ./clay_app --help
Usage: ./clay_app [options]
Options:
  -w <width>     Window width (default: 800)
  -h <height>    Window height (default: 600)
  -f             Fullscreen mode
  -t <title>     Window title
  --help         Show this help

23.9 Subcommands (git-style)

#include <stdio.h>
#include <string.h>

void cmd_init(int argc, char *argv[]) {
    printf("Initializing...\n");
}

void cmd_add(int argc, char *argv[]) {
    if (argc < 1) {
        printf("Error: add requires a file argument\n");
        return;
    }
    printf("Adding: %s\n", argv[0]);
}

void cmd_commit(int argc, char *argv[]) {
    char *message = "Default message";
    if (argc >= 2 && strcmp(argv[0], "-m") == 0) {
        message = argv[1];
    }
    printf("Committing: %s\n", message);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <command> [args]\n", argv[0]);
        printf("Commands: init, add, commit\n");
        return 1;
    }

    char *command = argv[1];
    int cmd_argc = argc - 2;
    char **cmd_argv = argv + 2;

    if (strcmp(command, "init") == 0) {
        cmd_init(cmd_argc, cmd_argv);
    }
    else if (strcmp(command, "add") == 0) {
        cmd_add(cmd_argc, cmd_argv);
    }
    else if (strcmp(command, "commit") == 0) {
        cmd_commit(cmd_argc, cmd_argv);
    }
    else {
        printf("Unknown command: %s\n", command);
        return 1;
    }

    return 0;
}

Running:

$ ./mygit init
Initializing...

$ ./mygit add file.txt
Adding: file.txt

$ ./mygit commit -m "Initial commit"
Committing: Initial commit

23.10 Environment Variables

Access environment variables alongside arguments:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[], char *envp[]) {
    printf("Arguments:\n");
    for (int i = 0; i < argc; i++) {
        printf("  %s\n", argv[i]);
    }

    printf("\nEnvironment Variables:\n");
    for (int i = 0; envp[i] != NULL; i++) {
        printf("  %s\n", envp[i]);
    }

    // Or use getenv
    char *home = getenv("HOME");
    printf("\nHOME: %s\n", home);

    return 0;
}

23.11 Argument Validation

Robust Argument Parser

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>

int parseInteger(const char *str, int *out) {
    char *endptr;
    errno = 0;

    long val = strtol(str, &endptr, 10);

    if (errno == ERANGE || val > INT_MAX || val < INT_MIN) {
        return 0;  // Out of range
    }

    if (endptr == str || *endptr != '\0') {
        return 0;  // Not a valid number
    }

    *out = (int)val;
    return 1;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <number>\n", argv[0]);
        return 1;
    }

    int value;
    if (!parseInteger(argv[1], &value)) {
        printf("Error: '%s' is not a valid integer\n", argv[1]);
        return 1;
    }

    printf("Valid integer: %d\n", value);
    return 0;
}

23.12 Configuration Priority

Combine defaults, config files, and command-line arguments:

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

typedef struct {
    int port;
    char *host;
    int debug;
} ServerConfig;

void setDefaults(ServerConfig *config) {
    config->port = 8080;
    config->host = "localhost";
    config->debug = 0;
}

void loadConfigFile(ServerConfig *config, const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) return;

    char line[256];
    while (fgets(line, sizeof(line), file)) {
        if (strncmp(line, "port=", 5) == 0) {
            config->port = atoi(line + 5);
        }
        else if (strncmp(line, "host=", 5) == 0) {
            config->host = strdup(line + 5);
            // Remove newline
            config->host[strcspn(config->host, "\n")] = 0;
        }
    }

    fclose(file);
}

void parseArgs(ServerConfig *config, int argc, char *argv[]) {
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
            config->port = atoi(argv[++i]);
        }
        else if (strcmp(argv[i], "-h") == 0 && i + 1 < argc) {
            config->host = argv[++i];
        }
        else if (strcmp(argv[i], "-d") == 0) {
            config->debug = 1;
        }
    }
}

int main(int argc, char *argv[]) {
    ServerConfig config;

    // Priority: defaults < config file < command line
    setDefaults(&config);
    loadConfigFile(&config, "server.cfg");
    parseArgs(&config, argc, argv);

    printf("Server configuration:\n");
    printf("  Host: %s\n", config.host);
    printf("  Port: %d\n", config.port);
    printf("  Debug: %s\n", config.debug ? "ON" : "OFF");

    return 0;
}

23.13 Wildcard Arguments

Handle wildcards expanded by shell:

#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <files...>\n", argv[0]);
        return 1;
    }

    printf("Processing %d files:\n", argc - 1);
    for (int i = 1; i < argc; i++) {
        printf("  - %s\n", argv[i]);
    }

    return 0;
}

Running:

$ ./program *.txt
Processing 3 files:
  - file1.txt
  - file2.txt
  - file3.txt

23.14 Interactive vs Batch Mode

#include <stdio.h>
#include <string.h>

int interactiveMode(void) {
    char input[100];

    while (1) {
        printf("> ");
        if (fgets(input, sizeof(input), stdin) == NULL) {
            break;
        }

        // Remove newline
        input[strcspn(input, "\n")] = 0;

        if (strcmp(input, "quit") == 0) {
            break;
        }

        printf("You entered: %s\n", input);
    }

    return 0;
}

int batchMode(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        printf("Error: Cannot open %s\n", filename);
        return 1;
    }

    char line[100];
    while (fgets(line, sizeof(line), file)) {
        // Process line
        printf("Processing: %s", line);
    }

    fclose(file);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc == 1) {
        // No arguments: interactive mode
        printf("Interactive mode (type 'quit' to exit)\n");
        return interactiveMode();
    }
    else {
        // Arguments provided: batch mode
        for (int i = 1; i < argc; i++) {
            printf("Processing file: %s\n", argv[i]);
            batchMode(argv[i]);
        }
        return 0;
    }
}

23.15 Clay Application Example

Complete Clay application with arguments:

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

// #define CLAY_IMPLEMENTATION
// #include "clay.h"

typedef struct {
    int width;
    int height;
    int fps;
    char *layout_file;
    int benchmark;
} ClayAppConfig;

void printHelp(const char *program) {
    printf("Usage: %s [options] [layout_file]\n", program);
    printf("\nOptions:\n");
    printf("  -w, --width <N>      Window width (default: 1024)\n");
    printf("  -h, --height <N>     Window height (default: 768)\n");
    printf("  --fps <N>            Target FPS (default: 60)\n");
    printf("  --benchmark          Run in benchmark mode\n");
    printf("  --help               Show this help\n");
    printf("\nExamples:\n");
    printf("  %s --width 1920 --height 1080 layout.clay\n", program);
    printf("  %s --benchmark\n", program);
}

int parseArguments(int argc, char *argv[], ClayAppConfig *config) {
    // Defaults
    config->width = 1024;
    config->height = 768;
    config->fps = 60;
    config->layout_file = NULL;
    config->benchmark = 0;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--width") == 0) {
            if (++i >= argc) return 0;
            config->width = atoi(argv[i]);
        }
        else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--height") == 0) {
            if (++i >= argc) return 0;
            config->height = atoi(argv[i]);
        }
        else if (strcmp(argv[i], "--fps") == 0) {
            if (++i >= argc) return 0;
            config->fps = atoi(argv[i]);
        }
        else if (strcmp(argv[i], "--benchmark") == 0) {
            config->benchmark = 1;
        }
        else if (strcmp(argv[i], "--help") == 0) {
            return 0;
        }
        else if (argv[i][0] != '-') {
            config->layout_file = argv[i];
        }
        else {
            printf("Unknown option: %s\n", argv[i]);
            return 0;
        }
    }

    return 1;
}

int main(int argc, char *argv[]) {
    ClayAppConfig config;

    if (!parseArguments(argc, argv, &config)) {
        printHelp(argv[0]);
        return 1;
    }

    printf("Clay Application Starting:\n");
    printf("  Resolution: %dx%d\n", config.width, config.height);
    printf("  Target FPS: %d\n", config.fps);
    printf("  Benchmark: %s\n", config.benchmark ? "Yes" : "No");
    if (config.layout_file) {
        printf("  Layout File: %s\n", config.layout_file);
    }

    // Initialize Clay
    // Clay_Dimensions windowSize = {config.width, config.height};
    // Clay_Initialize(...);

    // Main loop...

    return 0;
}

23.16 Key Concepts Learned

  • argc and argv basics
  • Processing command-line arguments
  • Parsing numeric arguments
  • Command-line flags
  • Flags with values
  • getopt for option parsing
  • Subcommands
  • Environment variables
  • Argument validation
  • Configuration priority
  • Interactive vs batch mode
  • Clay application with arguments

Practice Exercises

  1. Build a calculator that accepts expressions as arguments
  2. Create a file search utility with filtering options
  3. Implement a CSV processor with column selection
  4. Build a simple HTTP server with configurable port
  5. Create a code formatter with style options
  6. Implement a task manager CLI (add, list, complete)
  7. Build an image converter with format options
  8. Create a benchmarking tool with various options