linux-sysprog · intermediate · ~45 min

Final Project: mini-shell — argv tokenizer

Stateful character-by-character parsing with multiple modes (in-word / in-quote).

Challenge

Tokenise a command line into an argv vector the way a shell does, honouring double-quoted strings.

Task

Implement int shell_split(const char *line, char **argv, int max_argv) that splits line into tokens and stores a freshly allocated copy of each token in argv.

Input

  • line: a NUL-terminated command line. Tokens are separated by spaces/tabs; a run inside "..." is one token (the quotes are stripped, and whitespace inside them is kept).
  • argv, max_argv: the output array and its capacity (pointer slots).

Output

Stores up to max_argv - 1 token pointers in argv[0..ret-1] — each made with strdup, so the caller frees them — then sets argv[ret] = NULL. Does not modify line. Returns the token count, or -1 if a quote is never closed.

Example

shell_split("ls -la /tmp", argv, 16)          ->   3   argv = {"ls","-la","/tmp"}
shell_split("echo \"hello world\"", argv, 16)   ->   2   argv = {"echo","hello world"}
shell_split("", argv, 16)                      ->   0
shell_split("oops \"never ends", argv, 16)     ->   -1   (unterminated quote)

Edge cases

  • The empty line yields 0 tokens.
  • An unterminated " returns -1 (and frees any tokens already allocated).

Rules

  • Do not modify line; copy each token with strdup (don't use strtok).
  • Always NULL-terminate argv after the last token.

Why this matters

A POSIX shell is at heart a tokenizer + a fork/exec loop. The trickiest bit is splitting a command line into argv while respecting quotes — every shell on every UNIX-like OS does this dance, and getting it right is a great pointer/string exercise.

Input format

A NUL-terminated command line, an output array argv, and its capacity max_argv. Tokens split on whitespace; "..." groups one token without the quotes.

Output format

strdup'd token pointers in argv[0..ret-1], then argv[ret]=NULL. Returns the token count, or -1 on an unterminated quote.

Constraints

line is null-terminated command string; argv has space for max_args pointers.

Starter code

#include <stddef.h>
int shell_split(const char *line, char **argv, int max_argv) { /* TODO */ return 0; }

Common mistakes

Treating quoted spaces as word delimiters or overrunning argv array bound.

Edge cases to handle

Trailing whitespace, multiple consecutive spaces, unclosed quotes, empty line.

Complexity

O(n) time, O(tokens) memory.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.