cc (5)cc configuration files. |
Miscellaneous Information |
|
cc
reads commands from the
configuration file, and executes them to run the appropriate compiler and
linker. The command line that was passed to
cc
is examined by the code in the
configuration file. If you are familiar with MKS Toolkit, you may notice that
the commands resemble AWK or MKS KornShell commands. However, these commands are
sufficiently different that you should study them carefully before using them.
Look at sample configuration files, and experiment with
cc
in its interactive mode as you
program new configuration files. The program tries to load the script
/etc/compiler.ccg.
You can modify this behavior by setting the
CCG environment
variable. If
CCG contains the name of a file,
cc
loads and runs that file; if
CCG contains the name of a directory, the program loads and runs
the script in that directory.
If you rename the
cc
executable, it
attempts to find its default configuration in a
.ccg
file with the
same base name as the renamed executable. For example, if you rename
cc
to
c89
, then
c89
looks for a
default configuration file named
c89.ccg
in /etc.
In any case, if you have set the
CCG environment variable, its
value takes precedence. If
CCG contains a file name,
cc
looks for the default configuration
in that file. If
CCG contains a directory,
cc
looks for the default configuration
in a
.ccg
file in that directory. The base name of the
.ccg
file is the same as that of the executable. That is, if the
executable is
cc
, the default configuration is looked for in
$CCG/cc.ccg
and if the executable has been renamed
c89
,
the default configuration is looked for in
$CCG/c89.ccg
.
To enter interactive mode, set the environment variable
CCG to the
value
-
or
stdin
.
cc
then reads its configuration file
from its standard input. This is useful for debugging new configuration files:
you can interactively enter commands, dump
cc
's symbol table, and follow the
execution of your
cc
program.
A comment starts with a
#
character and extends to the end of
the line. This is the same convention as MKS Make, AWK, and the MKS KornShell.
cc
can manipulate three types of
objects: integers, strings, and arrays of strings or integers. Since arrays are
limited to one dimension, they are also called
vectors. Unlike AWK,
cc
does not support floating point
numbers.
Integers are written in the usual way. Here are some sample integers:
123 -49 20000
Strings are written with quotes around them, and cannot extend across a line.
Empty strings are written as
""
. The following escape sequences are
allowed within strings:
\a alert (bell)
\b backspace
\h hard space
\i hard tab
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\\ escaped \
\" embedded "
\ooo ASCII character with octal value ooo
Here are some sample strings:
"hello" "\"embedded\" quotes"
"\\escaped backslash, with tab\t and newline\n"
A vector is list of integers or strings, separated by commas and
surrounded by braces, as in
{ 1, 2, 3 }
{ "strings", 1, "and numbers" }
{ a++, ++c }
{ }
Because these are dynamic, the contents of the vector need not be constant
expressions. For example,
a++
is valid in a vector list.
Empty vectors are written as
{}
.
You indicate a vector's elements with the bracket
[]
operator.
Both strings and vectors can be indexed -- indexing is 0-origin. Indexable
objects have an associated cursor that indicates the current position of the
index. This means you can treat any indexable object as a stream. (The cursor is
actually associated with the value of a variable rather than with the variable
itself; if you assign a new value to a variable, the cursor is reset to the
origin.) The
rewind
statement lets you rewind the stream to
the start of an object. Several functions are provided for dealing with
indexable objects --
read()
,
offset()
, and
tail()
.
# Setup the string:
stream="Alph" ;
# Read and print the first 3 elements;
# this prints "A l p"
println read(stream), read(stream), read(stream);
# Rewind the stream:
rewind(stream);
# Print the first two elements
# and the index of the next ("A l 2"):
println read(stream), read(stream), offset(a);
# Print the remainder of the string ("ph"):
println tail(stream);
To look ahead on a stream, use indexing combined with the offset. See
String/Vector Operations
for more information on the functions.
A variable is a name with an associated value. The name follows the usual rules
for C identifiers; the first character must be a letter or underscore
(
_
), followed by zero or more letters, digits, or underscores.
Variables are created as they are first seen in
cc
input.
Any variable can have any type of value; the type changes automatically if
a value of a different type is assigned.
Several variables have pre-assigned values at the start of a
cc
program. You can change these values.
The variables are:
ARGV
- This variable is assigned a vector containing the strings that appeared on
the command line that called
cc
.
Note that the name of the program is not provided. ARGV
is
typically examined, and used to build new argument lists for the compiler
or linker that is being used.
DIRSEPSTR
- Contains the characters that can be used to separate path names into the
component directories. See the description of
filepath()
.
ECHO
- Originally assigned the empty (null) string, corresponding to a false
expression. If it is later assigned a true value, the
exec
and ovl_exec
operations (described later) print the
commands as they are about to be executed.
NORUN
- Originally assigned the empty (null) string, corresponding to a false
expression. If it is later assigned a true value, the
exec
and ovl_exec
operations
(described later) only echo their arguments, and exec
returns as though the command succeeded, while ovl_exec
terminates cc
with a return value
of 0. Setting NORUN
in this way corresponds to running Make
with the -n
option. By assigning non-null values to
both ECHO
and NORUN
, you can get
cc
to display the commands that it
attempts to execute to compile a program.
true
- Assigned the integer value 1,
true
is used internally by
cc
to denote a true expression.
You should not change this value; if you change the value of
true
to be an expression that is considered false (see
EXPRESSIONS),
cc
may not function correctly.
A
cc
program consists of a number of
statements, each of which is read and then executed. During the reading of a
statement, a syntax error causes
cc
to
print a diagnostic message, and then
cc
goes on to the next statement. During the execution of a statement, an error
causes
cc
to print a diagnostic message.
If
cc
finds a syntax error as it is
reading a statement, it prints a diagnostic message and goes on to the next
statement. While executing a statement, if
cc
finds an error, it prints a
diagnostic message; if an expression is being calculated, then the null string
is returned as the value of the expression.
This section describes the statements that may be used in input to
cc
. In the following, the characters {}
are used to indicate zero or more occurrences of the enclosed items.
The characters [ ] indicate an optional occurrence of the enclosed items.
The following table summarizes statements allowed in
cc
:
Description |
Syntax |
Advance |
advance name ; |
Assignment |
name = expr ; |
|
name[ expr1] = expr2 ; |
Assign & concatenate |
name |= expr ; |
|
name[ expr1] |= expr2 ; |
Break loop |
break [ expr] ; |
Close file |
close filename ; |
Delete file |
delete filename ; |
Dump debugging info |
dump ; |
Empty statement |
; |
Execute command |
exec expr ; |
Exit |
exit ; |
For |
for ( name in
expr) |
|
do statements done |
For |
for
( expr1; expr2; expr3) |
|
do statements done |
Functions |
function
name([ arg1{[, arg2]]}) |
|
{ statements } |
If |
if ( expr1) statements |
|
{ elif
( expr2) statements } |
|
[ else statements ] |
|
fi |
Open file |
open filename ; |
Overlay file |
ovl_exec cmd_expr1 {, expr2} ; |
Print expression(s) |
print expr1 {, expr2} ; |
Print line |
println expr1 {, expr2} ; |
Print to file |
print
expr1 {, expr2} -> filename; |
|
println
expr1 {, expr2} -> filename; |
Rewind cursor |
rewind indexable_object |
While |
while
( expr) do statements done |
Table 1: Summary of cc
Statements
In this definition,
statements means a list of statements, and
expr,
expr1, and
expr2 mean any legal expression as
described a bit later. The expression value is used as a logical expression.
See
EXPRESSIONS for the description of what
values are considered true and false.
- Empty statement
- An empty statement is written as a single semicolon (
;
).
It has no effect.
- If statement
- An
if
statement has the form:
if ( expression ) statements
{ elif ( expression ) statements }
[ else statements ]
fi
Note that fi
is required to end the
if
statement. Any number of elif
's
can be inserted before the end of the if
statement.
- While statement
- A while statement has the form:
while ( expression ) do statements done
The done
is required to mark the end of the
statements. The meaning is similar to the C while loop.
- For loop
- A
for
statement has one of these forms:
for ( name in expression ) do statements done
for ( expr1; expr2; expr3 ) do statements done
In the first form, the name must be a variable name, and the
expression must evaluate to a vector or string. If the vector is
not empty, the statements are executed repeatedly; on each
iteration, name is assigned each successive value in the vector.
The second form is much more like the C language for
loop. Usually, the first expression initializes some variable, the second
expression tests it, and the third expression increments or decrements
it.
- Advance
- An advance statement has the form:
advance name ;
where name is a variable name used in an enclosing
for
loop. This statement immediately assigns to
name the value it would normally have been given in the next
iteration of the for
loop, and subsequent iterations
skip this value.
- Break loop
- A
break
statement has the form:
break [expr] ;
where expr is the number of levels to break out of. If no
expression is specified, break
exits the current loop
(expr is 1).
- Exit Program
- The
exit
statement has the form:
exit expression ;
This terminates cc
with the
integer-valued expression as its exit value.
- Dump Debugging Information
- The
dump
statement is a debugging aid.
dump ;
outputs the names and values of all variables currently defined to the
standard output.
- Print Expression
- There are several printing statement forms.
print expression { , expression } ;
prints the values of the given expressions on the standard output.
If more then one expression is given, they are printed with a
single blank separating each item. print
does not put
a newline character at the end of the output.
println expression { , expression } ;
is similar to the previous format, but does print a newline at the end
of the list of expressions.
print expression { , expression } -> filename ;
println expression { , expression } -> filename ;
are similar to the other printing statements, but they print to the given
file instead of to the standard output. The filename is given as a
string-valued expression. If the file does not exist, it is created; if it
already exists, output is appended to the end of the current contents.
- Close File
- To close a given file, use
close filename ;
The filename is given as a string-valued expression. If you use
a printing statement to write to a file, it is important to close the file
before cc
runs a program that
requires the contents of the file to be up-to-date.
- Delete File
- To delete a given file, use
delete filename ;
You can use this to get rid of temporary (work) files created by earlier
statements in the cc
program.
The file name is given as a string-valued expression.
- Define Function
- You can define a function using the
function
statement,
which has the form:
function name ( [arg1{, arg2}] )
{
statements
}
- Execute a command
- To execute a command, use
exec expression { , expression } ;
Each expression is converted into a string (if necessary) and then
concatenated into a command string (with a single blank between each
expression in the list). For example,
exec "cp","infile","outfile" ;
executes the command
cp infile outfile
The exec
statement executes the command and then goes
on to the next statement. (See the exec
expression
following for ways to get the return status of the executed command.)
The behavior of exec
can be changed by the values of
the ECHO
and NORUN
variables.
- Overlay Execute
- The statement
ovl_exec expression { , expression } ;
is similar to an exec
statement. In this case, the
memory used by cc
is overlaid
with the command to be run, and cc
is effectively terminated. This frees the memory space that
cc
is occupying for the executed
program. The behavior of ovl_exec
can be changed by
the values of the ECHO
and NORUN
variables.
- Rewind Stream
- The
rewind
statement has the form:
rewind indexable_object ;
where indexable_object is a string or vector. This statement moves
the cursor associated with indexable_object back to the origin.
For more about indexable objects, see
Indexable Objects.
Expressions are created by combining literal strings, integers, vectors, and
variables, with the operators listed a bit later.
An expression has a type: integer, string, or vector. When an integer value is
required and a string or vector is provided, some operators convert the vector
into a string, and then interpret that string as an ASCII sequence representing
a number. For example,
"123"
has the integer value
123
when used in arithmetic operations. By the same token, if a string is required,
an integer value is converted into a string in the same way.
Some expressions are calculated by determining if operands are
true
or
false
. The following are considered false:
- the integer 0
- the null string
""
- an empty vector
{ }
Other objects are considered true. The built-in variable
true
can also be used as a condition.
Operators are grouped in precedence classes that are similar to those
of the C programming language. Each section that follows describes a
group of operators in a single-precedence class. The order of the
sections gives the order of precedence.
Order of Operations |
A++ A-- ++A --A |
pre- and post-increment and decrement |
-A (A) V[a] |
unary minus, grouping, array element |
|
A*B A/B | integer multiplication and division |
A+B A-B | addition and subtraction |
A%B | modulus |
|
: |
|
|
A==B A!=B | equality comparison |
A<B A>B A<=B A>=B |
relational comparison |
|
A&&B A||B !A |
logical AND, logical OR, logical negation |
|
, | |
-> | file redirection |
|
length(A) | length operation |
|
A | B | string/vector concatenation |
|
A=B A|=B | assignment |
|
A and B are any expression. |
V is any vector expression. |
Table 2: cc
Order of Operations
These expressions have the highest precedence.
- expression
gives the negative of an integer expression.
( expression )
gives the value of the expression inside the parentheses, and you use
it to change the order of evaluation of expressions.
indexable_object[expression]
obtains the value of a specific indexable element. Indexable objects (vectors
and strings) are indexed with 0-origin. If a vector subscript is out of range,
an error message is displayed, and the null string is returned. As a special
case, the 0-th element of a 0-length vector does not cause an error; instead,
its value is the null string.
The standard multiplication operators are:
expression1 * expression2
expression1 / expression2
expression1 % expression2
representing integer multiplication, division, and the modulus.
Non-integer operands are converted to integers in the usual way.
The standard addition operators are:
expression1 + expression2
expression1 - expression2
representing addition and subtraction. Non-integer operands are
converted to integers in the usual way.
The result of:
expression1 == expression2
is true if the two expressions have equal values.
expression1 != expression2
is true if the two expressions are not equal.
In both cases, if either operand is an integer, the other is converted
into an integer for the purposes of comparison; otherwise, both
expressions are converted into strings, and then compared.
The relational operators are:
expression1 > expression2
expression1 < expression2
expression1 >= expression2
expression1 <= expression2
The value of these expressions is true if the given relationship is true.
If one operand is an integer, the other is converted to an integer
(if necessary) and the comparison takes place numerically; otherwise, operands
are compared as strings, according to the ASCII collating sequence.
The expression
! expression
is true if
expression is false, and false if
expression is true.
The conditional AND expression has the form:
expression1 && expression2
The value of this expression is false if
expression1 is false, in which
case
expression2 is not evaluated; otherwise, if
expression1 is
not false, the value of the expression is the value of
expression2.
The conditional OR expression has the form:
expression1 || expression2
If
expression1 is not false, the value of the expression is that value,
and
expression2 is not evaluated; otherwise, the value of the expression
is the value of
expression2.
The length operation has the form:
length (expression)
If
expression is a vector, the value is the number of elements in the
vector. If
expression is a string, the value is the number of characters
in the string; otherwise, the value is 0.
The operator
|
concatenates strings or vectors. The form is
expression1 | expression2
where
expression1 must be a string or vector. If
expression1 is
a string, the second argument is converted to a string and the two strings are
concatenated for the result. For example, in
X = "hello";
X = X | " good" | "bye";
the variable
X
is assigned the value
"hello goodbye"
You can use the shorthand notation
|=
to append to the end of
an existing string, as in
x = "hello";
x |= " good" | "bye";
which has the same result.
If the first argument of the
|
operator is a vector, the second
argument is added as a new component to the vector. In this way, you can assign
values to a vector incrementally. You can use the
|=
operator as a
shorthand assignment. For example:
x = {} # ensure x is a vector
x |= 1; # x is now { 1 }
x |= "so"; # x is now { 1, "so" }
x |= 2 | 3; # x is now { 1, "so", "23" }
# Remember, the '|' is applied to '2' and '3',
# forming a string, that is then added
x |= { 4, 5 }; # x is now { 1, "so", "23", 4, 5 }
There are several assignment expressions.
name = expression ;
assigns the value of the
expression to the given named variable.
name[expression1] = expression2 ;
assigns the value of
expression2 to the element of the vector
variable specified by
expression1. It is an error to use an
index greater then the length of the vector.
name |= expression ;
name[expression1] |= expression2 ;
are concatenation assignments. The value of the right hand side is
concatenated to the existing value of the left hand side. See
String/Vector Operators for more
about concatenation.
Because assignments are expressions, rather than statements, they can be used
anywhere. For example, this is valid:
if (a = 2)
println "true" ;
fi
There are a number of other string/vector operators, summarized in this table:
Function | Description |
access( filename,[ num| str]) |
return true if filename's mode is str or num |
cd( pathname) |
change to named directory |
cinclude( filename) |
include filename; return 0 if it does not exist |
exec( expression {, expression}) |
execute command line |
filepath( expression) |
return vector containing path name components |
getcwd() |
return current working directory path |
getenv( name) |
return value of environment variable name |
getopt([ str[, optind]]) |
process options string |
include( filename) |
include filename; abort if filename does not exist |
index( str, expr) |
return index of first expr in str |
isatty( fileno) |
return true if fileno is a tty |
offset( indexable_object) |
return cursor offset in indexable_object |
ovl_exec( ) |
execute command line in overlaid memory |
putenv( ) |
alter value of environment variable |
read( indexable_object) |
return next token from indexable_object |
replace( str, old, new) |
return str with old replaced by new |
rindex( str, expr) |
return index of last expr in str |
strerror() |
return string corresponding to errno |
substr( expr, pos) |
return substring of expr starting at pos |
substr( expr, pos, len) |
return length len substring of expr starting at pos |
tail( indexable_object) |
return unread portion of indexable_object |
tempfile([ dir_str,] pre_str) |
temporary file name |
tolower( expr) |
return expr in lowercase |
toupper( expr) |
return expr in uppercase |
Table 3: Summary of cc
Functions
access(
filename, [
mode])
- returns
true
if the access mode of filename is the
same as the supplied mode. The mode should be passed as a
string composed of the characters r
, w
,
x
, or f
(for read, write, execute, or file
existence). If no mode is supplied, f
is assumed.
access("temp.af9","f") -- checks if temp.af9
exists
access("temp.af9","rw") -- checks for read/write
permission on temp.af9
If the base operating system allows, mode can be passed as a number.
This is non-portable, so you should use a string representation.
cd(
pathname)
- changes the current directory to the value of pathname.
cinclude(
filename)
- includes the specified file. No path search is performed. If
filename is not an absolute path name, the current directory is
searched. If the file does not exist, this function returns 0; otherwise,
it returns 1.
exec(
expression {,
expression})
- is similar to the
exec
statement. The value of an
exec
expression is the exit status of the last command
executed. This is an integer.
filepath(
expression)
- returns a vector whose components are the strings corresponding to the
path name components of the string expression. The built-in variable
DIRSEPSTR
is used to determine the components of the path.
getcwd()
- returns the current working directory path.
getenv(
name)
- returns the string value of the environment variable indicated by the
string expression name. If the environment variable is not set,
the null string "" is returned.
getopt([
optstr[,
optind]])
- processes an option string; this simplifies the business of writing
scripts that take options. optstr is a format string listing each
option character; if the option character is followed by a colon, the
option takes an argument. For example, the format string
"aei:o:u"
specifies that the options
a, e, i, o,
and u
are valid, and
the colons show that both i
and o
require arguments. The option being processed is stored in a variable; if
there is an argument, it is stored in the global variable
OPTARG
.
optstr only needs to be specified on the first call. Subsequent
calls to getopt()
continue processing the same
ARGV
argument string. Here is a bit of code that sets flag
variables for each option called:
while (opt = getopt("dl:rtx")) do
if (opt == "d" ) d_flag++;
elif (opt == "l" )
l_flag++;
l_arg = OPTARG;
elif (opt == "r" ) r_flag++;
elif (opt == "t" ) t_flag++;
elif (opt == "x" ) x_flag++;
fi
done
The getopt
call does not remove items from the
ARGV
string, it just moves the OPTIND
pointer
through the ARGV
vector. If you want to pass the tail of the
argument string without the options, you can reset the value of
ARGV
with code like this:
members = {};
for (i=OPTIND; ARGV{i}; i++) do
members |= { ARGV[i] );
done
ARGV = members;
include(
filename)
- includes the specified file. No path search is performed. If
filename is not an absolute path name, the current directory is
searched. If the file does not exist, the script aborts.
- Note:
- Expressions are compiled completely and then executed. As a result,
if you use
include
in a function, the file is not included
until an expression referencing the function is executed.
index(
expression,
string-expression)
- returns the index of the first occurrence of string-expression in
the expression If the first expression is a vector, it is first
converted into a string. The index is 1-origin; a value of 0 is returned
if the second expression is not a substring of the first.
isatty(
filenum\)
- returns
true
if filenum is a tty; filenum is an
integer representing a file number. The values 0, 1,
and
2
represent standard input, standard output, and standard
error, respectively.
offset(
indexable_object)
- returns the current stream offset in an indexable object.
ovl_exec(
expression {,
expression})
- constructs a command line in the same way, and then overlays
cc
with that command.
cc
exits with the exit code
returned by that command.
putenv(
string)
- where string is a string expression of the form
name=value assigns value to the environment variable
indicated by the string expression name and adds that variable to
the current environment.
read(
indexable_object)
- reads the next item from indexable_object and returns it.
replace(
string,
old,
new)
- returns string with all occurrences of the first character of the
string old replaced with the first character of the string
new.
rindex(
expression,
string-expression)
- is similar to
index
but returns the index of the last
occurrence.
strerror()
- Returns the string that corresponds to the current value of
errno
. If errno
is not set, returns a null
string.
substr(
expression,
position)
- returns the substring of the first string, starting at the position given
by the second argument (an integer expression). Strings are 1-origin;
however, for convenience, if the value of position is 0,
substr
returns the substring beginning at 1. If
position is negative, it is taken to be an offset from the end of
the string. For example:
substr("hello",0) = substr("hello",1) -> "hello"
substr("hello",length("hello")) -> "o"
substr("hello",-3) -> "llo"
substr("hello",-1) -> "o"
substr
may also take three arguments, as in:
substr(
expression,
position,
length)
- The length argument is an integer expression, giving the desired
number of characters in the substring. If this value is negative, the
substring is obtained by advancing to the indicated position, marking that
the end, and then retreating by the specified amount. For example:
substr("hello",0,1) = substr("hello",1,1) -> "h"
substr("hello",-1,-3) -> "llo"
tail(
indexable_object)
- returns the unread portion of an indexable_object.
tempfile([
dir_str],
pre)
- returns a string guaranteed to be an unused temporary file name in the
directory specified by dir_str. If no dir_str is provided,
then the directory specified by TMPDIR is used; if
TMPDIR is not set, the system default is used (usually
/tmp
. If pre is not an empty string, it is taken as a
prefix for the name of the temporary file.
tolower(
expression)
- returns the string expression with all uppercase characters
translated to lowercase.
toupper(
expression)
- does the opposite translation.
- Commands:
cc