Builtins

Header for builtin function registry and declarations.

Defines the t_builtin_fn type and declares builtin functions. Also includes the builtin lookup function.

Author

zweng, jguillem, pulgamecanica

Typedefs

typedef struct s_shell t_shell

Forward declaration to avoid circular dependency with 42sh.h

typedef int (*t_builtin_fn)(struct s_shell *shell, int argc, char **argv)

Builtin function type.

Every builtin shares this signature. argv[0] is the builtin name itself, argv[argc] is NULL. Return value is the exit status the shell should observe.

Param shell:

Pointer to the central shell state, for accessing variables, jobs, etc.

Param argc:

Argument count (number of elements in argv).

Param argv:

Argument vector (argv[0] is the command name).

Return:

Exit status code (0 for success, non-zero for failure).

Functions

t_builtin_fn builtin_get(const char *name)

Look up a builtin function by name.

Parameters:
  • name – Name of the builtin function to retrieve

Returns:

Pointer to the builtin function, or NULL if name is not a registered builtin.

Returns:

Pointer to the builtin function, or NULL if not found

int builtin_is_builtin(const char *name)

Test whether name is a registered builtin.

Returns:

Non-zero if name is a builtin, 0 otherwise.

int builtin_echo(struct s_shell *shell, int argc, char **argv)

Print arguments separated by spaces, followed by a newline.

Usage: echo [-neE] [arg ...]

Options may be combined (e.g. -nE). Unknown letters disable option parsing for that token, so -x is printed literally.

Option

Effect

-n

Suppress the trailing newline.

-e

Interpret backslash escapes (\n, \t, \xNN, …).

-E

Disable backslash interpretation (default).

Examples:

echo hello world         # hello world
echo -n no-newline       # no-newline
echo -e 'a\tb\nc'        # a<TAB>b<NEWLINE>c
echo -E 'literal\\n'     # literal\n

Returns:

Always 0.

int builtin_cd(struct s_shell *shell, int argc, char **argv)

Change the shell’s current working directory.

Usage: cd [-L|-P] [--] [dir]

With no argument, changes to $HOME. The argument - switches to the previous directory ($OLDPWD) and prints the new path. -- ends option parsing so a directory literally named -foo can be reached.

Option

Effect

-L

Logical: keep symlinks in PWD (default).

-P

Physical: resolve symlinks via realpath.

After a successful change, OLDPWD is set to the previous PWD and PWD is updated.

Examples:

cd                       # go to $HOME
cd /tmp                  # absolute path
cd ..                    # parent directory
cd -                     # back to previous directory, prints it
cd -P /var/run           # follow symlinks
cd -- -weird-dir-name    # directory whose name starts with '-'

Returns:

0 on success, 1 on filesystem error (no such directory, not a directory, permission denied, name too long, HOME/OLDPWD unset), 2 on usage error (invalid option, too many arguments).

int builtin_pwd(struct s_shell *shell, int argc, char **argv)

Print the current working directory.

Usage: pwd [-L|-P]

Option

Effect

-L

Logical: print $PWD (default).

-P

Physical: print getcwd(3) output (symlinks resolved).

The default -L form is what makes cd /bin; pwd report /bin even when /bin is a symlink to /usr/bin — the shell tracks the logical path the user typed, not what the kernel resolved.

Returns:

0 on success, 1 on getcwd failure or usage error.

int builtin_exit(struct s_shell *shell, int argc, char **argv)

Exit the shell.

Usage: exit [n]

If n is supplied, the shell exits with status n & 0xFF (POSIX truncation to 8 bits). With no argument, exits with the status of the most recently executed command ($?).

Examples:

exit                     # exit with $?
exit 0                   # exit success
exit 42                  # exit with status 42
exit -1                  # exit with status 255 (-1 & 0xFF)
exit foo                 # error: "numeric argument required", exit 2
exit 1 2                 # error: "too many arguments", returns 1

Returns:

On success the shell terminates and never returns to the caller. On usage error: 2 (non-numeric argument) or 1 (more than one argument — the shell is not exited in this case).

int builtin_type(struct s_shell *shell, int argc, char **argv)

Describe how each name would be interpreted as a command.

Usage: type [-tpa] name [name ...]

Option

Effect

(none)

name is a shell builtin / name is /path / name not found

-t

Print one word: builtin or file (nothing if not found).

-p

Print the $PATH resolution of a disk command only.

-a

Print the builtin entry and every $PATH match.

Options may be combined (-ta); -- ends option parsing. An unknown option is reported on stderr and yields exit status 2.

Examples:

type cd ls bogus
# cd is a shell builtin
# ls is /usr/bin/ls
# bogus not found
type -t ls        # file
type -p ls        # /usr/bin/ls

Returns:

0 if every name was identified, 1 if any name was not found, 2 on an invalid option.

int builtin_jobs(struct s_shell *shell, int argc, char **argv)

List active jobs known to the shell.

Usage: jobs

Output format, one line per job:

[<id>]  <marker> <status>\t<command-line>

<marker> is + for the current job and a space otherwise. <status> is Running, Stopped, or Done. Listing a job clears its pending notification flag.

Example:

sleep 100 &
sleep 200 &
jobs
# [1]    Running        sleep 100
# [2]  + Running        sleep 200

Returns:

Always 0.

int builtin_fg(struct s_shell *shell, int argc, char **argv)

Resume a job in the foreground.

Usage: fg [job_spec]

Brings the selected job to the foreground, prints its command line, and waits for it to finish or stop. With no argument, acts on the current job (%+).

job_spec accepts:

Spec

Meaning

(omitted)

The current job.

%, %%, %+

The current job.

%-

The previous current job.

%<n>

Job whose id is <n> (the number in [ ]).

%<str>

Most recent job whose command line begins with <str>.

Examples:

sleep 100 &              # [1] 12345
fg %1                    # resume job 1 in foreground
fg                       # resume the current job
fg %sleep                # resume job whose command begins with "sleep"

Returns:

Exit status of the resumed job, or 1 if no matching job exists.

int builtin_bg(struct s_shell *shell, int argc, char **argv)

Resume a stopped job in the background.

Usage: bg [job_spec]

Sends SIGCONT to the selected job and lets it continue without occupying the terminal. The job spec syntax is identical to fg (see builtin_fg). With no argument, acts on the current job.

Examples:

sleep 100                # ... press Ctrl-Z
# [1]+  Stopped          sleep 100
bg                       # resume in background
bg %1                    # same, by job id

Returns:

0 on success, 1 if no matching job exists.

int builtin_kill(struct s_shell *shell, int argc, char **argv)

Send a signal to processes or jobspec jobs.

Usage: kill [-l] [-s sig | -sig | -signum] pid|jobspec ...

-l with no further argument lists every recognised signal name. Without -s/-signal/-signum, the default signal is SIGTERM (15). jobspec resolves through job_find_by_spec and signals the whole process group (-pgid). Numeric arguments are passed verbatim to kill(2).

Returns:

0 if every target was signalled, 1 if any failed (no such job, invalid signal, unreachable pid).

int builtin_history(struct s_shell *shell, int argc, char **argv)

Print the command history.

Usage: history [n]

With no argument, prints every entry currently held by readline. With a positive integer n, prints only the last n entries. Non-positive n prints nothing. Each line is <index> <command> where <index> starts at history_base (typically 1).

Examples:

history                  # print full history
history 5                # print last 5 commands

Returns:

Always 0.

int builtin_set(struct s_shell *shell, int argc, char **argv)

The set builtin command.

Usage: set [NAME=VALUE ...]

  • With no arguments, displays all variables in NAME=VALUE format.

  • Each NAME=VALUE argument sets a variable.

Examples:

set                  # display all variables
set var1=value1      # set a variable
set x=10 y=hello     # set multiple variables

Parameters:
  • shell – Pointer to the shell state

  • argc – Argument count

  • argv – Argument vector

Returns:

0 on success, 1 on error

int builtin_unset(struct s_shell *shell, int argc, char **argv)

The unset builtin command.

Usage: unset name [name ...]

Removes one or more variables by name. Each argument should be a valid variable name (identifier). If a name is not found, unset succeeds silently (POSIX behavior).

Examples:

unset var1           # remove a variable
unset x y z          # remove multiple variables
unset nonexistent    # succeeds silently

Parameters:
  • shell – Pointer to the shell state

  • argc – Argument count

  • argv – Argument vector

Returns:

0 on success, 1 on error (invalid identifier)

int builtin_export(struct s_shell *shell, int argc, char **argv)

The export builtin command.

Usage: export [name] [name=value ...]

  • With no arguments, displays all exported variables in exportable format.

  • With name (identifier), marks the variable for export to child processes.

  • With NAME=VALUE argument, sets the variable and marks it for export.

Examples:

export                       # display all exported variables
export PATH                  # export existing PATH variable
export var1=value1           # set and export a variable
export x=10 MYVAR=hello      # set and export multiple variables

Parameters:
  • shell – Pointer to the shell state

  • argc – Argument count

  • argv – Argument vector

Returns:

0 on success, 1 on error

int builtin_hash(struct s_shell *shell, int argc, char **argv)

Manage the shell’s PATH-lookup cache.

Usage: hash [-r] [-p path] [-dt] [name ...]

Form

Effect

(no args)

Print cached entries (hits<TAB>command).

name [name ...]

Resolve each name through $PATH and cache it.

-r

Forget every cached entry.

-d name [name ...]

Forget specific entries.

-p path name

Cache name -> path without consulting $PATH.

-t name [name ...]

Print the cached path for each name.

The cache powers find_command, which consults this table before walking $PATH - so a frequent command costs one strcmp instead of a directory scan.

Returns:

0 on success, 1 on lookup failure (unknown name to resolve, delete, or print), 2 on usage error (unknown flag, missing operand, mutually-exclusive flags).

int builtin_alias(struct s_shell *shell, int argc, char **argv)

The alias builtin command.

Usage: alias [name[=value] ...]

  • With no arguments, lists every alias as alias name=’value, sorted by name, with embedded single quotes escaped as’’’.

  • Withname=value, defines or redefines an alias.

  • Withname`, prints that alias, or reports it as not found.

Returns:

0 on success, 1 if a queried name is not an alias.

int builtin_unalias(struct s_shell *shell, int argc, char **argv)

The unalias builtin command.

Usage: unalias [-a] name [name ...]

  • -a removes every alias.

  • Each name removes one alias; an unknown name is reported on stderr.

Returns:

0 on success, 1 if a name was not an alias or on usage error.

int builtin_test(struct s_shell *shell, int argc, char **argv)

Execute the shell builtin test.

Parameters:
  • shell – Pointer to the current shell state.

  • argc – Number of arguments in argv.

  • argv – Array of argument strings.

Returns:

Exit status for the builtin command.

struct t_test_context
#include <builtins.h>

t_test_context - Context structure for test command execution @argc: Argument count for the test command @argv: Argument vector containing test command arguments @pos: Current position or index within the argument parsing

This structure maintains the state and arguments needed to execute and parse test builtin commands in the shell.

Public Members

int argc
char **argv
int pos
int syntax_error

registry

Builtin function registry and implementations.

Author

pulgamecanica

Functions

t_builtin_fn builtin_get(const char *name)

Look up a builtin function by name.

Parameters:
  • name – Name of the builtin function to retrieve

Returns:

Pointer to the builtin function, or NULL if not found

int builtin_is_builtin(const char *name)

Test whether name is a registered builtin.

Returns:

Non-zero if name is a builtin, 0 otherwise.

Variables

static const t_builtin_entry g_builtins [] = {{"history",builtin_history},{"echo",builtin_echo},{"cd",builtin_cd},{"pwd",builtin_pwd},{"jobs",builtin_jobs},{"fg",builtin_fg},{"bg",builtin_bg},{"kill",builtin_kill},{"exit",builtin_exit},{"type",builtin_type},{"set",builtin_set},{"unset",builtin_unset},{"export",builtin_export},{"hash",builtin_hash},{"alias",builtin_alias},{"unalias",builtin_unalias},{"test",builtin_test},{"[",builtin_test},{NULL, NULL}}

Builtin lookup table.

struct t_builtin_entry

Public Members

const char *name
t_builtin_fn fn

bg

bg [spec] - resume a stopped job in the background.

Author

pulgamecanica

Functions

static void no_such_job(const char *spec)
int builtin_bg(t_shell *shell, int argc, char **argv)

cd

Implementation of the cd builtin command for the 42sh shell.

Author

jguillem

Functions

static char *join_paths(char *old_path, char *relative)

Join a base path with a relative path, ensuring proper ‘/’ separation.

Parameters:
  • old_path – : the base path

  • relative – : the relative path to append

Returns:

The newly allocated combined path

static char *concatenate(char *base, char *add)

Concatenate two paths, inserting ‘/’ when needed and freeing base.

Parameters:
  • base – : the base string

  • add – : the string to append

Returns:

The resulting concatenated string

static int resolve_path(char **path)

Normalize a path by resolving ‘.’, ‘..’ and redundant separators.

Parameters:
  • path – : pointer to the path string to resolve

Returns:

0 on success, 1 if an error occurs (e.g. path too long)

static int access_failure(char *directory, char *path, char *oldpwd, char *msg)

Print a cd error message and free allocated resources.

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

  • msg – : the error message to display

Returns:

1 (failure)

static int no_such_file_or_directory(char *directory, char *path, char *oldpwd)

Handle No such file or directory error.

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

Returns:

1 (failure)

static int not_a_directory(char *directory, char *path, char *oldpwd)

Handle Not a directory error.

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

Returns:

1 (failure)

static int permission_denied(char *directory, char *path, char *oldpwd)

Handle Permission denied error.

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

Returns:

1 (failure)

static int filename_too_long(char *directory, char *path, char *oldpwd)

Handle File name too long error.

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

Returns:

1 (failure)

static int is_invalid_path(char *directory, char *path, char *oldpwd)

Validate the path (length, type, permissions)

Parameters:
  • directory – : the directory argument provided by the user

  • path – : the resolved path

  • oldpwd – : the previous working directory

Returns:

0 if valid, 1 if an error occurs

static void publish_pwd(t_shell *shell, const char *new_pwd, const char *oldpwd)

Update PWD/OLDPWD in the shell variable table and mark exported.

Using var_set (instead of setenv) is what makes $PWD visible to subsequent expansions and to the rebuilt child environ. var_set preserves the existing ‘exported’ flag, but PWD/OLDPWD may not have been inherited from the parent environment, so we call var_export to be safe.

static int change_directory(t_shell *shell, char *target, int argc, int physical)

Resolve and change the current working directory, updating PWD/OLDPWD.

Parameters:
  • target – : the target directory argument

  • argc – : number of remaining arguments

  • physical – : flag indicating physical (-P) or logical (-L) resolution

Returns:

0 on success, 1 on failure

static int detect_option(char *option)

detect if the string correspond to an option

Parameters:
  • option – : a string from *argv

Returns:

0 | 1

int builtin_cd(struct s_shell *shell, int argc, char **argv)

Change the shell’s current working directory.

Usage: cd [-L|-P] [--] [dir]

With no argument, changes to $HOME. The argument - switches to the previous directory ($OLDPWD) and prints the new path. -- ends option parsing so a directory literally named -foo can be reached.

Option

Effect

-L

Logical: keep symlinks in PWD (default).

-P

Physical: resolve symlinks via realpath.

After a successful change, OLDPWD is set to the previous PWD and PWD is updated.

Examples:

cd                       # go to $HOME
cd /tmp                  # absolute path
cd ..                    # parent directory
cd -                     # back to previous directory, prints it
cd -P /var/run           # follow symlinks
cd -- -weird-dir-name    # directory whose name starts with '-'

Returns:

0 on success, 1 on filesystem error (no such directory, not a directory, permission denied, name too long, HOME/OLDPWD unset), 2 on usage error (invalid option, too many arguments).

echo

Implementation of the echo builtin command for the 42sh shell.

Author

jguillem

Functions

static int is_valid_option(char *token, int *nl, int *escape)

check if the option is -e, -E, -N or a combination thereof

Parameters:
  • token – : the token to analyze

  • nl – : pointer to int to know the trailing newline status

  • escape – : pointer to int to know the backslash interpretation status

Returns:

1 | 0

static int is_valid_symbol(char c, char symbols[])

check if the symbol c own to the symbols list

Parameters:
  • c – : symbol to check

  • symbols – : list of valid symbols

Returns:

0 | 1

static void display_base(char **scout, int base)

: display the char corresponding to the base number given

Parameters:
  • scout – : the pointer of pointer on the number

  • base – : the input base

static int display_token(char *token, int last, int escape)

display the token

Parameters:
  • token – : char *

  • last – : flag to know if a trailing space is necessary

  • escape – : flag to know if there is a backslash interpretation

Returns:

0 | 1

static int report_write_error(void)

Force buffered output to fd 1 and report any write failure.

Bash-posix-compatible behaviour: fd 1 may have been closed by a >&- redirection, in which case the buffered stdio writes only surface as errors once we flush. clearerr() resets the stream so a later builtin (after the redirection is restored) can write again.

Returns:

0 on success, 1 if a write error was detected.

int builtin_echo(struct s_shell *shell, int argc, char **argv)

Print arguments separated by spaces, followed by a newline.

Usage: echo [-neE] [arg ...]

Options may be combined (e.g. -nE). Unknown letters disable option parsing for that token, so -x is printed literally.

Option

Effect

-n

Suppress the trailing newline.

-e

Interpret backslash escapes (\n, \t, \xNN, …).

-E

Disable backslash interpretation (default).

Examples:

echo hello world         # hello world
echo -n no-newline       # no-newline
echo -e 'a\tb\nc'        # a<TAB>b<NEWLINE>c
echo -E 'literal\\n'     # literal\n

Returns:

Always 0.

exit

Implementation of the exit builtin command for the 42sh shell.

Author

zweng

Functions

static int is_numeric(const char *str)

Check if a string is a valid numeric argument.

Allows optional leading ‘+’ or ‘-’, followed by digits only. Also rejects values that overflow long.

Parameters:
  • str – The string to check.

Returns:

1 if numeric, 0 otherwise.

int builtin_exit(struct s_shell *shell, int argc, char **argv)

Exit the shell.

Usage: exit [n]

If n is supplied, the shell exits with status n & 0xFF (POSIX truncation to 8 bits). With no argument, exits with the status of the most recently executed command ($?).

Examples:

exit                     # exit with $?
exit 0                   # exit success
exit 42                  # exit with status 42
exit -1                  # exit with status 255 (-1 & 0xFF)
exit foo                 # error: "numeric argument required", exit 2
exit 1 2                 # error: "too many arguments", returns 1

Returns:

On success the shell terminates and never returns to the caller. On usage error: 2 (non-numeric argument) or 1 (more than one argument — the shell is not exited in this case).

fg

fg [spec] - resume a stopped job in the foreground.

Author

pulgamecanica

Functions

static void no_such_job(const char *spec)
int builtin_fg(t_shell *shell, int argc, char **argv)

history

Implementation of the history builtin command for the 42sh shell.

Author

pulgamecanica

Functions

static void print_entries(HIST_ENTRY **list, int start, int end)

Print the command history list.

Prints all entries of the history

Parameters:
  • list – The list of history entries obtained from readline’s history_list()

  • start – The starting index in the history list to print from

  • end – The ending index in the history list to print to (exclusive)

int builtin_history(struct s_shell *shell, int argc, char **argv)

Print the command history.

Usage: history [n]

With no argument, prints every entry currently held by readline. With a positive integer n, prints only the last n entries. Non-positive n prints nothing. Each line is <index> <command> where <index> starts at history_base (typically 1).

Examples:

history                  # print full history
history 5                # print last 5 commands

Returns:

Always 0.

jobs

jobs builtin - list active background jobs.

Author

pulgamecanica

Functions

int builtin_jobs(t_shell *shell, int argc, char **argv)

type

Implementation of the type builtin command for the 42sh shell.

Author

zweng

Defines

TYPE_F_T
TYPE_F_P
TYPE_F_A

Functions

static void type_invalid_option(char opt)

Report an unrecognised option letter on stderr.

static int parse_type_opts(int argc, char **argv, int *flags)

Parse leading -t/-p/-a options (combinable; -- ends parsing).

Parameters:
  • argc – Argument count.

  • argv – Argument vector.

  • flags – Out: bitmask of TYPE_F_* for the options seen.

Returns:

Index of the first name argument, or -1 on an invalid option.

static void type_free_dirs(char **dirs)

Free a NULL-terminated string array.

static char *type_join(const char *dir, const char *name)

Build “dir/name”. Caller frees the result.

static int type_path_matches(t_shell *shell, const char *name)

Print every location holding an executable name.

A name containing ‘/’ is probed directly; otherwise every $PATH directory is walked and each match printed.

Returns:

1 if at least one match was printed, 0 otherwise.

static int type_kind(t_shell *shell, const char *name)

type -t: print the one-word category (builtin/file).

static int type_path(t_shell *shell, const char *name)

type -p: print the resolved path of a disk command only.

static int type_all(t_shell *shell, const char *name)

type -a: print the builtin entry and every PATH match.

static int type_default(t_shell *shell, const char *name)

Default form: “<name> is a shell builtin” / “<name> is <path>”.

static int type_dispatch(t_shell *shell, const char *name, int flags)

Route one name to the handler selected by the parsed flags.

int builtin_type(struct s_shell *shell, int argc, char **argv)

Describe how each name would be interpreted as a command.

Usage: type [-tpa] name [name ...]

Option

Effect

(none)

name is a shell builtin / name is /path / name not found

-t

Print one word: builtin or file (nothing if not found).

-p

Print the $PATH resolution of a disk command only.

-a

Print the builtin entry and every $PATH match.

Options may be combined (-ta); -- ends option parsing. An unknown option is reported on stderr and yields exit status 2.

Examples:

type cd ls bogus
# cd is a shell builtin
# ls is /usr/bin/ls
# bogus not found
type -t ls        # file
type -p ls        # /usr/bin/ls

Returns:

0 if every name was identified, 1 if any name was not found, 2 on an invalid option.