thecodingidiot.com

The PipelineSetup

Setup

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-practice

If 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-practice

Verify the libraries build:

make re

You 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 all

The 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
done

for 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 *));
 
#endif

The 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 re

Expected 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 world

Expected output:

argv[0] = ./pipeline
argv[1] = hello
argv[2] = world

The binary exists and links correctly. The next page replaces the stub with real process execution.