Code style

The primary goal of toybox is _simple_ code. Keeping the code small is second, with speed and lots of features coming in somewhere after that. (For more on that, see the design page.)

A simple implementation usually takes up fewer lines of source code, meaning more code can fit on the screen at once, meaning the programmer can see more of it on the screen and thus keep more if in their head at once. This helps code auditing and thus reduces bugs. That said, sometimes being more explicit is preferable to being clever enough to outsmart yourself: don't be so terse your code is unreadable.

Toybox has an actual coding style guide over on the design page, but in general we just want the code to be consistent.

Building Toybox

Toybox is configured using the Kconfig language pioneered by the Linux kernel, and adopted by many other projects (buildroot, OpenEmbedded, etc). This generates a ".config" file containing the selected options, which controls which features are included when compiling toybox.

Each configuration option has a default value. The defaults indicate the "maximum sane configuration", I.E. if the feature defaults to "n" then it either isn't complete or is a special-purpose option (such as debugging code) that isn't intended for general purpose use.

For a more compact human-editable version .config files, you can use the miniconfig format.

The standard build invocation is:

Type "make help" to see all available build options.

The file "configure" contains a number of environment variable definitions which influence the build, such as specifying which compiler to use or where to install the resulting binaries. This file is included by the build, but accepts existing definitions of the environment variables, so it may be sourced or modified by the developer before building and the definitions exported to the environment will take precedence.

(To clarify: ".config" lists the features selected by defconfig/menuconfig, I.E. "what to build", and "configure" describes the build and installation environment, I.E. "how to build it".)

By default "make install" puts files in /usr/toybox. Adding this to the $PATH is up to you. The environment variable $PREFIX can change the install location, ala "PREFIX=/usr/local/bin make install".

If you need an unstripped (debug) version of any of these binaries, look in generated/unstripped.

Running a command

main

The toybox main() function is at the end of main.c at the top level. It has two possible codepaths, only one of which is configured into any given build of toybox.

If CONFIG_SINGLE is selected, toybox is configured to contain only a single command, so most of the normal setup can be skipped. In this case the multiplexer isn't used, instead main() calls toy_singleinit() (also in main.c) to set up global state and parse command line arguments, calls the command's main function out of toy_list (in the CONFIG_SINGLE case the array has a single entry, no need to search), and if the function returns instead of exiting it flushes stdout (detecting error) and returns toys.exitval.

When CONFIG_SINGLE is not selected, main() uses basename() to find the name it was run as, shifts its argument list one to the right so it lines up with where the multiplexer function expects it, and calls toybox_main(). This leverages the multiplexer command's infrastructure to find and run the appropriate command. (A command name starting with "toybox" will recursively call toybox_main(); you can go "./toybox toybox toybox toybox ls" if you want to...)

toybox_main

The toybox_main() function is also in main,c. It handles a possible --help option ("toybox --help ls"), prints the list of available commands if no arguments were provided to the multiplexer (or with full path names if any other option is provided before a command name, ala "toybox --list"). Otherwise it calls toy_exec() on its argument list.

Note that the multiplexer is the first entry in toy_list (the rest of the list is sorted alphabetically to allow binary search), so toybox_main can cheat and just grab the first entry to quickly set up its context without searching. Since all command names go through the multiplexer at least once in the non-TOYBOX_SINGLE case, this avoids a redundant search of the list.

The toy_exec() function is also in main.c. It performs toy_find() to perform a binary search on the toy_list array to look up the command's entry by name and saves it in the global variable which, calls toy_init() to parse command line arguments and set up global state (using which->options), and calls the appropriate command's main() function (which->toy_main). On return it flushes all pending ansi FILE * I/O, detects if stdout had an error, and then calls xexit() (which uses toys.exitval).

Infrastructure

The toybox source code is in following directories:

Adding a new command

To add a new command to toybox, add a C file implementing that command to one of the subdirectories under the toys directory. No other files need to be modified; the build extracts all the information it needs (such as command line arguments) from specially formatted comments and macros in the C file. (See the description of the "generated" directory for details.)

Currently there are five subdirectories under "toys", one for commands defined by the POSIX standard, one for commands defined by the Linux Standard Base, an "other" directory for commands not covered by an obvious standard, a directory of example commands (templates to use when starting new commands), and a "pending" directory of commands that need further review/cleanup before moving to one of the other directories (run these at your own risk, cleanup patches welcome). These directories are just for developer convenience sorting the commands, the directories are otherwise functionally identical. To add a new category, create the appropriate directory with a README file in it whose first line is the description menuconfig should use for the directory.)

An easy way to start a new command is copy the file "toys/example/hello.c" to the name of the new command, and modify this copy to implement the new command (more or less by turning every instance of "hello" into the name of your command, updating the command line arguments, globals, and help data, and then filling out its "main" function with code that does something interesting).

You could also start with "toys/example/skeleton.c", which provides a lot more example code (showing several variants of command line option parsing, how to implement multiple commands in the same file, and so on). But usually it's just more stuff to delete.

Here's a checklist of steps to turn hello.c into another command:

Headers.

Commands are implemented as self-contained .c files, and generally don't have their own .h files. If it's common code put it in lib/, and if it's something like a local structure definition just put it in the command's .c file. If it would only ever be #included from one place, inline it. (The line between implementing multiple commands in a C file via OLDTOY() to share infrastructure and moving that shared infrastructure to lib/ is a judgement call. Try to figure out which is simplest.)

The top level toys.h should #include all the standard (posix) headers that any command uses. (Partly this is friendly to ccache and partly this makes the command implementations shorter.) Individual commands should only need to include nonstandard headers that might prevent that command from building in some context we'd care about (and thus requiring that command to be disabled to avoid a build break).

Target-specific stuff (differences between compiler versions, libc versions, or operating systems) should be confined to lib/portability.h and lib/portability.c. (There's even some minimal compile-time environment probing that writes data to generated/portability.h, see scripts/genconfig.sh.)

Only include <linux/*.h> headers from individual commands (not from other headers), and only if you really need to. Data that varies per architecture is a good reason to include a header. If you just need a couple constants that haven't changed since the 1990's, it's ok to #define them yourself or just use the constant inline with a comment explaining what it is. (A #define that's only used once isn't really helping.)

Top level directory.

This directory contains global infrastructure.

toys.h

Each command #includes "toys.h" as part of its standard prolog. It may "#define FOR_commandname" before doing so to get some extra entries specific to this command.

This file sucks in most of the commonly used standard #includes, so individual files can just #include "toys.h" and not have to worry about stdargs.h and so on. Individual commands still need to #include special-purpose headers that may not be present on all systems (and thus would prevent toybox from building that command on such a system with that command enabled). Examples include regex support, any "linux/" or "asm/" headers, mtab support (mntent.h and sys/mount.h), and so on.

The toys.h header also defines structures for most of the global variables provided to each command by toybox_main(). These are described in detail in the description for main.c, where they are initialized.

The global variables are grouped into structures (and a union) for space savings, to more easily track the amount of memory consumed by them, so that they may be automatically cleared/initialized as needed, and so that access to global variables is more easily distinguished from access to local var