Documentation for CCshTM Version 2.1c, "The Bourne Shell Compiler"

(c) Copyright 1985-2013 Comeau Computing. All Rights Reserved
CCsh is a trademark of Comeau Computing.
UNIX is a registered trademark of AT&T.

I. Why use CCsh?

The UNIX environment is a multi-faceted concept. On the one hand, there is the UNIX kernel, the general purpose operating system, the heart of everything. On the other hand, there is the rich command set (e.g. any program run from disk) used by many programmers. One of these commands, called the shell, is a powerful interface between the programmer and the UNIX environment. The shell allows the programmer to use the command set in a creative way as well as being able to "talk" to the kernel. Many operating systems and environments support a simple interface of some sort, and most have the facility to run "batch" commands. The shell is different and goes way beyond this ability. Its features are comprised of looping and logic constructs found in most high-level languages. Shell programmers also have access to variables of their own naming. Also supported are features unique to the UNIX environment such as pipes, background processes, I/O redirection, etc. This all results in a language that is powerful and useful.

These features are enhanced even further since the shell is an interpreter. Therefore programmers can change their shell programs (also known as shell scripts) easily or can even run them interactively. Because of all these qualities, the shell is often favored over typical high-level languages, even C. In addition, the shell is very good for projects that are for demonstration purposes only, are still in the prototyping stage, have gone beyond their deadlines, or where the development time in a compiled language is inappropriate. So, as can be seen, the shell is very useful. On the negative side though, the shell is not as fast as a compiled language, nor does it allow a user to set attributes commonly given to executables (a.out type files) such as split instruction and data space, the sticky bit, or the setuid bit. Another side effect is that since shell scripts are just "text" files, they must be readable. For many projects this is undesirable, since security or proprietary code may be at issue. Until now, protecting source code has been considered futile since it requires shells or front-ends be run setuid as well as having to develop elaborate directory roadblock schemes.

It would seem that being able to compile shell scripts would remove all these restrictions. This is exactly what CCsh does. In fact, CCsh handles the problem in such a way that the removal of the negative aspects of shell programming goes beyond just fixing the problems mentioned. For instance, CCsh has a whole set of built-in commands that are normally fork()'ed by the shell. This does not happen when using CCsh. Instead CCsh reduces system and command overhead. This is of considerable importance since some commands may actually take more time to start up than their actual execution time would take! CCsh can increase shell script speed up to 50 times, although increases of 10 times are more typical.

For general information about CCsh, have a look at CCsh literature, the CCsh FAQ, the CCsh license agreement, the CCsh order worksheet and the CCsh online compiler.

II. Installation of CCsh

Your copy of CCsh has come on magnetic media such as a diskette, floppy tape, or mag tape. The commands for transferring CCsh to your machine are written on the face of that media. Installation typically requires that you give the media to your System's Administrator, since this person needs to invoke a super-user shell then run the command(s) written on the transfer media. Installation typically involves logging into your system as root. Then after you are prompted with the '#' character, insert the installation disk (there is only one disk) into the disk drive, and type using lower-case characters:

cd /
cpio -ivcd < /dev/rdiskette

The device name of the install medium that will be read from will vary from machine to machine, so it may not be /dev/rdiskette on your machine. The cpio should take less than one minute and will report the names of the files it is copying along with a block count indicating how many blocks (usually around 420) it copied from the diskette to the hard disk. Any other message(s) will most likely indicate an error. In such a situation, please contact your System Administrator for assistance.

A list of the files CCsh uses are listed in the `man' page for CCsh. This can be found in Appendix A. In some rare circumstances, your needs may require that you remove CCsh from your system. This can be accomplished by logging in as root and running /usr/bin/rmccsh at the '#' prompt. rmccsh will end by emitting the message Remove of CCsh complete. If not, contact your Systems Administrator.

Finally, in some cases, a port of CCsh may need to be done via modem using your actual machine. In these situations, installation will be accomplished by Comeau Computing. From time to time, a new or existing customer will require a port of CCsh to another machine. There will probably be a good chance that such a port already exists. In the case where it doesn't, Comeau Computing will determine if the port is possible. If the port presents no problems, we will discuss our porting method with the customer. This will usually require that the customer have a modem and a C compiler.

Also recently, we are now distributing CCsh electronically in most cases, normally through ftp. Follow the instructions provided with the email you received.

III. Requirement for the use of CCsh

The only prerequisite for using CCsh is that the machine support the software development system (the cc command in particular). Executable programs generated from CCsh will typically be around 50K, so there are no memory requirements. Disk space for CCsh (the compiler, library, and header file) is 200K bytes.

In addition, there are no requirements such as setting up special "environment variables", files, or directories since CCsh does not need any "external" information to run.

IV. The ARGVSIZE, ENVPSIZE and WAITSIZE variables

Normally, upon start-up, the shell must set up its environment list and consolidate the argument list that's been passed to it. CCsh is no different in this respect. More explicitly, when a shell script is compiled by CCsh, the C code expects that the environment variable list will have a finite length. Also, a CCshd program will build an argument list of finite length for the commands it invokes. In addition, the invocation of background commands also requires that information on background processes be stored. The sizes of the variables CCsh uses for these lists (Argv, Lenviron, and Backgrounds) are controlled by the pre-processor definitions ARGVSIZE, ENVPSIZE, and WAITSIZE. CCshd programs will terminate with an error message informing you that you must increase the size of one of these defines if any list has overflowed. This can be accomplished easily by editing the generated C code output from CCsh and changing the appropriate lines where ARGVSIZE, ENVPSIZE, and WAITSIZE are defined.

For instance, the shell script

             for i in $*
             do
                 echo i=$i
             done

would generate the C code shown in Appendix B. If you were to find that this simple shell script were being passed many arguments, you would increase the size of ARGVSIZE shown in line 10 of Appendix B to something more appropriate.

Unfortunately, this requires you to be aware of what's going on and that you remember to edit the generated C file. It's also hard to generate systems automatically with commands like make if you're required to do some hand-editing. CCsh makes this task a bit easier by allowing you to specify the size of ARGVSIZE, ENVPSIZE, and WAITSIZE as command line parameters when it is invoked. See the man page presented in the appendix for details on using these options.

V. Shell scripts that don't use argv and envp

There are a few subroutine calls at the beginning of the main procedure of C code that is generated by CCsh. The first one, setupargs, sets up the CCshd program's argument list. The second subroutine, setupenv, sets up the CCshd program's environment list. Since there are many shell scripts that do not use or care about either of these "external" variables (the argument list or the environment), the programmer can remove either or both of these calls if they are not needed, thus reducing startup overhead for the C code when it executes, as well as obtaining a smaller object code size. Removing these lines can be as simple as editing the .c file or using the -E and -A options explained in the `man' page found in Appendix A.

NOTE: Do not remove the calls to setupargs or setupenv if the program uses argv or envp.

NOTE: Do not remove the call to setupenv if any of the program's children will use envp.

VI. The Dot Command

The Dot command (`.') is now supported in CCsh V1.4+. While its syntax and functionality is the same as the shells, its implementation it different. The difference in the implementation is brought about because of the differences between an interpreted environment and a compiled environment. When the shell is running a shell script, it doesn't care or know that the next line might be a `dot' command. In other words, the dot'ing normally doesn't happen until a dot command is encountered and executed. With CCsh the dot'ing must occur during compilation of the shell script, not during execution time. This requires that
  1. the file being dot'ed needs to exist during compilation,
  2. the file being dot'ed cannot be expected to change after the compilation, and
  3. dot commands using variable substitution for the file name cannot wait until runtime for the file name to be determined.
In the latter case, CCsh will pick up the last assignment of the variable that occurred in the shell script, regardless of conditional statements enclosing the assignment. Alternately, if this presents a problem, or if the variable is not explicitly set in the shell script, it may be specified and assigned on the command line during compilation. Please see Appendix A, the man page, for further instructions.

VII. The Eval Command

The eval command is now supported in CCsh V1.4+. Like the Dot command its syntax and functionality is the same as the shell, but its implementation is different. Again, the difference in the implementation is brought about because of the differences between an interpreted environment and a compiled environment. The problem with eval is that it supports self-modifying code. This is no big problem except in one situation. For example, in the following command lines:

         PIPEIT='| od -bc'
          ...
         eval cat file $PIPEIT
the use of the pipe operation is not known until execution time since there may be conditional statements between the assignment of PIPEIT and the eval command. If CCsh is to determine this at execution time, then it needs to have a completely functional shell interpreter inside every executable file it produces. This certainly defeats the point of CCsh altogether. The use of eval in all other situations is allowed and supported.

VIII. Additions

Versions of CCsh previous to V1.4 did not support the use of shell functions, (command-list), {command-list}, the IFS variable, digit redirection (i.e. 2>&1), and ${parameter??word} initializations whatsoever. All these constructs are now fully supported making CCsh completely compatible with the System V Release 2 shell.

IX. Built-in Commands

Various UNIX commands have been built into CCsh. This avoids the overhead of an exec() and/or fork() altogether. In addition, many of CCshs built-in commands are faster than their UNIX equivalents. The commands currently built-in include basename, cat, cp, dirname, dsort, echo, expr, false, getopt, head, od, page, pr, pwd, split, tail, tee, test, touch, true, and wc. Future releases of CCsh will have even more built-in commands. Also, with the introduction of CCsh V1.4 users now have the ability to use site-specific built-in commands of their own choosing. This ability to have custom built-in commands will also make non-UNIX ports of CCsh as well as ports of CCsh generated C code easier. Appendix C provides sample code and explanations for creating user built-ins.

X. Error Messages

Compile Time Errors

Usage: ccsh [ ccsh options ] shell_file
When you have specified an incorrect argument to CCsh this message will print informing you of the correct usage. Please refer to the man page for more information.

ccsh: -OPT option in argument 'ARG' invalid
The option character OPT within the argument ARG is not a valid option character to specify on the command line.

ccsh: no input file specified.
CCsh could not find the name of the shell_file to compile while parsing the command line.

ccsh: warning: filename 'FILE' > than X characters.
An executable file created by CCsh will have the characters ".exe" appended to it. If the name of the input shell file is larger than X characters (typically 10), the file name will be truncated.

ccsh: Shell script 'FILE' does not exist.
The input shell file could not be found.

ccsh: Shell script 'FILE' is not readable.
CCsh could not read the input shell file.

ccsh: Cannot stat 'FILE'.
Contact Comeau Computing if you receive this error.

ccsh: 'FILE' is not a regular file.
You are trying to compile a shell file that is a directory or device, and not a regular file.

ccsh: Cannot open shell script 'FILE'.
The input shell file cannot be opened for reading. Check the path name for accessibility and/or existence.

ccsh: Cannot open C output file 'FILE' for writing.
The output shell file cannot be opened for writing. Check the path name for accessibility and/or existence.

CCsh ends with NUMBER errors and NUMBER warnings.
CCsh always prints this message when it ends.

syntax error: `KEYWORD' was expected on line LINE
Your shell script has a syntax error that was created by a missing keyword, such as a missing then in an if statement.

warning: `KEYWORD' found on line LINE is not supported
Your shell script is trying to use a feature of the shell that is not yet supported by CCsh. This should no longer occur in versions after 1.3l. Please contact Comeau Computing if it does.

warning: `break' is not enclosed in a `for' or `while' on line LINE
A `break' statement must be within a for or while statement for it to have any effect. It is a syntax error otherwise.

warning: `continue' is not enclosed in a `for' or `while' on line LINE
A `continue' statement must be within a for or while statement for it to have any effect. It is a syntax error otherwise.

warning: `[' command on line LINE does not have a matching `]'
A bracketed `test' command requires a closing `]' character.

warning: `trap' command on line LINE should be single quoted
The `trap' command in a compiled environment must operate slightly different than when used in an interpretive environment. This is because `sh' scans a command and then executes it. With a `trap', a second scan is performed if the `sh' process receives a signal that is to be trapped. In this scenario, the shell will attempt to rescan the line for variable substitutions, etc. Now, since CCsh is not an interpreter, there is no way to do the first scan that the shell normally does. That is, CCsh will not scan the commands in the trap string until the appropriate signal has been received. This in effect results in the trap string being single quoted, since the shell would not do any substitutions on a single quoted trap string until the trap has been "sprung". Therefore, any double quoted trap string(s) will result in a warning message from CCsh and it's up to the programmer to ensure that the trap string is acceptable.

syntax error: unexpected `KEYWORD' found on line LINE
A misplaced keyword was encountered.

Compiler error in 'screener'
This should never occur. If it does, contract Comeau Computing.

Program will not continue.
A error has occurred and CCsh is aborting itself.

XX: malloc space used up
This message should never occur. If it does, contract Comeau Computing.

Line LINE too long.
The length of a shell input line may not typically be greater than 512 characters.

warning: Var table filled up.
CCsh records all variable assignments it passes so that the dot command can perform reliably in a compiled environment. If this message comes up, please contact Comeau Computing.

ccsh: Compile interrupted
CCsh received a signal (typically from the keyboard) causing it to abort.

ccsh: info: Buffer size of BUFFERSIZE being used
You have specified a -b option of BUFFERSIZE bytes on the command line. This is strictly informative.

ccsh: Buffer size must be >= 512 bytes.
You have specified a -b option on the command line, but the buffer needs to be at least 512 bytes long.

warning: `;;' was expected on line LINE
The shell does not require a closing `;;' before an `esac'. CCsh does not either, but it would prefer that you would.

Warning: Command line define of 'VARIABLE' set to NULL
You have chosen to define a shell variable on the command line (perhaps for use with the `.' command?), yet the variable was given no specific value.

Warning: Builtin table filled up.
CCsh only allows up to 50 user builtins at a time. Contact Comeau Computing if this presents a problem.

warning: C compiler will generate a "warning: statement not reached" message
Because CCsh runs in a variety of environments, it generates very portable C code. One anomaly of this code generation occurs during if/then/else processing in which CCsh generates a break statement followed by a goto statement. This causes most C compilers to give a non-fatal warning message.

warning: unreachable code on line LINE
CCsh encountered code after a non-nested `exit' statement.

Error: line LINE: Trying to create more than NUM functions: use -F flag.
CCsh usually only allows 50 functions to be defined in a shell script at any one time. Use of the -F option can be used to bypass this default.

warning: substitution of 'VARIABLE' on line LINE yields null.
or
Error: $VARIABLE on line LINE doesn't expand to anything.
CCsh was processing a `.' command involving variable substitution and discovered that the variable being interrogated had no value or was never defined.

Warning: Usage of 'VARIABLE' on line LINE is unpredictable.
During parsing, CCsh records variable assignments so that the `.' command can function correctly. This message is emitted when CCsh discovers that a variable has been assigned to more than once. In such situations CCsh discards previous assignments to the variable and will only "remember" the last assignment value.

error: bad file number 'FILE_NUMBER' on line LINE
or
error: bad number 'FILE_NUMBER' on line LINE
A file number used in digit redirection (say 2>&1) can only consist of the digits 1 through 9, or '-'. Any other value is incorrect.

warning: EOD in here document on line LINE may be premature
Under certain circumstances, the shell allows the end-of-data marker in a `here document' (`<<' redirection) to occur after a tab character rather than as the first character on a line. CCsh will also accept this syntax but issues a warning in case that is not what you wanted to do.

warning: case on line LINE has no statements
Better safe than sorry type of message...

warning: `.' (dot) command on line LINE not fully supported.
See Section VI for a discussion of this.

Warning: nothing's being `.'ed on line LINE
CCsh encountered a `.' command yet there was no file name specified to dot.

Dotting ORIGFILE==>DOTFILE
CCsh prints this out to inform you that it is now processing a dotfile. The syntax specified in the shell script for the file is ORIGFILE. The actual file being used is DOTFILE. Note that ORIGFILE and DOTFILE need not be the same (because of variable substitutions).

Error: line LINE: 'DOTFILE' not found.
The DOTFILE specified on line LINE is invalid.

End of dotfile 'DOTFILE'
CCsh prints this out to inform you that it has finished processing the dotfile named in the message.

warning: Your use of `eval' on line LINE may not be supported. Check the manual.
Please see Section VII of this manual for further discussion.

Warning: nothing's being `eval'ed on line LINE
CCsh encountered a "null" `eval' statement.

`return' on line LINE does not occur in a function
A `return' statement must occur within a shell function.

warning: `return' on line LINE1 and LINE2
CCsh would like you to double check your `return' statements.

error: Single quoted string beginning on line LINE1 till line LINE2 is too long.
or
error: Double quoted string beginning on line LINE1 till line LINE2 is too long.
The output buffer for strings is usually 512 bytes long. This can be changed with the -b option to CCsh.

XI. Error Messages

Runtime Errors

com.exe: DIRECTORY: bad directory
Your executable shell script has attempted to `cd' to a non-existent or unsearchable directory.

Cannot pipe
Your executable shell script is implementing a pipeline but has opened too many file descriptors, hence cannot set up the pipe.

IDENTIFIER: is not an identifier
You have attempted to `export', `readonly', or `read' a shell variable but the variable does not have the correct form of an identifier.

Invalid type: NN
This should never occur. Contact Comeau Computing if you receive this message.

Cannot times
An error occurred while processing a `times' command.

pwd: Can't open 'DIRECTORY'
The `pwd' command attempted to print the current directory but failed.

Cannot fork 'COMMAND'
Your executable shell script could not run a command. Make sure the command exists and is executable. Otherwise, check with your Systems Administrator for system problems.

Discarding output from `command`
Your executable shell script only has a 2K buffer for `command` constructs. Any data over this limit is thrown away.

com.exe: Cannot get ulimit??
Your executable shell script could not obtain the current ulimit setting.

com.exe: Bad ulimit
Your executable shell script could not change the current ulimit setting.

Cannot get umask??
Your executable shell script could not obtain the current umask setting.

Bad umask
Your executable shell script could not change the current umask setting.

ccsh.com: setupenv: no more room for variables
Your executable shell script could not create a list of the current variables. The environment variable list can be increased as explained in the manual and in the man page if you get this message.

com.exe: VARIABLE: is read only
Your executable shell script has attempted to assign to or `unset' a shell variable that has the `readonly' attribute.

com.exe: cannot shift
Your executable shell script has attempted to do a `shift' command but there were not enough positional parameters left to fulfill the request.

com.exe: 'FILE': cannot open
Your executable shell script has attempted to perform redirection, but the file to be used for the redirection could not be opened.

com.exe: Error duping for 'FILE'
Your executable shell script has too many file descriptors open while attempting to redirect the standard output.

com.exe: 'FILE': cannot create
Your executable shell script could not create an output file while attempting to redirect the standard output. You may have too many file descriptors open. Also, check permissions on the directory that FILE would have been created in.

com.exe: 'FILE': cannot dup
Your executable shell script has too many file descriptors open while attempting to redirect the standard input.

com.exe: test: unknown operator 'OPERATOR'
The operator in a `test' expression is not a valid one.

com.exe: test: argument expected
Your executable shell script is running the `test' command and has encountered an invalid or incomplete expression for evaluation.

com.exe: bad trap number 'NUMBER' on line LINE
The signal number you have specified on the `trap' command is not numeric.

com.exe: bad trap on line LINE: cannot trap 11
The shell prohibits receiving signal #11 (a memory fault), so do we.

com.exe: TRAP_NUMBER: bad trap on line LINE
The signal number you have specified on the `trap' command is not a valid signal number.

com.exe: unset: bad argument count
Your executable shell script has executed the `unset' command but no shell variable(s) was specified for unsetting.

com.exe: malloc space used up
This message should rarely occur. If it does, contract Comeau Computing.

XII. Problems

Because CCsh must perform complicated logic as a compiler and since the shell is an interpreter with an "incomplete" grammar specification, situations will arise that will not compile and in rare circumstances the a.out file will not function properly. Therefore, it is suggested that you compile shell scripts AFTER they have been tested under the actual shell. If you still have a problem please notify Comeau Computing and save a copy of the shell script, C code, and object code before contacting us. Do not send any files, instead just send the line of code that's problematic.

XIII. Future Directions

CCsh is under continuous development and will be going through various phases of development. Some of this development depends upon your input.

If you have any questions or suggestions we can be reached here

Appendix A: CCsh MAN Page

CCsh(1)

CCsh(1)

Name
CCsh - Bourne Shell Compiler

Synopsis
ccsh [-cvVuAE] [-asize] [-esize] [-wsize] [-bbufsize] [-Bbuiltin] [-Ddefine] shell_file

Description
CCsh is a Bourne Shell Compiler which translates a shell file (better known as a shell script) into C language source or into the object code of your native machine to produce an executable equivalent of your shell script.

Normally, execution of ccsh will create both a .c and a .exe file. If the -c option is specified, only C code (.c) is generated. By default, the C file and/or object code are placed into the same directory as the shell script.

The -v option causes the shell source to be inserted into the generated C code as comments. If the -V option is used instead, the line numbers of the shell script are also printed along with comments.

C code generated by CCsh is normally sent to the .c file using buffered I/O. If this is not desired, the -u option will cause CCsh to write to the file without buffering. Since this degrades performance, this option should only be used for debugging purposes.

The -A and -E options allow you to control whether or not CCshd shell scripts will use their environment or argument list. If you use the -A and -E options, CCsh will not include the routines to handle the arguments and environment, respectively.

The -a, -e, and -w options allow you to override the default values for the ARGVSIZE, ENVPSIZE, and WAITSIZE variables within the .c file. These variables normally have values of 256, 256, and 9 respectively. The purpose of these variables is to control the sizes and storage area used by the argument list and environment variables.

Because of the limitations of some C compilers, CCsh will use an output line of 512 characters by default. If this default becomes too small (say you're printing out a screen-long string using echo), you can increase the limit by using the -b option. A buffer size must be used with this option.

When using user defined built-ins with CCsh you must use the -B option. The name of the built-in being used is specified along with the -B option. Please refer to Appendix C for more information. This option may be specified repeatedly.

The -D option is useful for defining shell variables so that the dot command (`.') can resolve names of files, at compile time, needing substitution. The name of the variable is placed along with this option. This option may be specified repeatedly.

Files
/usr/bin/ccsh Shell script which runs CCsh
/usr/bin/ccshcomp The compiler
/usr/lib/libccshrun*.a Runtime library(ies)
/usr/include/ccsh.h Header file used in CCsh'd programs

See Also
sh(1)
UNIX Shell Programming by Kochan and Wood

Example
To compile a shell script located in a file named example, type:

               ccsh example

This will create two files: example.c and example.exe.

Appendix B: Sample C file output

         1   /*
         2    *
         3    *  Generated by CCsh V2.1 (tm), "The Bourne Shell Compiler"
         4    *  CCsh is manufactured by Comeau Computing: comeaucomputing@gmail.com
         5    *
         6    *  Derived from '/tmp/argdump'
         7    *  Created: Tue Jan 26 14:31:00 1997
         8    */
         9
        10   #define ARGVSIZE     (256)
        11   #define ENVPSIZE     (256)
        12   #define WAITSIZE     (9)
        13
        14   #include <ccsh.h>
        15
        16   main(argc, argv, envp)
        17   int     argc;
        18   char    *argv[];
        19   char    *envp[];
        20   {
        21       CCshinit(ARGVSIZE);
        22       setupargs(argc, argv);      /* delete if arguments are not used  */
        23       setupenv(ENVPSIZE);         /* delete if environment is not used */
        24       setupsigs();
        25
        26       while (1) {
        27           Bufp = inarg(&Nest1,
        28               TEXT,               NCAT,   "$",
        29               TEXT,               CAT,    "*",
        30               ENDOFLIST,  FORK,   (char *)0
        31           );
        32           if (Bufp == (char *)0)
        33               break;
        34           setvar("i", Bufp);
        35
        36           rc = echo(
        37               TEXT,               NCAT,   "i",
        38               TEXT,               CAT,    "=",
        39               TEXT,               CAT,    "$i",
        40               ENDOFLIST,  FORK,   (char *)0
        41           );
        42       }
        43       Nest1 = (char **)0;
        44
        45       CCendup();
        46       exit(rc);
        47   }
        48   char    *(*Subcom[])() = {
        49           (char *(*)())0,
        50   };

Appendix C: Adding Builtin Commands

Adding your own built-in commands to CCsh can be very simple depending upon your needs. For instance, lets take a typical C program like example.c:
         $ cat example.c
         main()
         {
             printf("Hello, world\n");

             exit (0);
         }
         $
One would typically run this through the C compiler and place it into a shell script, say ex, by entering the text example, saving it, and running it. The ex script may then also be passed through CCsh. A sample session follows:
         $ cc -o example example.c
         $ cat ex
         example
         $ chmod 755 ex
         $ ex
         Hello, world
         $ ccsh ex # first case: example is fork()'ed

                       CCsh(tm) Version 2.1, "The Bourne Shell Compiler"
                (c) Copyright 1987, 1997 Comeau Computing.  All rights reserved.

         CCsh ends with 0 errors and 0 warnings.

         Compiling C code ...

         Creation of ex.exe finished.
         $ ex.exe
         Hello, world
         $ cat ex.c
         /*
          *
          *  Generated by CCsh V2.1 (tm), "The Bourne Shell Compiler"
          *  CCsh is manufactured by Comeau Computing: comeaucomputing@gmail.com
          *
          *  Derived from '/tmp/ex'
          *  Created: Tue Jan 26 17:57:47 1997
          */

         #define ARGVSIZE     (256)
         #define ENVPSIZE     (256)
         #define WAITSIZE     (9)

         #include <ccsh.h>

         main(argc, argv, envp)
         int     argc;
         char    *argv[];
         char    *envp[];
         {
             CCshinit(ARGVSIZE);
             setupargs(argc, argv);      /* delete if arguments are not used  */
             setupenv(ENVPSIZE);         /* delete if environment is not used */
             setupsigs();

             rc = simpcommand(
                 TEXT,           NCAT,   "example",
                 ENDOFLIST,      FORK,   (char *)0
             );

             CCendup();
             exit(rc);
         }
         char    *(*Subcom[])() = {
                 (char *(*)())0,
         };
If instead, we change main to a function definition that CCsh would understand in addition to changing the exit call into a return statement, then the CCshd code can now call example as a subroutine as seen below.
         $ cat example2.c
         int
         example()
         {
             printf("Hello, world\n");

             return (0);
         }
         $ cc -c example2.c
         $ ar cq mylib.a example2.o
         $ CCLIB=mylib.a # CCLIB is used by ccsh to find your builtin library
         $ export CCLIB
         $ # second case: example is now builtin via the -B option.
         $ # It runs as a subroutine!
         $ ccsh -Bexample ex # second case: example is now builtin

                       CCsh(tm) Version 2.1, "The Bourne Shell Compiler"
                (c) Copyright 1987, 1997 Comeau Computing.  All rights reserved.

         CCsh ends with 0 errors and 0 warnings.

         Compiling C code ...

         Creation of ex.exe finished.
         $ cat ex.c
         /*
          *
          *  Generated by CCsh V2.1 (tm), "The Bourne Shell Compiler"
          *  CCsh is manufactured by Comeau Computing: comeacomputing@gmail.com
          *
          *  Derived from '/tmp/ex'
          *  Created: Tue Jan 26 18:03:19 1997
          */

         #define ARGVSIZE     (256)
         #define ENVPSIZE     (256)
         #define WAITSIZE     (9)

         #include <ccsh.h>

         main(argc, argv, envp)
         int     argc;
         char    *argv[];
         char    *envp[];
         {
             CCshinit(ARGVSIZE);
             setupargs(argc, argv);      /* delete if arguments are not used  */
             setupenv(ENVPSIZE);         /* delete if environment is not used */
             setupsigs();

             rc = example(   /* NOTE: no more fork()ing of `example' */
                 ENDOFLIST,      FORK,   (char *)0
             );

             CCendup();
             exit(rc);
         }
         char    *(*Subcom[])() = {
                 (char *(*)())0,
         };
         $
Note that in the first case, there is not a large speed benefit in running the script through CCsh since example still needs to be fork()'ed and then exec()'ed. The second case is different though since the -B options has informed CCsh to make example a built-in command. Now, the executable file ex.exe literally has a copy of example linked into it as a subroutine producing the effect we wanted. In general, this technique works fine so long as the shell script being enhanced in this way does not use any redirection, pipes, command substitution, or command arguments. If this is the case, and one should assume that it always is since programs change through their lifetime, then you need to put some header and trailer code into any commands that are to become built-in. Therefore, a proper setup for our example subroutine should be:
         $ cat example3.c
         #include <ccsh.h>

         int
         example(ccshc, ccsht, ccshs)
         int     ccshc;
         int     ccsht;
         char    *ccshs;
         {
             register char    **argv = Argv;
             FILE    *stream = stdout;

             makeargv(&ccshc);
             dobuiltio(&stream);

             argv = Argv;

             /* Your code goes from here to........... */

             fprintf(stream, "Hello, world\n");

             /* ................................. here */

         outahere:
             fflush(stream);
             endbuiltio(&stream);
             fixstandio();

             return (0); /* The return value from your program. */
                         /* Note you *must* pass through outahere to "exit" */
                         /* BUT, don't call exit() unless you really want to */
         }
         $ cc -c example3.c
         $ rm mylib.a
         $ ar cq mylib.a example3.o
         $ cat ex
         example
         var=`example`
         echo var=$var
         $ ccsh -Bexample ex # third case: previous example using I/O

                       CCsh(tm) Version 2.1, "The Bourne Shell Compiler"
                (c) Copyright 1987, 1997 Comeau Computing.  All rights reserved.

         CCsh ends with 0 errors and 0 warnings.

         Compiling C code ...

         Creation of ex.exe finished.
         $ ex.exe
         Hello, world
         var=Hello, world
         $
Note that this must be setup this way because the subroutine (example in our case) is now part of the generated code instead of an independent command. In such cases, it needs to handle I/O and arguments all by itself since the .exe file is to run as a single process. Also, note the use of the variable stream. All output that had previously been going through stdout must now go through stream. If this presents a problem to a large program you will need run it through an editor or sed to replace all standard I/O routines to use their "file" versions, i.e changing printf( to fprintf(stream, or putchar(char) to putc(char, stream).
Comeau Computing.
Copyright © Comeau Computing. All rights reserved.
Revised: July, 2013.