Provided by: icmake_10.03.03-2_amd64 bug

NAME

       icmscript - The C-like icmake scripting language

DESCRIPTION

       Icmake(1)  is  a  generic  tool  handling  program  maintenance  that  can  be  used as an
       alternative for make(1). It’s a generic tool in that icmake-scripts, written in a language
       closely  resembling  the  C programming language, can perform tasks that are traditionally
       the domain of scripting languages.

       Icmake  allows  programmers  to  use  a  programming  language  (closely  resembling   the
       C-programming  language)  to  define  the  actions that are required for (complex) program
       maintenance. For this, icmake offers various special operators as well as a set of support
       functions that have shown their usefulness in program maintenance.

       This man-page covers the icmake scripting language in de following sections:

       o      DATA TYPES
              - int, list, string, and void (for functions);

       o      OUTLINE
              -  outline  of  icmake  scripts:  what  are  their  requirements, the structure and
              organization of their main-functions.

       o      PREPROCESSOR DIRECTIVES
              - supported preprocessor directives, like #include and #define;

       o      PREDEFINED CONSTANTS
              - like O_FILE, OFF, and S_IFREG;

       o      OPERATORS
              - like +, younger, and casts

       o      FLOW CONTROL
              - if, for, while, etc. (the switch is not available);

       o      PREDEFINED FUNCTIONS
              - executing programs, changing directories, operations  on  string  and  list  type
              variables,  etc..  Functions  are  marked  as INT FUNCTIONS, LIST FUNCTIONS, STRING
              FUNCTIONS

       o      USER DEFINED FUNCTIONS
              - at least main, with or without its common parameters argc, argv, and envp.

DATA TYPES

       Icmake supports the following five data and value types:

       o      ASCII character constants
              ASCII character constants are ascii-characters,  surrounded  by  single  or  double
              quotes.  Single  characters  (e.g.,  ’a’)  represent the character itself. Standard
              escape sequences (e.g., ’\n’) are supported and are converted to  their  well-known
              values  (e.g.,  ’\n’  represents  ascii  value  10  (decimal)). Non-standard escape
              sequences (e.g., ’\x’) are converted to the ascii character  following  the  escape
              character  (so ’\x’ equals ’x’). Escaped sequences consisting of three octal digits
              represent the ascii character corresponding to the octal value, modulo  256  (e.g.,
              ’\113’  represents  ’K’).  Escape  sequences  consisting  of  an  x followed by two
              hexadecimal digits represent the ascii character corresponding to  the  hexadecimal
              value (e.g., ’\x4b’, also representing ’K’);

       o      int
              Integral  values,  ranging  from  -0x8000  through  0x7fff.  int  constants  may be
              specified as decimal numbers (starting with digits  1  through  9),  octal  numbers
              (starting  with  0,  followed  by  one  or  more octal digits), hexadecimal numbers
              (starting with 0x, followed by  one  or  more  hexadecimal  digits),  or  as  ASCII
              character constants;

       o      string
              Text  values: text (or `string’) constants are delimited by double quotes. Multiple
              string constants may be concatenated, but a single string  constant  may  not  span
              multiple  lines.  Multiple  string  constants, only separated by white space (i.e.,
              blanks, newlines, comment) are concatenated and are considered  one  single  string
              constant.  To  indicate  an  end-of-line  in  a  string  constant use the \n escape
              sequence;

              If arithmetic expressions use at least one int operand then those  expressions  may
              also  contain  single character ASCII constants using double quotes. In those cases
              they represent the ascii-values of their characters.

              Conversely, ASCII character constants using single quotes may be used in situations
              where  string operands are expected;

       o      list
              A  list  is  a data structure containing a series of individually accessible string
              values. When a list contains elements, its first element has index 0;

              Lists may be written to the standard output stream or  to  file  (using  printf  or
              fprintf).  Lists  can  also  be  inserted into string variables using strformat. In
              these cases all (space delimited) elements of the lists  are  inserted  into  their
              destinations;

              Lists can also be defined as constants. They consist of an optional series of comma
              separated string constants surrounded by a pair of square brackets. E.g.,

                  list words = ["a", "list", "constant"];

       o      void
              The type void is used when defining functions to indicate that  such  functions  do
              not  return  values. Alternatively, functions may return int, string or list values
              (cf. section USER DEFINED FUNCTIONS).

       Variables can be defined at the global level inside functions (not  only  at  the  top  of
       compound  statements but also between statements and in the initialization section of for-
       and if-statements). When defined inside functions, the standard C scoping  and  visibility
       rules apply. Variables are strongly typed, and cannot have type void.

       Variables  may be initialized when they are defined. Initializations are expressions which
       may use predefined or user-defined functions, constant values, and  values  of  variables.
       Functions  or  variables  that  are  used  for  initialization  must  be  visible  at  the
       initialization point.

OUTLINE

       Icmake scripts require a user-defined function main. The function main has three  optional
       parameters, which may be omitted from the last one (envp) to the first one (argc), like in
       C. Its full prototype is:

           void main(int argc, list argv, list envp)

       or

           int main(int argc, list argv, list envp)

       When a void main function ends (using a return; statement or when  its  execution  reaches
       its  body’s  closing curly) the value 0 is returned to the operating system. When int main
       functions end using  return  statements  then  those  statements  must  be  provided  with
       int-expressions.  It’s  OK  when  the execution of an int main function reaches its body’s
       closing curly, om which case 0 is automatically returned to the operating system

       In main the parameter

       o      argc represents the number of elements in argv;

       o      argv contains the arguments, with element 0 being equal to the  name  of  the  .bim
              file,  that  were  passed  to  the  .bim file. The OPTIONS section of the icmake(1)
              manpage covers how these arguments are forwarded to the icmake script using options
              -e, -s, and -t.

       o      envp contains the `environment’ variables. The (predefined) function listlen can be
              used to determine the number of its elements.  Elements  in  envp  use  the  format
              variable=value.  Alternatively,  the  (predefined)  function  getenv can be used to
              retrieve a specific environment variable immediately.

       Example (the implementations of the user-defined functions usage,  modified,  and  compile
       are left as an exercise for the reader):

           void main(int argc, list argv)
           {
               if (argc == 1)
                   usage(argv[0])
       ;

       if  (list  toCompile  =  modified("*.cc"))  {  for (int idx = listlen(toCompile); idx--; )
       compile(toCompile[idx]); } } ) When executing an icmake script icmake’s  run-time  support
       system  first  initializes  all  all  global  variables in the order of their definitions.
       Followin this the function main is called. The script ens once once main returns  or  when
       the (predefined) function exit is called by the script).

PREPROCESSOR DIRECTIVES

       Before  actually  compiling  icmake  scripts  they  are  first pre-processed by the icmake
       pre-processor.  The  pre-processor  removes   comment,   includes   files   specified   by
       include-directives, and processes #define and comparable directives.

       The following preprocessor directives are recognized:

       o      comment:
              standard    C    comment   (everything   from   /*   through   */)   as   well   as
              comment-to-end-of-line (starting at //, continuing to  the  end  of  the  line)  is
              ignored;

       o      Shell  startup:  The  first  line of the icmake-script may start with #!path, where
              path defines the absolute location of the icmake  program.  By  making  the  script
              executable, it can be called without explicitly calling icmake.

              E.g.,  if  the  first  line of an (executable) icmakefile ’icm’ (without extension)
              contains

                  #!/usr/bin/icmake -t.

              then icm can be issued as a command, interpreting  the  remaining  content  of  the
              script  as an icmake source which is compiled and then executed by icmake. In these
              cases the binary files are removed when the scipts end;

       o      #include "filename"
              The file filename is included at the location of the directive;

       o      #include <filename>
              The file filename is included at the location of the #include  directive;  filename
              is  searched  in  the  colon-separated  directories specified by the IM environment
              variable. The first occurrence of filename in the directories specified by  the  IM
              environment variable is used;

       o      #define identifier [definition]
              The  text  identifier  is  replaced  by  definition.  The  definition  may  contain
              references to already defined identifiers, using the format ${identifier}.  If  the
              ${identifier} hasn’t been defined (yet), the literal text ${identifier} is used. To
              prevent infinite recursion at most 100 ${identifier} replacements are accepted;

              If the last character on a line is a backslash (\) then definitions continue at the
              next  line.   (the  backslash  is not included in the definition). The preprocessor
              concatenates double-quoted strings. Double quoted strings  may  not  span  multiple
              lines.  Multiple  blanks  (outside  of  double  quoted  strings) in definitions are
              contracted to a single blank space;

              Following the #define’s identifier  a  definition  may  optional  be  provided.  If
              omitted,  the  macro  is  defined,  so  it can be used in #if(n)def directives (see
              below), but in those cases these intentifiers are simply removed from  icmake  code
              statements.

       o      #ifdef identifier
              If  the identifier macro was defined the next block of code (until a matching #else
              or #endif directive was read) is byte-compiled. Otherwise, the  block  of  code  is
              ignored;

       o      #ifndef identifier
              If  the  identifier  macro was not defined the next block of code (until a matching
              #else or #endif directive was detected) is byte-compiled. Otherwise, the  block  of
              code is ignored;

       o      #else
              Terminates   #ifdef and #ifndef directives, reversing the acceptance decision about
              the following code. Only one #else  directive  can  be  associated  with  #if(n)def
              directives;

       o      #endif
              Terminates the preprocessor block starting at the matching #ifdef, #ifndef or #else
              directive. The #endif directory  and  its  matching  #if(n)def  directive  must  be
              specified in the same file;

       o      #undef identifier
              Remove  identifier  from  the  set  of  defined  symbols.  This does not affect the
              specification of any previously defined symbols in  which  identifier’s  definition
              has been used. If identifier hasn’t been defined a warning is issued.

PREDEFINED CONSTANTS

       The following predefined int constants are available (the functions listed in the intended
       for column are described in the upcoming sections covering the predefined functions):

       ─────────────────────────────────
       symbol      value   intended for
       ─────────────────────────────────
       O_ALL       8       makelist
       O_DIR       2       makelist
       O_FILE      1       makelist
       O_SUBDIR    4       makelist
       ─────────────────────────────────
       OFF         0       echo
       ON          1       echo
       ─────────────────────────────────
       P_CHECK     0       system calls
       P_NOCHECK   1       system calls
       ─────────────────────────────────
       S_IEXEC     32      stat
       S_IFCHR     1       stat
       S_IFDIR     2       stat
       S_IFREG     4       stat

       S_IREAD     8       stat
       S_IWRITE    16      stat
       ─────────────────────────────────

       The following constants are architecture dependent:

       ──────────────────────────────────────────────────────────────
       symbol           1 when defined on the platform, otherwise 0
       ──────────────────────────────────────────────────────────────
       unix             Unix, usually with GNU’s gcc compiler
       UNIX             may alternatively be available
       linux            x86 running Linux (usually with gcc)
       LINUX            may alternatively be available
       M_SYSV, M_UNIX   x86 running SCO/Unix
       _POSIX           _SOURCE   Unix with Posix compliant compiler
       __hpux           HP-UX, with the native HP compiler
       ──────────────────────────────────────────────────────────────

OPERATORS

       Since icmake version 10.00.00 the << operator can be used like the C++ insertion operator.
       See the description of the functions printf and fprintf below.

       int-operators:

       All  C  operators  (including  the  ternary  operator)  are  available (except for pointer
       operators, as icmake does not support pointers). They  operate  like  their  C-programming
       language’s  counterparts.  Comparison  operators  return  1  if  the  comparison  is true,
       otherwise 0 is returned.

       string-operators:

       For string variables and/or constants the following operators are available (lhs  and  rhs
       are string variables or constants):

       o      lhs  +  rhs: returns a new string value containing the concatenation of strings lhs
              and rhs. Note that string constants can also directly be  concatetated  (not  using
              the  +  operator),  e.g.,  the  following  two  lines both define the string "hello
              world":

                  "hello "   "world"
                  "hello " + "world"

       o      lhs += rhs: lhs must be a string variable, to which the string  variable  or  value
              rhs is appended;

       o      string  comparisons: operators == != <= >= < > != and == return 1 if the comparison
              is true, otherwise 0. The  ordering  operators  (like  <  and  >=)  use  the  (case
              sensitive) character ordering defined by the ASCII character set;

       o      !lhs:  the boolean ! (not) operator returns 1 if the string lhs is empty, otherwise
              0 is returned. Strings containing white-space characters are not empty;

       o      lhs younger rhs, lhs newer rhs: returns 1 if file lhs is more recent than file rhs.
              E.g., "source.cc" newer "source.o". The files lhs and rhs do not have to exist:

              o      if both don’t exist 0 is returned,

              o      if lhs doesn’t exist 0 is returned,

              o      if rhs doesn’t exist, 1 is returned,

              o      if they are equally old 0 is returned.

              The  predefined  function exists() (see below, section PREDEFINED FUNCTIONS) can be
              used to test whether a file exists;

       o      lhs older rhs: returns 1 if file lhs is older  than  file  rhs.  E.g.,  "libprog.a"
              older "source.o". The files lhs and rhs do not have to exist:

              o      if both don’t exist 0 is returned,

              o      if lhs doesn’t exist 1 is returned,

              o      if rhs doesn’t exist, 0 is returned,

              o      if they are equally old 0 is returned.

       o      []:  the  index  operator returns a character from a string variable or constant. A
              string is returned as an rvalue. Thus, the following statement compiles OK:

                  lhs = rhs[3];

              but the following statement won’t compile:

                  lhs[3] = "a";

              If an invalid (out of bounds) index value is specified an empty string is returned.

       o      The backtick operator (`string cmd`)
              A string placed between two backticks is executed as a separate command.  Different
              from  the  exec and system calls the backtick operator collects the standard output
              produced by `cmd’ returning this output as a list.

              The elements of the list contain the subsequent lines of output (including a  final
              newline,  if  present) produced by `cmd’. A command that could be executed but that
              did not produce any output returns a list containing one string element,  which  is
              empty.

              An empty list indicates that the command could not be executed.

              The  command’s  standard  error  stream output is ignored by the backtick operator.
              However, standard shell redirection may be  used  to  collect  the  standard  error
              stream’s output.

              Example:

                  printf << `"ls"`;   // prints the elements in
                                      // the current directory

              Also  note  that  the backtick operator requires a string argument: either a string
              constant or a string variable.

              The  predefined  function  eval(string  cmd)  behaves  exactly  like  the  backtick
              operator: they are synonyms.

       list-operators:

       For list variables and/or values the following operators are available:

       o      lhs  +  rhs: returns a new list value containing the concatenation of the values of
              lists lhs and rhs. This is not a set operation: if an element appears both  in  lhs
              and  in  rhs, then both will appear in the resulting list (set-addition is provided
              by the built-in function listunion);

       o      lhs - rhs: returns a new list value containing the elements in  lhs  that  are  not
              present  in  rhs. This is a set-difference operation. The ordering of the remaining
              elements in the returned list is  equal to the ordering of those elements in lhs;

       o      lhs += rhs: elements in rhs are added to the elements in lhs, which must be a  list
              variable.  This is not a set operation;

       o      lhs  -=  rhs:  elements in rhs are removed from the elements in lhs.  This is a set
              operation: all elements of lhs that are found in rhs  are  removed  from  lhs.  The
              ordering of the remaining elements in lhs is not altered;

       o      list  equality  comparisons:  operators  != and == may be applied to list values or
              variables. Operator == returns 1 if both lists  have  element-by-element  identical
              elements, otherwise 0 is returned. Operator != reverses the result of ==;

       o      !lhs:  the  boolean  !  operator returns 1 if the list lhs is empty, otherwise 0 is
              returned;

       o      []: the index operator retrieves an element from a  list  variable:  it  returns  a
              string as an rvalue. Thus, the following statement compiles OK:

                  // assume lst is a list, str is a string
                  str = lst[3];

              but the following statement won’t compile:

                  lst[3] = str;

              If an invalid (out of bounds) index value is specified an empty string is returned.

       Casting:

       Type-casts using the standard C-style cast-operator can be used to cast:

       o      strings to ints and vice versa ((int)"123", (string)55)
              If  the  content  of  a  string does not represent a (decimal) int value 0 the cast
              returns  0;

       o      Strings to lists (list lst = (list)"hello"): this returns a list having one element
              (hello)  (note  that  casting a string to a list as shown is overkill as list lst =
              ["hello"] performs the same initialization).

FLOW CONTROL

       Icmake offers a subset of C’s flow control statements. They  can  be  used  as  in  the  C
       programming language.

       o      expression ;
              The plain expression statement.

              Insert-expression  statements  are  defined  for  the functions fprintf and printf.
              Expression statements may start with printf <<  or  fprintf  <<  filename  <<.  The
              values  of  all  subsequent  expressions,  separated by << operators (which in this
              context are called insertion operators) are written to the standard  output  stream
              (when  using  printf  <<),  or  to  the  file  whose name is provided in the string
              filename (when using fprintf << filename <<).  Examples:

                  printf << "hello" << ’ ’ << "world" << ’\n’;
                  fprintf << "out.txt" << "hello" << ’ ’ << "world" << ’\n’;

       o      The compound statement
              Variables may be defined and initialized inside compound  statements  at  locations
              where expression statements can also be used. The visibility of variables starts at
              their points of definition;

       o      if ([definition;] condition) statement
              The [definition;] phrase is optional. If used it  defines  a  type  followed  by  a
              comma-separated  list  of  variables  which  may  be  provided  with initialization
              expressions.

              The condition phrase is required, and may define and initialize a variable. E.g,

                  if (string str = getText())
                      process(str);

              In this example, process is not called if getText() returns an empty string.

              Variables defined in the definition and  condition  phrases  do  not  exist  either
              before or after the if statement.

       o      if ([definition;] condition) statement1 else statement2
              Acts  like the previous statement. If the condition is true statement1 is executed;
              if the condition is false statement2 is executed;

       o      for (init; condition; increment) statement
              Variables (of a single type) may be initialized (and  optionally  defined)  in  the
              init  section. The condition phrase may define and initialize a variable. The init,
              condition and increment sections may remain empty. An empty  condition  section  is
              interpreted as `always true’;

       o      while (condition) statement
              Inside the condition a variable may be defined and initialized.

              A  complementary  do  ...  while()  statement  is  not  available. Note that when a
              variable is defined and initialized in the  condition  section  the  initialization
              expression is executed at each iteration of the while statement. Thus the following
              statement never ends, and displays a never ending stream of values 10:

                  while (int x = 10)
                      printf(x--, "\n");

       o      return;, and return expression;
              Plain return statements can be  used  in  void  functions,  and  return  expression
              statements are used in other type of functions.

       o      break
              break;  statements  can  only  be  used  in  for and while statements, ending those
              statements;

       o      continue
              continue; statements can only be used in for and while statements, continuing their
              next iteration.

PREDEFINED FUNCTIONS

       Icmake  provides  the following predefined functions, which can be used anywhere in icmake
       scripts. In the following overview the functions are ordered  by  categories,  and  within
       categories they are ordered alphabetically by function name.

       Five categories are distinguished:

       o      Functions operating on ints (see INT FUNCTIONS below):
              these functions receive int arguments, processing those arguments;

       o      Functions operating on strings (see STRING FUNCTIONS below):
              these  functions  operate  on  the  strings  which are passed to these functions as
              arguments;

       o      Functions operating on lists (see LIST FUNCTIONS below):
              these functions operate on the  lists  which  are  passed  to  these  functions  as
              arguments;

       o      Functions manipulating file system entries (see FILESYSTEM FUNCTIONS below):
              these functions receive the names of file-system entries (files, directories, etc.)
              as their string arguments.

              Note that these functions are not listed in the STRING FUNCTIONS section,  as  they
              do  not  directly operate on their string arguments, but merely use those arguments
              to identify file system entries.

              On the other hand, functions like change_base do not operate on file-system entries
              and are therefore entries in the STRING FUNCTIONS section;

       o      System-related functions (see SYSTEM FUNCTIONS below):
              these  functions  interface  to  facilities  provided by the operating system, like
              executing programs or changing the script’s environment variables.  Some  of  these
              functions  use  specialized  support  functions,  which  are  also included in this
              section.

       INT FUNCTIONS:

       o      string ascii(int value)
              returns value as a string: ascii(65) returns the string "A";

       o      echo(int opt)
              controls echoing of called programs (and their arguments), specify OFF  if  echoing
              is not requested. By default echo(ON) is active.

       STRING FUNCTIONS:

       o      int ascii(string str)
              returns the first character of str as an in: ascii("A") returns 65;

       o      string change_base(string file, string base)
              returns  file  whose  base  name is changed into base: change_base("/path/demo.im",
              "out") returns "/path/out.im";

       o      string change_ext(string file, string ext)
              returns file whose extension is  changed  into  ext:  change_ext("source.cc",  "o")
              returns  "source.o".  The  extension  of  the returned string is separated from the
              file’s base name by  a  single  dot  (e.g.,  change_ext("source.",  ".cc")  returns
              "source.cc");

       o      string change_path(string file, string path)
              return  file whose path is changed into path: change_path("tmp/binary", "/usr/bin")
              returns "/usr/bin/binary". To remove the path specify path as an empty string;

       o      string element(int index, string var)
              acts identically to the index operator: refer to the index ([]) operator in section
              OPERATORS;

       o      string get_base(string file)
              returns  the  base  name of file. The base name is the file without its path prefix
              and without its extension. The extension is all information starting at  the  final
              dot  in  the  filename.  If  no final dot is found, the file name is the base name.
              E.g., the base name of a.b equals a, the base name of a.b.c equals  a.b,  the  base
              name of a/b/c equals c;

       o      string get_dext(string file)
              returns  the extension of file, including the separating dot (hence the d in dext).
              The extension is all information starting at the filename’s final dot. If file does
              not have a final dot then an empty string is returned;

       o      string get_ext(string file)
              returns  the  extension  of file, without the separating dot. The extension are all
              characters in file starting at file’s final dot. If no final dot is found, an empty
              string is returned;

       o      string get_path(string file)
              returns  file’s  path-prefix.  The  path  prefix  is  all  information  up  to (and
              including) the final directory separator (which  is,  depending  on  the  operating
              system,  a forward slash or a backslash).  If file does not contain a path-element,
              then an empty string is returned;

       o      string resize(string str, int newlength) returns a copy of string str,  resized  to
              newlength  characters.   If newlength is negative then an empty string is returned,
              if newlength exceeds str’s length then the newly added characters  are  initialized
              to blank spaces;

       o      int strchr(string str, string chars)
              returns the first index in str where any of the characters in chars is found, or -1
              if str does not contain any of the characters in chars;

       o      int strfind(string haystack, string needle)
              returns index in haystack where needle is found, or -1 if needle is  not  found  in
              haystack;

       o      string strformat(string format, argument(s))
              returns  a  string constructed from the format string containing placeholders %1 ..
              %2 to refer to arguments following the format string. The specification  %1  refers
              to  the  first  argument following the format string. If fewer arguments than n are
              provided then additional 0 arguments are provided by icmake. Example:

                  void main()
                  {
                      string s2 = = strformat("%1 %2 %1\n", 10, 20);
                      printf("s2 = ", s2);        // shows: s2 = 10 20 10
                  }

       o      int strlen(string str)
              returns  the  number  of  characters  in  str   (not   counting   the   terminating
              NUL-character);

       o      string strlwr(string str)
              returns a lower-case duplicate of str;

       o      list strtok(string str, string separators)
              returns  a  list  containing  all  substrings  of  str  separated  by  one  or more
              (consecutive)  characters  in  separators:  strtok("hello  icmake’s+world",  "  +")
              returns a list containing the three strings "hello", "icmake’s", and "world";

       o      string strupr(string str)
              returns an upper-case duplicate of str.

       o      string substr(string text, int offset, int count)
              returns a substring of text, starting at offset, consisting of count characters. If
              offset exceeds (or equals) the string’s length or if count  <=  0,  then  an  empty
              string  is  returned. If offset is less than 0 then offset = 0 is used. If offset +
              count exceeds text’s length then the available substring starting  at  text[offset]
              is returned (which may be empty);

       o      string trim(string str)
              returns a copy of str without leading and trailing white spaces;

       o      string trimleft(string str)
              returns a copy of str without leading white spaces;

       o      string trimright(string str)
              Returns a copy of str without trailing white spaces.

       LIST FUNCTIONS:

       o      string element(int index, list var)
              acts identically to the index operator: refer to the index ([]) operator in section
              OPERATORS;

       o      int listfind(list lst, string str)
              returns the smallest index in lst where the string str is found, or -1 if lst  does
              not contain str;

       o      int listlen(list l)
              returns the number of elements in list;

       o      list listunion(list lhs, list rhs)
              returns a list containing the union of the elements in lhs and the elements of rhs.
              The original order of the elements in lhs is kept. Subsequent elements in rhs  that
              are not available in lhs are added to the end of lhs;

       o      list listunion(list lst, string str)
              returns  a  list  containing the union of the elements in lst and str. The original
              order of the elements in lhs is kept. If rhs is not available in  lhs  then  it  is
              added to the end of lhs.

       FILESYSTEM FUNCTIONS:

       o      string chdir([int check,] string dir)
              changes  the  script’s working directory to dir (which may be specified as absolute
              or relative to the script’s current  working  directory).  The  first  argument  is
              optional:   if   omitted   and  changing  the  working  directory  fails  then  the
              icmake-script ends with exit value 1; by specifying P_NOCHECK  the  function  won’t
              terminate the script but merely returns the script’s current working directory. The
              script’s working directory after completing the change-dir request is  returned  as
              an absolute path, ending in a `/’ directory separator.

              Use  chdir(".")  to  merely  obtain the current working directory; use chdir("") to
              change-dir to the script’s startup working directory;

       o      int exists(string file)
              if file exists, 1 is returned, otherwise 0 is returned;

       o      list fgets(string file, list offset)
              the next line found at offset value offset[3] is read from file. Pass an empty list
              to fgets to read file from its beginning.

              The returned list has four elements:

              o      its  first  element ([0]) contains the read line (without the line’s \n line
                     terminator);

              o      its second element ([1]) contains the line’s \n line terminator (or an empty
                     string if the line was not terminated by a \n);

              o      its  third element ([2]) contains the string OK if the line was successfully
                     read and FAIL if reading from file failed;

              o      its fourth element ([3]) contains the offset beyond the last read byte.

              To read multiple lines, pass the returned list as argument to fgets:

                  list ret;
                  while (ret = fgets("filename", ret))
                      process(ret);

              Be careful not to define list ret in while’s condition, as this will reset  ret  to
              an empty list at each iteration;

       o      int fprintf(string filename, argument(s))
              appends  all  (comma or left-shift (insertion) operator separated) arguments to the
              file filename. Returns the number of printed arguments.

              If the first argument (following filename) contains placeholders (%1, %2,  ...  %n)
              then  that  argument is considered a format string (see also the function strformat
              in the string functions section for additional information about  format  strings).
              Some examples:

                  fprintf("out", "hello", "world", ’\n’);
                  fprintf << "out" << "hello" << "world" << ’\n’;

                  fprintf("out", "%1 %2\n", "hello", "world");           // 1
                  fprintf << "out" << "hello" << ’ ’ << "world" << ’\n’; // 2
                  fprintf << "out" << "%1 %2\n" << "hello" << "world";   // 3

              When  writing  statement  1 using insertion operators (cf. the expression statement
              description in section FLOW CONTROL)  statement 2 would  normally  be  encountered,
              although statement 3, using the format string, would still be accepted;

       o      string getch()
              returns  the  next  pressed  key  as  a  string  (pressing  the  `Enter’-key is not
              required). The pressed key is not echoed. If the key should be  echoed  use,  e.g.,
              printf(getch());

       o      string gets()
              returns  the  next  line  read from the keyboard as a string. The line contains all
              entered characters until the  `Enter’-key  was  pressed.  The  `Enter’-key’s  value
              itself is not stored in the returned string;

       o      list makelist([int type = O_FILE], string mask)
              the  argument  type  is optional, in which case O_FILE is used.  Makelist returns a
              list of all type file-system entries matching mask. E.g., makelist("*.c") returns a
              list containing all files ending in .c. For type one of the following set of values
              can be used to obtain a more specific selection of directory entries:

              symbol     meaning
              O_ALL      obtain all directory entries
              O_DIR      obtain all directories, including . and ..
              O_FILE     obtain a list of regular files
              O_SUBDIR   obtain all directories except for . and ..

              In Unix-type operating systems the pattern * does not match entries starting with a
              dot (hidden entries). To obtain a list of such entries use the pattern .*;

       o      list  makelist([int  type  =  O_FILE,]  string  mask, {newer,older,younger}, string
              comparefile)
              the (optional) parameter type may be  specified  as  in  the  previous  variant  of
              makelist. The third parameter must be either newer (or younger) or older. A list of
              all file-system entries matching mask which are,  resp.,  newer  or  older  than  a
              provided  comparefile  is  returned. Note that newer and younger are operators, not
              strings;

       o      int printf(argument(s))
              the function’s (comma or left-shift (insertion) operator separated)  arguments  are
              written  to  the standard output file (cf.  the expression statement description in
              section FLOW CONTROL and this section’s description of the  fprintf  function).  If
              the  first  argument  contains %1, %2, ... %n specifications then it’s considered a
              format string (see also the function strformat in the STRING FUNCTIONS section  for
              additional  information  about  format  strings).  Like  fprintf printf returns the
              number of printed arguments;

       o      list stat([int check,] string entry)
              Returns stat(2) information of directory entry entry as a list. The first  argument
              is  optional:  if  omitted  and  calling  the  system  stat function fails then the
              icmake-script ends with exit value 1; by specifying P_NOCHECK  the  function  won’t
              terminate the script but returns the return value (-1) of the system stat function.

              The returned list has two elements:

              its  first  element ([0]) holds the entry’s attributes.  Attributes are returned as
              the file type and mode of the specified file (cf. stat(2) and inode(7)). E.g.,

                  S_IRUSR  - owner has read permission
                  S_IWUSR  - owner has write permission
                  S_IXUSR  - owner has execute permission

                  S_IFSOCK - socket
                  S_IFLNK  - symbolic link
                  S_IFREG  - regular file
                  S_IFBLK  - block device
                  S_IFDIR  - directory
                  S_IFCHR  - character device
                  S_IFIFO  - FIFO

              its second element ([1]) contains the entry’s  size  in  bytes.  If  P_NOCHECK  was
              specified  and  ’entry’  doesn’t  exists then a list having one element is returned
              containing -1.

       SYSTEM FUNCTIONS:

       o      void arghead(string str)
              support function of exec() (see also below at exec()): defines the `argument  head’
              that  is  used with exec(). By default, the `argument head’ is an empty string. The
              argument head is text that is prefixed to all exec arguments, like a  directory  in
              which provided arguments are found;

       o      void argtail (string str)
              support  function of exec() (see also below at exec()): defines the `argument tail’
              that is used with exec(). By default, the `argument tail’ is an empty  string.  The
              argument  tail  is text that is appended to all exec arguments, like the extensions
              of files that are passed as arguments to exec;

       o      cmdhead(string str)
              support function of exec() (see also below at exec()).  Defines  a  `command  head’
              that  is  used  with  exec().  By  default it is an empty string. It can be used to
              specify, e.g., compiler options when  the  arguments  themselves  are  modified  by
              arghead  and  argtail.   The  cmdhead argument itself is not modified by arghead or
              argtail;

       o      cmdtail(string str)
              support function of exec() (see also below at exec()).   Defines  a  `command  tail
              that  is  used  with  exec().  By  default it is an empty string. It can be used to
              specify a final argument (not modified by arghead and argtail);

       o      list eval(string str)
              this function can be used instead of the backtick operator (cf. section OPERATORS).
              The  example  provided  with  the  backtick operator could therefore also have been
              written like this:

                  printf << eval("ls");   // prints the elements in the current
                                          // directory

              As mentioned at the backtick  operator:  the  elements  of  the  list  contain  the
              subsequent  lines  of  output  (including  a final newline, if present) produced by
              `cmd’. A command that could be executed but that did not produce any output returns
              a list containing one string element, which is empty.

              An empty list indicates that the command could not be executed.

       o      int exec([int check,] string cmd, argument(s))
              Executes  the  command  cmd with (optional) arguments. Each argument is prefixed by
              arghead and postfixed by argtail. Note that no blanks are inserted between arghead,
              argument(s),  and  argtail. The thus modified arguments are concatenated, separated
              by single blanks. Cmdhead is inserted between cmd and the first argument (delimited
              by  single  blanks) and cmdtail is appended to the arguments, separated by a single
              blank. PATH is searched to locate cmd. 0 is returned.

              The first argument is optional: if omitted and the command does not  return  0  the
              icmake  script  terminates. By specifying P_NOCHECK exec won’t terminate the script
              but returns the called command’s exit status,  or  0x7f00  if  the  command  wasn’t
              found.

              The  remaining  arguments may be ints, strings or lists. Int and list arguments are
              cast to strings. Their string representations are then appended to cmd;

       o      int  execute([int  checking,]  string  cmd,   string   cmdhead,   string   arghead,
              argument(s), string argtail, string cmdtail)
              Same functionality as the previous function, but the cmdhead, arghead, argtail, and
              cmdtail are explicitly specified (and are reset to empty  strings  after  executing
              cmd);

       o      exit(expression)
              Ends  the  execution  of  an  icmake-script. The expression must evaluate to an int
              value, which is used as the script’s exit value;

       o      list getenv(string envvar)
              returns the value of environment variable envvar in a list containing two elements:

              if the first element ([0]) is "1" then the environment variable was defined;

              environment variables are of the form variable=value.  If element [0] is  "1"  then
              the  returned  list’s  second  element  [1] holds the value part of the environment
              variable, which is empty if the environment variable is merely defined;

       o      int getpid()
              returns the process-id of the icmake byte code interpreter icm-exec;

       o      int putenv(string envvar)
              adds or modifies envvar to the current icmake-script environment. Use  the  format:
              "VAR=value". Use "VAR" to remove "VAR" from the environment. The function returns 0
              unless envvar is empty, in which case 1 is returned;

       o      int system([int check,] string command)
              executes command using the system(3) function. The first argument is  optional:  if
              omitted and calling the system(3) function does not return 0 then the icmake-script
              ends with exit value 1; by specifying  P_NOCHECK  icmake’s  system  function  won’t
              terminate  the  script  but  returns  the  return  value  of the system(3) function
              (normally  the  executed  command’s  exit  value).  The  string  command  may   use
              redirection and/or piping.

USER DEFINED FUNCTIONS

       In  addition  to  main additional functions are usually defined. Once defined, they can be
       called. Forward referencing of either variables or functions is not supported, but calling
       functions  recursively  is.  As function declarations are not supported indirect recursion
       cannot be used.

       User-defined functions must have the following elements:

       o      The function’s return type, which must be void, int, string or list.  There  is  no
              default type;

       o      The function’s name, e.g., compile;

       o      A  parameter list, defining zero or more comma-separated parameters. The parameters
              themselves consist  of  a  type  name  (int,  string,  or  list)  followed  by  the
              parameter’s identifier. E.g., (string outfile, string source);

       o      A body surrounded by a pair of curly braces ({ and }).

       Function  bodies  may  contain  variable  definitions  (optionally  initialized  at  their
       definitions). Variable definitions start with a type name, followed by one or  more  comma
       separated and optionally initialized variable identifiers.

       If  a  variable  is not explicitly initialized it is initialized by default: int variables
       are initialized to 0, string variables are initialized to  empty  strings  ("")  and  list
       variables are initialized to empty lists.

       Function  bodies may also contain zero or more statements (cf. section FLOW CONTROL). Note
       that variables may be defined (and optionally initialized) anywhere inside functions where
       expression  statements  can  be  used,  and also in the condition sections of if, for, and
       while statements and in the initialization sections of if andd for statements.

EXAMPLE

       In the following example all C++ source files in the current directory are compiled unless
       their  object  files are more recent. The main function creates a list of source files and
       then passes each of them to a function inspect. That function inspects whether the  source
       file  is  younger  than  its object file, and if so it calls compile. The function compile
       uses exec to call the compiler. If a compilation fails the script stops so the  error  can
       be  repaired. Source files for which the compilation succeeded are not recompiled when the
       script is rerun. Assuming the script is named compile.im  then  it  can  be  called  using
       icmake  -s  compile.im.  This  also  creates compile.bim, so after the -s call the command
       icmake -e compile.bim can be used to immediately execute the bim-file:

           void compile(string src)
           {
               exec("g++ -c " + src);      // compile ’src’
           }

           void inspect(string src)
           {                               // get the obj-file’s name:
                                           // only compile if necessary
               if (src younger change_ext(src, ".o"))
                   compile(src);
           }

           int main()
           {                               // find all .cc source files
               list sources = makelist("*.cc");

               for (                       // visit all source files
                   int idx = 0, end = listlen(sources);
                       idx != end;
                           ++idx
               )
                   inspect(sources[idx]);  // compile if needed
           }

SEE ALSO

       icmake(1), icmbuild(1), icmconf(7), icmstart(1), icmstart.rc(7)

BUGS

       Standard comment starting  on lines containing preprocessor directives may not extend over
       multiple lines.

       Path names containing blanks are not supported.

COPYRIGHT

       This  is  free  software,  distributed  under  the terms of the GNU General Public License
       (GPL).

AUTHOR

       Frank B. Brokken (f.b.brokken@rug.nl).