Skip to content
RhysUPublic

About

A shebang-friendly script for "interpreting" single C99, C11, and C++ files, including rcfile support.

Resources

Stars

114 stars

Watchers

6 watching

Forks

Latest commit

 

History

162 Commits

Folders and files

Repository files navigation

c99sh

Build Status

Overview

c99sh shortens the edit-compile-run loop when prototyping by "interpreting" single C99, C11, C23, and C++ files. It is shebang-friendly and reads rcfiles.

For example, with this ~/.c99shrc

-Wall -g -O2
#include <stdio.h>

and c99sh in your path, hello runs as expected:

#!/usr/bin/env c99sh
int main()
{
    puts("Hello, world!");
}

Simple Tasks

Combine options with HERE documents:

$ c99sh -ms <<HERE
puts("Hello, world!");
HERE

Add lines with -e. Unlike Perl's -e, standard input is still read:

$ c99sh -e 'int main()' -e '{}' </dev/null

Run c99sh foo.c when foo.c has no shebang line. Add -v to see the compilation command.

Complicated Tasks

Rcfiles simplify using libraries with richer data structures. c99shrc.example enables GSL, GLib, and SQLite via pkg-config.

One-off scripts can move directly into C ABI code, skipping a {Python,Octave,R}-to-C translation and debugging phase. Compare the Octave version of some simple logic with the c99sh version, which needs only a few one-time additions to your ~/.c99shrc.

A more entertaining example computes π by OpenMP-enabled Monte Carlo, screaming like a banshee on all your cores. Its c99shrc adds -fopenmp and omp.h:

#!/usr/bin/env c99sh

int main(int argc, char *argv[])
{
    long long niter = argc > 1 ? atof(argv[1]) : 100000;
    long long count = 0;

    #pragma omp parallel
    {
        unsigned int seed = omp_get_thread_num();

        #pragma omp for reduction(+: count) schedule(static)
        for (long long i = 0; i < niter; ++i) {
            const double x = rand_r(&seed) / (double) RAND_MAX;
            const double y = rand_r(&seed) / (double) RAND_MAX;
            count += sqrt(x*x + y*y) < 1;
        }

    }

    printf("%lld: %g\n", niter, M_PI - 4*(count / (double) niter));
}

Reference

$ c99sh -h
Usage: c99sh [OPTION]... [--] PROGRAM [PROGRAMOPTION]...
  or:  c99sh [OPTION]... [--] -       [PROGRAMOPTION]...
  or:  c99sh [OPTION]... [--]
Compile c99 PROGRAM, or standard input, and run it supplying [PROGRAMOPTION]...
If compilation is successful, the exit status is that of PROGRAM.

Example:
  echo 'puts("Hello, world!");' | c99sh -ms

Source options:
  -e LINE  Prepend LINE to any input; often used in conjunction with -ms
  -m       Surround input with main(argc, argv) declaration
  -t STMT  Follow input with main(argc, argv) containing STMT;
  -s       Include all standard C, not C++, headers for the language
  -S       Include all standard C++ library headers for the language

Build options:
  -l LIB   Link to the library LIB
  -p PKG   Make PKG headers and libraries available to PROGRAM via pkg-config(1)
  -F OPT   Add '-OPT' to $CFLAGS when using $CFLAGS during compilation
  -L OPT   Add '-OPT' to $LDFLAGS when using $LDFLAGS during linking
  -W       Enable and enforce warnings; equivalent to -F Wall -F Werror

Output options:
  -x EXE   Save the compiled executable as EXE instead of running it
  -E       Print generated source to standard output instead of compiling
  -v       Increase verbosity; may be supplied multiple times
  -h       Display this help message

Rcfile processing:
  -r RC    Load compilation settings from RC suppressing normal rcfile search
  -R       Suppress rcfile loading; equivalent to -r /dev/null

  An rcfile 'c99shrc' controls compilation if present in the same directory
  as PROGRAM, or in the current working directory when processing standard
  input.  Otherwise, if it exists, the file ~/.c99shrc controls compilation.

Rcfile syntax:
  Each non-blank line must be a // comment, compiler flags, a preprocessor
  directive, a C++ using or namespace directive, a pkg-config request, linker
  flags, or a source, object, or archive file to build alongside PROGRAM.

    // Single-line comment
    -O2 -Wall
    #include <sqlite3.h>
    using std::vector
    namespace fs = std::filesystem
    pkg-config sqlite3
    -L/foo/lib -lfoo -lm
    /bar/extra_source.c
    /bar/libextra.a

Compiling Source with a Shebang

Three lines let ./shebang.c run as a script and gcc shebang.c compile it:

#if 0
exec c99sh "$0" "$@"
#endif

#include <stdio.h>

int main(int argc, char *argv[])
{
    for (int i = 1; i < argc; ++i) {
        printf("Hello, %s!\n", argv[i]);
    }
}

Add -t to test valid C source files quickly. A shebang cannot pass -t reliably because of argument splitting, but this pattern can:

#if 0
exec c99sh -t 'test()' "$0" "$@"
#endif

#include <stdio.h>

int logic()
{
    return 42;
}

static void test()
{
    printf("%d\n", logic());
}

Testing in this manner resembles how folks use Python's __main__ inside libraries.

C11 and C23

C11 and C23 can be used via symlinks named c11sh and c23sh with rcfiles like c11shrc and c23shrc.

C++

Invoke c99sh through a copy or symlink named cxxsh to write C++. Rcfiles are then named like cxxshrc and also accept directives like using namespace std and namespace fb = foo::bar. See cxx/hello with cxx/cxxshrc for hello world. See cxx/shebang.cpp and cxx/quicktest.cpp for dual shebang/compiled idioms.

Eigen supports pkg-config, so cxxsh -p eigen3 myprogram builds and runs a one-off Eigen program. The right cxxshrc turns it into a script. C++ compiles noticeably slower than C. Save the binary with -x when recompiling costs too much.

Credits

c99sh grew from "Compiling C Programs via Here Document" in Ben Klemens's 21st Century C. That section is available online. elsamuko/cppsh also prompted it.

mcandre and jtsagata suggested compiling source with a shebang. Thank you both. I did not think three clean lines could do it.

mattapiroglu added -e. flipcoder added -l. ProducerMatt added -F and -L.

About

A shebang-friendly script for "interpreting" single C99, C11, and C++ files, including rcfile support.

Resources

Stars

114 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages