No new tools are needed for this chapter. fork, execve, pipe,
and dup2 are POSIX system calls — they are part of glibc and their
headers ship with every Linux system.
Start from c03
The pipeline binary uses both libtci and libtciutil, and this chapter extends libtciutil with the linked-list API. Create a new working directory from your c03 project:
cp -r ~/c03-practice ~/c05-practice
cd ~/c05-practiceIf you are starting fresh or want the reference version, clone the c03 companion repo and copy the solution:
git clone https://github.com/thecodingidiot-com/c03-the-reader.git
cp -r c03-the-reader/solution ~/c05-practice
cd ~/c05-practiceVerify the libraries build:
make reYou should see ar rcs libtci.a and ar rcs libtciutil.a complete
with no warnings.
Update the Makefile
The Makefile needs two additions: the list source files join libtciutil,
and a new pipeline binary target is introduced that links against
both libraries.
NAME = pipeline
LIBNAME = libtci.a
UTIL = libtciutil.a
CC = gcc
CFLAGS = -Wall -Wextra -g -std=c99 -D BUFFER_SIZE=32
AR = ar rcs
SRCS = tci_memset.c tci_memcpy.c tci_memmove.c tci_memchr.c tci_bzero.c \
tci_isascii.c tci_isalpha.c tci_isdigit.c tci_isalnum.c \
tci_isspace.c tci_isupper.c tci_islower.c tci_isprint.c \
tci_toupper.c tci_tolower.c \
tci_strlen.c tci_strcpy.c tci_strncpy.c tci_strlcpy.c tci_strlcat.c \
tci_strcmp.c tci_strncmp.c tci_strchr.c tci_strrchr.c tci_strnstr.c \
tci_atoi.c \
tci_calloc.c tci_strdup.c tci_strndup.c \
tci_printf.c \
tci_getline.c
UTIL_SRCS = tciu_split.c \
tciu_lstnew.c tciu_lstadd_front.c tciu_lstadd_back.c \
tciu_lstsize.c tciu_lstlast.c \
tciu_lstdelone.c tciu_lstclear.c tciu_lstiter.c tciu_lstmap.c
PIPE_SRCS = main.c exec.c pipeline.c
OBJS = $(SRCS:.c=.o)
UTIL_OBJS = $(UTIL_SRCS:.c=.o)
PIPE_OBJS = $(PIPE_SRCS:.c=.o)
.PHONY: all clean fclean re
all: $(LIBNAME) $(UTIL) $(NAME)
$(LIBNAME): $(OBJS)
$(AR) $(LIBNAME) $(OBJS)
$(UTIL): $(UTIL_OBJS)
$(AR) $(UTIL) $(UTIL_OBJS)
$(NAME): $(PIPE_OBJS) $(LIBNAME) $(UTIL)
$(CC) $(PIPE_OBJS) -L. -ltci -ltciutil -o $(NAME)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(UTIL_OBJS) $(PIPE_OBJS)
fclean: clean
rm -f $(LIBNAME) $(UTIL) $(NAME)
re: fclean allThe pipeline binary rule links against -ltci and -ltciutil. The
-L. flag tells the linker to search the current directory for those
archives.
Add the list function stubs
Create nine stub files for the list functions. They compile but return nothing yet — the implementations come in pages 01 and 02.
for f in tciu_lstnew tciu_lstadd_front tciu_lstadd_back \
tciu_lstsize tciu_lstlast \
tciu_lstdelone tciu_lstclear tciu_lstiter tciu_lstmap; do
printf '#include "libtciutil.h"\n' > ${f}.c
donefor f in ... ; do ... done is a Bash loop. The shell assigns each
word in the list to f in turn and runs the body once per word.
printf '#include "libtciutil.h"\n' writes a string to standard
output — \n becomes a real newline. The > operator redirects
that output into a file, creating it if it does not exist and
overwriting it if it does. ${f}.c expands the variable f and
appends .c; the braces prevent the shell from reading .c as part
of the variable name.
The loop runs nine times. After it finishes, nine .c files exist,
each containing exactly one #include line. That is enough for the
compiler to see a translation unit with no errors — the actual
function bodies come next.
Each file needs a minimal stub body. For now they are empty shells — the build verifies only that the declarations match:
tciu_lstnew.c:
#include "libtciutil.h"
t_list *tciu_lstnew(void *content)
{
(void)content;
return (NULL);
}Create similar stubs for the remaining eight files. The pattern is the
same for all of them: #include "libtciutil.h", the function signature
from the header, (void) casts for each parameter, and a return of
the right type (NULL for pointer-returning functions, nothing for
void functions). Headers beyond libtciutil.h — <stdlib.h> for
malloc and free, <unistd.h> for write — are added on pages 01 and 02 when each function is actually implemented.
Add list declarations to libtciutil.h
Open libtciutil.h and add the list type and declarations after the
tciu_split line:
#ifndef LIBTCIUTIL_H
# define LIBTCIUTIL_H
# include "libtci.h"
char **tciu_split(char const *s, char sep);
/* lists */
typedef struct s_list {
void *content;
struct s_list *next;
} t_list;
t_list *tciu_lstnew(void *content);
void tciu_lstadd_front(t_list **lst, t_list *new);
void tciu_lstadd_back(t_list **lst, t_list *new);
int tciu_lstsize(t_list *lst);
t_list *tciu_lstlast(t_list *lst);
void tciu_lstdelone(t_list *lst, void (*del)(void *));
void tciu_lstclear(t_list **lst, void (*del)(void *));
void tciu_lstiter(t_list *lst, void (*f)(void *));
t_list *tciu_lstmap(t_list *lst, void *(*f)(void *),
void (*del)(void *));
#endifThe pipeline source stubs
Create main.c, exec.c, and pipeline.c with the barest stubs that
compile:
main.c:
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "libtciutil.h"
int main(int argc, char **argv)
{
int i;
i = 0;
while (i < argc) {
tci_printf("argv[%d] = %s\n", i, argv[i]);
i++;
}
return (0);
}exec.c:
#include <unistd.h>
#include "libtciutil.h"
void exec_cmd(char **argv)
{
(void)argv;
}pipeline.c:
#include <unistd.h>
#include <sys/wait.h>
#include "libtciutil.h"
void run_pipeline(t_list *cmds, int infd, int outfd)
{
(void)cmds;
(void)infd;
(void)outfd;
}The headers included here — <unistd.h> for read/write/fork/execve/pipe/dup2/close, <fcntl.h> for open, <sys/wait.h> for waitpid — are everything the pipeline needs. No SDL2, no math library.
Verify the build
make reExpected output: libtci compiles (thirty-one objects), libtciutil
compiles (ten objects including the nine list stubs), pipeline compiles
(three objects). One ar line for each library. One linker line for
the binary. No warnings.
./pipeline hello worldExpected output:
argv[0] = ./pipeline
argv[1] = hello
argv[2] = worldThe binary exists and links correctly. The next page replaces the stub with real process execution.