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.
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.
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...)
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).
The toybox source code is in following directories:
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:
First "cp toys/example/hello.c toys/other/yourcommand.c" and open the new file in your preferred text editor.
Note that the name of the new file is significant: it's the name of the new command you're adding to toybox. The build includes all *.c files under toys/*/ whose names are a case insensitive match for an enabled config symbol. So toys/posix/cat.c only gets included if you have "CAT=y" in ".config".
Change the one line comment at the top of the file (currently "hello.c - A hello world program") to describe your new file.
Change the copyright notice to your name, email, and the current year.
Give a URL to the relevant standards document, where applicable. (Sample links to SUSv4, LSB, IETF RFC, and man7.org are provided, feel free to link to other documentation or standards as appropriate.)
Update the USE_YOURCOMMAND(NEWTOY(yourcommand,"blah",0)) line. The NEWTOY macro fills out this command's toy_list structure. The arguments to the NEWTOY macro are:
the name used to run your command
the command line argument option parsing string (0 if none)
a bitfield of TOYFLAG values (defined in toys.h) providing additional information such as where your command should be installed on a running system, whether to blank umask before running, whether or not the command must run as root (and thus should retain root access if installed SUID), and so on.
Change the kconfig data (from "config YOURCOMMAND" to the end of the comment block) to supply your command's configuration and help information. The uppper case config symbols are used by menuconfig, and are also what the CFG_ and USE_() macros are generated from (see [TODO]). The help information here is used by menuconfig, and also by the "help" command to describe your new command. (See [TODO] for details.) By convention, unfinished commands default to "n" and finished commands default to "y", so "make defconfig" selects all finished commands. (Note, "finished" means "ready to be used", not that it'll never change again.)
Each help block should start with a "usage: yourcommand" line explaining any command line arguments added by this config option. The "help" command outputs this text, and scripts/config2help.c in the build infrastructure collates these usage lines for commands with multiple configuration options when producing generated/help.h.
Change the "#define FOR_hello" line to "#define FOR_yourcommand" right
before the "#include
Update the GLOBALS() macro to contain your command's global variables. If your command has no global variables, delete this macro.
Variables in the GLOBALS() block are are stored in a space saving union of structures format, which may be accessed using the TT macro as if TT were a global structure (so TT.membername). If you specified two-character command line arguments in NEWTOY(), the first few global variables will be initialized by the automatic argument parsing logic, and the type and order of these variables must correspond to the arguments specified in NEWTOY(). (See lib/args.c for details.)
NOTE: the GLOBALS() block creates a "this.filename" entry in generated/globals.h. If your toys/*/filename.c does not match the first command name, you'll need to "#define TT this.filename" yourself before #including toys.h if you want to use TT globals
Rename hello_main() to yourcommand_main(). This is the main() function where execution of your command starts. Your command line options are already sorted into this.optflags, this.optargs, this.optc, and the GLOBALS() as appropriate by the time this function is called. (See get_optflags() for details.)
Switch on TOYBOX_DEBUG in menuconfig (toybox global settings menu) the first time you build and run your new command. If anything is wrong with your option string, that will give you error messages.
Otherwise it'll just segfault without explanation when it falls off the end because it didn't find a matching end parantheses for a longopt, or you put a nonexistent option in a square bracket grouping... Since these kind of errors can only be caused by a developer, not by end users, we don't normally want runtime checks for them. Once you're happy with your option string, you can switch TOYBOX_DEBUG back off.
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.)
This directory contains global infrastructure.
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