Midterm Project — Build a Real CLI Tool
ছোট কিন্তু কাজ-করা একটি টুল তৈরি করুন
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 প্রিন্ট করা।
2. Command-Line Arguments
প্রোগ্রাম চালু হওয়ার সময় main-এ দুটি argument আসে: argc (count) ও argv (array of strings)।
#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 কে ইনপুট হিসেবে দেখাবে।
#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)
#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)
#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
- কমপক্ষে ২টি module-এ ভাগ করা source code (main + feature)।
- একটি Makefile —
makeদিয়ে build,make cleanদিয়ে clean। - README.md — usage উদাহরণ, screenshot সহ।
- কমপক্ষে একটি বাগ খুঁজে বের করে ঠিক করা + সংক্ষিপ্ত description।
- Demo transcript — terminal session যেখানে প্রতিটি feature কাজ করতে দেখা যায়।
9. Grading Rubric
| Criterion | Weight |
|---|---|
-Wall -Wextra-তে সম্পূর্ণ warning-free compile | 15% |
| Feature সঠিকতা | 30% |
| Error handling (ভুল arg, missing file) | 15% |
| Memory safety (leak/UAF নেই) | 15% |
| Code organization & modularity | 15% |
| README & demo | 10% |
10. Tips & Mini Practice
- 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" - Run with
-fsanitize=address,undefinedbefore 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 সমস্যা ধরে দেবে।
- 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; } - Why is peer review so valuable for this project?এই প্রোজেক্টে peer review কেন গুরুত্বপূর্ণ?
✨ Show Answer
অন্য একজনের চোখে কোড অনেক বাগ দ্রুত ধরা পড়ে। আপনি নিজের কোডকে "কীভাবে চালালে চলে" সেটা ভাবেন; নতুন কেউ ভাবে "কী করলে ভাঙে"। এতে edge case, অস্পষ্ট error message এবং documentation-এর গ্যাপ দেখা যায়।
- What's the difference between
argv[0]and the otherargv[i]?argv[0]আর অন্যargv[i]-এর পার্থক্য কী?✨ Show Answer
argv[0]সাধারণত প্রোগ্রাম নিজে যে নামে চালু হয়েছে (যেমন./todo)। বাকিargv[i]-গুলো user-এর দেওয়া argument।argv[argc]alwaysNULL— এটি দিয়ে সেন্টিনেল হিসেবে loop শেষ করা যায়।
Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| CLI | Command-Line Interface — text-based program control. | Text-ভিত্তিক command-line interface। |
argc | The number of command-line arguments (including program name). | Command-line argument-এর সংখ্যা। |
argv | The array of argument strings. | Argument string-এর array। |
argv[0] | Usually the program's invocation name. | সাধারণত প্রোগ্রামের চালানোর নাম। |
| Flag / Option | An argument like -v that toggles behavior. | আচরণ পরিবর্তনকারী argument যেমন -v। |
getopt | POSIX function for parsing command-line options. | POSIX-এর option parser। |
| Subcommand | A named action like git commit. | নামকরা action যেমন git commit। |
| Exit Code | Integer 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 Streams | stdin, stdout, stderr. | তিনটি স্ট্যান্ডার্ড stream। |
| Usage Message | The 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।