Midterm Project — Build a Real CLI Tool

ছোট কিন্তু কাজ-করা একটি টুল তৈরি করুন

3–5 hours Intermediate 5 deliverables Milestone

1. Goal

Apply everything from Modules 1–18 to ship a working command-line program in C. Pick ONE of the four tracks below — ছোট কিন্তু সত্যিকার অর্থে চলে, এমন একটি টুল বানানোই উদ্দেশ্য।

  • wc-clone — একটি ফাইলে লাইন, শব্দ ও অক্ষর গণনা।
  • todo — একটি text ফাইলে কাজ যোগ/তালিকা/মুছে ফেলা।
  • grep-mini — ইনপুটে substring খুঁজে সেই লাইনগুলো print করা।
  • stats — ইনপুট থেকে সংখ্যা পড়ে min, max, mean, median প্রিন্ট করা।
পর্যবেক্ষণ: নিজে নিয়মিত ব্যবহার করার মতো ছোট একটি টুল বানালে আগ্রহ বজায় থাকে। Perfect নয়, চলে — এটাই লক্ষ্য।

2. Command-Line Arguments

প্রোগ্রাম চালু হওয়ার সময় main-এ দুটি argument আসে: argc (count) ও argv (array of strings)।

argv_skeleton.c
#include <stdio.h>

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

    if (argc < 2) {
        fprintf(stderr, "usage: %s <command> [args]\n", argv[0]);
        return 1;
    }
    return 0;
}

Online runner সাধারণত argv পাস করতে দেয় না। OnlineGDB-তে "Command line arguments" ঘরে মান দিতে পারেন।

3. Project Architecture — Multi-file Layout

todo/
├── main.c          // entry point, argument parsing
├── task.h          // Task struct + function prototypes
├── task.c          // add, list, done, remove
├── storage.h       // read / write the todo file
├── storage.c
├── Makefile        // builds the project
└── README.md       // usage, examples
প্রতিটি .c ফাইলে একটি নির্দিষ্ট বিষয়ের কোড থাকবে। .h ফাইলে শুধু function declaration ও struct definition। একই symbol একাধিক ফাইলে দরকার হলে extern ব্যবহার করুন, file-private হলে static।

4. Simple Makefile

CC     = gcc
CFLAGS = -std=c17 -Wall -Wextra -g
OBJS   = main.o task.o storage.o

todo: $(OBJS)
	$(CC) $(CFLAGS) -o $@ $(OBJS)

%.o: %.c
	$(CC) $(CFLAGS) -c $<

clean:
	rm -f $(OBJS) todo

make চালালে শুধু পরিবর্তিত ফাইলগুলোই পুনরায় compile হয় — বড় প্রোজেক্টে এটি অনেক সময় বাঁচায়।

5. Reference Implementation — wc-clone (live)

নিচের প্রোগ্রামটি একটি minimal wc-র মতো — stdin থেকে পড়ে line, word ও character সংখ্যা প্রিন্ট করে। Run-এ লেখা stdin কে ইনপুট হিসেবে দেখাবে।

mini_wc.c — stdin: 4 lines
#include <stdio.h>
#include <ctype.h>

int main(void) {
    long lines = 0, words = 0, chars = 0;
    int c, in_word = 0;

    while ((c = getchar()) != EOF) {
        chars++;
        if (c == '\n') lines++;
        if (isspace(c)) in_word = 0;
        else if (!in_word) { in_word = 1; words++; }
    }

    printf("%ld %ld %ld\n", lines, words, chars);
    return 0;
}

Output format: lines words chars — ঠিক যেমন Unix wc।

6. Reference — Grep-mini (live)

mini_grep.c — searches for "apple" in stdin
#include <stdio.h>
#include <string.h>

int main(void) {
    const char *pattern = "apple";    // hard-coded for the demo
    char line[1024];
    int lineno = 0;

    while (fgets(line, sizeof line, stdin)) {
        lineno++;
        if (strstr(line, pattern))
            printf("%d: %s", lineno, line);
    }
    return 0;
}

আসল প্রোজেক্টে pattern argv[1] থেকে নিন এবং input file argv[2] থেকে — একাধিক ফাইল সমর্থন যোগ করতে পারেন।

7. Reference — Stats (live)

mini_stats.c — 10 numbers via stdin
#include <stdio.h>
#include <stdlib.h>

int cmp_d(const void *a, const void *b) {
    double d = *(const double *)a - *(const double *)b;
    return (d > 0) - (d < 0);
}

int main(void) {
    double buf[1024]; int n = 0;
    while (n < 1024 && scanf("%lf", &buf[n]) == 1) n++;
    if (n == 0) { puts("no input"); return 1; }

    qsort(buf, n, sizeof *buf, cmp_d);

    double sum = 0;
    for (int i = 0; i < n; i++) sum += buf[i];
    double median = (n % 2) ? buf[n/2] : (buf[n/2 - 1] + buf[n/2]) / 2.0;

    printf("count  = %d\n", n);
    printf("min    = %.2f\n", buf[0]);
    printf("max    = %.2f\n", buf[n - 1]);
    printf("mean   = %.2f\n", sum / n);
    printf("median = %.2f\n", median);
    return 0;
}

8. Deliverables

  1. কমপক্ষে ২টি module-এ ভাগ করা source code (main + feature)।
  2. একটি Makefile — make দিয়ে build, make clean দিয়ে clean।
  3. README.md — usage উদাহরণ, screenshot সহ।
  4. কমপক্ষে একটি বাগ খুঁজে বের করে ঠিক করা + সংক্ষিপ্ত description।
  5. Demo transcript — terminal session যেখানে প্রতিটি feature কাজ করতে দেখা যায়।

9. Grading Rubric

CriterionWeight
-Wall -Wextra-তে সম্পূর্ণ warning-free compile15%
Feature সঠিকতা30%
Error handling (ভুল arg, missing file)15%
Memory safety (leak/UAF নেই)15%
Code organization & modularity15%
README & demo10%

10. Tips & Mini Practice

  1. Start with the simplest version that actually runs. Then add features one at a time, committing to git after each.
    সরলতম একটি working সংস্করণ দিয়ে শুরু করুন, এরপর একটি করে feature যোগ করুন — প্রতিটি step-এ git commit।
    ✨ Sample commit sequence
    git init
    git add main.c
    git commit -m "chore: initial hello world"
    # add arg parsing
    git commit -am "feat: read file from argv[1]"
    # add counting
    git commit -am "feat: count lines, words, chars"
    # add usage message
    git commit -am "feat: usage on missing arg"
  2. Run with -fsanitize=address,undefined before you ship.
    Ship করার আগে -fsanitize=address,undefined দিয়ে একবার চালান।
    ✨ Command
    gcc -std=c17 -Wall -Wextra -g -fsanitize=address,undefined *.c -o todo
    ./todo add "write midterm report"

    AddressSanitizer memory bug এবং UndefinedBehaviorSanitizer overflow/alignment সমস্যা ধরে দেবে।

  3. Write a simple usage() helper.
    একটি ছোট usage() helper লিখুন।
    ✨ Show Answer
    usage.c
    #include <stdio.h>
    #include <stdlib.h>
    
    static void usage(const char *prog) {
        fprintf(stderr,
            "usage: %s <command> [args]\n"
            "  commands:\n"
            "    add   <text>   add a todo\n"
            "    list           list todos\n"
            "    done  <id>     mark done\n"
            "    remove <id>    remove todo\n", prog);
        exit(1);
    }
    
    int main(int argc, char *argv[]) {
        if (argc < 2) usage(argv[0]);
        puts("OK, got a command.");
        return 0;
    }
  4. Why is peer review so valuable for this project?
    এই প্রোজেক্টে peer review কেন গুরুত্বপূর্ণ?
    ✨ Show Answer

    অন্য একজনের চোখে কোড অনেক বাগ দ্রুত ধরা পড়ে। আপনি নিজের কোডকে "কীভাবে চালালে চলে" সেটা ভাবেন; নতুন কেউ ভাবে "কী করলে ভাঙে"। এতে edge case, অস্পষ্ট error message এবং documentation-এর গ্যাপ দেখা যায়।

  5. What's the difference between argv[0] and the other argv[i]?
    argv[0] আর অন্য argv[i]-এর পার্থক্য কী?
    ✨ Show Answer

    argv[0] সাধারণত প্রোগ্রাম নিজে যে নামে চালু হয়েছে (যেমন ./todo)। বাকি argv[i]-গুলো user-এর দেওয়া argument। argv[argc] always NULL — এটি দিয়ে সেন্টিনেল হিসেবে loop শেষ করা যায়।

Glossary (শব্দকোষ)

TermMeaningবাংলায়
CLICommand-Line Interface — text-based program control.Text-ভিত্তিক command-line interface।
argcThe number of command-line arguments (including program name).Command-line argument-এর সংখ্যা।
argvThe array of argument strings.Argument string-এর array।
argv[0]Usually the program's invocation name.সাধারণত প্রোগ্রামের চালানোর নাম।
Flag / OptionAn argument like -v that toggles behavior.আচরণ পরিবর্তনকারী argument যেমন -v।
getoptPOSIX function for parsing command-line options.POSIX-এর option parser।
SubcommandA named action like git commit.নামকরা action যেমন git commit।
Exit CodeInteger returned to the shell — 0 = success.Shell-কে ফেরত দেওয়া integer (০ = সফল)।
Pipe (|)Connects one program's stdout to another's stdin.এক প্রোগ্রামের stdout আরেকটির stdin-এ যুক্ত করে।
Redirect (>, <)Send output to a file or read input from one.Output/input কে file-এ ঘুরিয়ে দেওয়া।
Standard Streamsstdin, stdout, stderr.তিনটি স্ট্যান্ডার্ড stream।
Usage MessageThe help text shown when arguments are wrong.ভুল argument-এ দেখানো help text।

Tips for Success

  • Start stupidly small. Working beats elegant-but-broken।
  • Each commit = one working step।
  • Test with good AND bad inputs।
  • Write README first — then fill the gap with code।
  • Peer review saves hours of debugging।