Skip to content

Exam Rank 04 Solutions

Download Subject Files:



Execute a pipeline of commands, connecting stdout of each to stdin of the next.

picoshell.c
#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 prev
wait for all

Implement a simplified version of the standard popen() function.

ft_popen.c
#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]

Test if a function is “nice” (exits 0) or “bad” (signal, non-zero exit, timeout).

sandbox.c
#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?


Parse a simplified JSON format (numbers, strings, objects only, no whitespace).

argo.c
#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 | '{'object
object = '{' (key:value ,)* '}'
string = chars until '"', handle \" and \\

Evaluate math expressions with +, *, and parentheses, respecting operator precedence.

vbc.c
#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 precedence
term = factor ('*' factor)* ← higher precedence
factor = '(' expr ')' | digit ← highest precedence
3+4*5 = 3+(4*5) = 23 ✓

ExerciseCore Pattern
picoshellprev=-1; for cmd: pipe(), fork(), dup2(prev→0, pfd[1]→1), exec; wait all
ft_popen'r'→return pfd[0], 'w'→return pfd[1]
sandboxfork(), alarm(), waitpid(), check WIFEXITED/WIFSIGNALED
vbcexpr→term→factor (precedence: + < * < ())
argogetc() dispatch: '"'→string, digit→number, '{'→object

  1. Forgetting exit(1) after failed execvp()
  2. Not closing all pipe ends (causes hangs)
  3. Wrong operator precedence in vbc (* before +)
  4. Wrong error message format (must match exactly)
  5. Not handling empty input / EOF
  6. Zombie processes (always wait() for all children)