Exam Rank 04 Solutions
Download Subject Files:
Level 1: Process Exercises
Section titled “Level 1: Process Exercises”picoshell
Section titled “picoshell”Execute a pipeline of commands, connecting stdout of each to stdin of the next.
#include <unistd.h>#include <sys/wait.h>#include <stdlib.h>
int picoshell(char **cmds[]){ int n = 0; int pfd[2]; int prev = -1;
while (cmds[n]) n++;
for (int i = 0; cmds[i]; i++) { if (cmds[i + 1] && pipe(pfd) == -1) return 1;
pid_t pid = fork(); if (pid == -1) return 1;
if (pid == 0) { // Child: connect stdin to previous pipe's read-end if (prev != -1) { dup2(prev, 0); close(prev); }
// Child: connect stdout to current pipe's write-end if (cmds[i + 1]) { close(pfd[0]); dup2(pfd[1], 1); close(pfd[1]); }
execvp(cmds[i][0], cmds[i]); exit(1); }
// Parent: close previous pipe's read-end (child has it now) if (prev != -1) close(prev);
// Parent: close write-end, save read-end for next iteration if (cmds[i + 1]) { close(pfd[1]); prev = pfd[0]; } }
while (n--) wait(NULL);
return 0;}Pattern to memorize:
prev = -1 (no previous pipe initially)for each cmd: if (not last) pipe() fork() child: dup2(prev→stdin), dup2(pfd[1]→stdout), exec parent: close prev, save pfd[0] as new prevwait for allft_popen
Section titled “ft_popen”Implement a simplified version of the standard popen() function.
#include <unistd.h>#include <stdlib.h>
int ft_popen(const char *file, char *const argv[], char type) { if (type != 'r' && type != 'w') return -1;
int pfd[2]; if (pipe(pfd) == -1) return -1;
pid_t pid = fork(); if (pid == -1) { close(pfd[0]); close(pfd[1]); return -1; }
if (pid == 0) { if (type == 'r') { close(pfd[0]); dup2(pfd[1], 1); close(pfd[1]); } else { close(pfd[1]); dup2(pfd[0], 0); close(pfd[0]); } execvp(file, argv); exit(1); } if (type == 'r') { close(pfd[1]); return pfd[0]; } else { close(pfd[0]); return pfd[1]; }}Pattern to memorize:
'r' = I Read → child writes stdout → return pfd[0]'w' = I Write → child reads stdin → return pfd[1]sandbox
Section titled “sandbox”Test if a function is “nice” (exits 0) or “bad” (signal, non-zero exit, timeout).
#include <unistd.h>#include <sys/wait.h>#include <signal.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdbool.h>
static volatile sig_atomic_t g_timeout = 0;static void handler(int s) { (void)s; g_timeout = 1; }
int sandbox(void (*f)(void), unsigned int timeout, bool verbose) { if (!f) return -1;
struct sigaction sa = {.sa_handler = handler}; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, NULL) == -1) return -1;
g_timeout = 0; pid_t pid = fork(); if (pid == -1) return -1; if (pid == 0) { f(); exit(0); }
if (timeout) alarm(timeout); int st; if (waitpid(pid, &st, 0) == -1) { alarm(0); return -1; } alarm(0);
if (g_timeout) { kill(pid, SIGKILL); waitpid(pid, NULL, 0); if (verbose) printf("Bad function: timed out after %u seconds\n", timeout); return 0; } if (WIFEXITED(st)) { int code = WEXITSTATUS(st); if (verbose) printf(code ? "Bad function: exited with code %d\n" : "Nice function!\n", code); return code == 0; } if (WIFSIGNALED(st)) { if (verbose) printf("Bad function: %s\n", strsignal(WTERMSIG(st))); return 0; } return -1;}Pattern to memorize:
fork child → run f() → exit(0)parent: alarm(), waitpid(), alarm(0)check: timeout? signal? exit code?Level 2: Parsing Exercises
Section titled “Level 2: Parsing Exercises”argo (JSON Parser)
Section titled “argo (JSON Parser)”Parse a simplified JSON format (numbers, strings, objects only, no whitespace).
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>
typedef enum { JSON_NUMBER, JSON_STRING, JSON_OBJECT } json_type;typedef struct json json;typedef struct json_pair { char *key; json *value; struct json_pair *next; } json_pair;struct json { json_type type; union { int number; char *string; json_pair *pairs; }; };
static int parse_value(FILE *f, json *dst);
static void err(int c) { printf(c == EOF ? "Unexpected end of input\n" : "Unexpected token '%c'\n", c);}
static int parse_string(FILE *f, json *dst) { char buf[4096]; int i = 0, c; while ((c = getc(f)) != EOF && c != '"') { if (c == '\\') { c = getc(f); if (c != '"' && c != '\\') { err(c); return -1; } } buf[i++] = c; } if (c == EOF) { err(c); return -1; } buf[i] = 0; dst->type = JSON_STRING; dst->string = strdup(buf); return 1;}
static int parse_object(FILE *f, json *dst) { dst->type = JSON_OBJECT; dst->pairs = NULL; json_pair **tail = &dst->pairs; int c = getc(f); if (c == '}') return 1; ungetc(c, f);
while (1) { if ((c = getc(f)) != '"') { err(c); return -1; } json k; if (parse_string(f, &k) == -1) return -1; if ((c = getc(f)) != ':') { free(k.string); err(c); return -1; } json *v = malloc(sizeof(json)); if (parse_value(f, v) == -1) { free(k.string); free(v); return -1; } json_pair *p = malloc(sizeof(json_pair)); *p = (json_pair){k.string, v, NULL}; *tail = p; tail = &p->next; c = getc(f); if (c == '}') break; if (c != ',') { err(c); return -1; } } return 1;}
static int parse_value(FILE *f, json *dst) { int c = getc(f); if (c == '"') return parse_string(f, dst); if (isdigit(c)) { ungetc(c, f); fscanf(f, "%d", &dst->number); dst->type = JSON_NUMBER; return 1; } if (c == '{') return parse_object(f, dst); err(c); return -1;}
int argo(json *dst, FILE *stream) { return parse_value(stream, dst); }Pattern to memorize:
value = '"'string | digit→number | '{'objectobject = '{' (key:value ,)* '}'string = chars until '"', handle \" and \\vbc (Expression Calculator)
Section titled “vbc (Expression Calculator)”Evaluate math expressions with +, *, and parentheses, respecting operator precedence.
#include <stdio.h>#include <stdlib.h>#include <ctype.h>
static int expr(const char **s);
static void err(char c) { printf(c ? "Unexpected token '%c'\n" : "Unexpected end of input\n", c); exit(1);}
static int factor(const char **s) { if (**s == '(') { (*s)++; int r = expr(s); if (**s != ')') err(**s); (*s)++; return r; } if (isdigit(**s)) return *(*s)++ - '0'; err(**s); return 0;}
static int term(const char **s) { int r = factor(s); while (**s == '*') { (*s)++; r *= factor(s); } return r;}
static int expr(const char **s) { int r = term(s); while (**s == '+') { (*s)++; r += term(s); } return r;}
int main(int ac, char **av) { if (ac != 2) { printf("Usage: %s 'expression'\n", av[0]); return 1; } const char *s = av[1]; int r = expr(&s); if (*s) err(*s); printf("%d\n", r); return 0;}Pattern to memorize:
expr = term ('+' term)* ← lowest precedenceterm = factor ('*' factor)* ← higher precedencefactor = '(' expr ')' | digit ← highest precedence
3+4*5 = 3+(4*5) = 23 ✓Quick Reference Card
Section titled “Quick Reference Card”| Exercise | Core Pattern |
|---|---|
| picoshell | prev=-1; for cmd: pipe(), fork(), dup2(prev→0, pfd[1]→1), exec; wait all |
| ft_popen | 'r'→return pfd[0], 'w'→return pfd[1] |
| sandbox | fork(), alarm(), waitpid(), check WIFEXITED/WIFSIGNALED |
| vbc | expr→term→factor (precedence: + < * < ()) |
| argo | getc() dispatch: '"'→string, digit→number, '{'→object |
Common Exam Mistakes to Avoid
Section titled “Common Exam Mistakes to Avoid”- Forgetting
exit(1)after failedexecvp() - Not closing all pipe ends (causes hangs)
- Wrong operator precedence in vbc (
*before+) - Wrong error message format (must match exactly)
- Not handling empty input / EOF
- Zombie processes (always
wait()for all children)