Part 2 covered some of the the grammar of icmake source files. This part completes the task. The final part of this article will appear next month and will show examples of the use of icmake.
by Frank B. Brokken and K. Kubat
ICCE, Department of Education
University of Groningen
Groningen, the Netherlands
In addition to the operators of the C programming language, icmake recognizes some `special' operators. These are:
The expression using the younger operator yields non-zero if a file with the name represented by the left operand is more recent than the file represented by the right operand.
E.g., the following code prints a message if file main.c is more recent than main:
if ("main.c" newer "main") printf ("main.c is more recent than main\n");
if ("try.c" younger "try") printf ("try.c should be compiled!!\n");
Though icmake does not allow the use of operators on different types, it is possible to convert one type into another. The conversion of a type into another type is referred to as a `type cast'.
Type casts are denoted by a type name in parentheses before the operand which should be converted, e.g., (int)x converts the operand x to integer representation. Type casts are not allowed on all types. For example, a list variable cannot be converted to int.
The following type casts are permitted:
string stringvar; stringvar = (string) 14; // now, stringvar is "14"
list cfiles; // cfiles is set to hold a list of cfiles = makelist ("*.c"); // all filenames with extension .c cfiles -= (list) "main.c"; // filename main.c is removed from // the list
Note that the string "main.c" must be converted to a list type to allow the subtraction from the list.
Other typecasts, specifically from a string to an ascii-representation, can be realized through specialized functions (see, e.g., the function ascii() in the next section).
Built into icmake is a number of functions which may be used to perform special operations, such as scanning a directory for files, displaying information, etc.. Here, all built-in functions are described.
Example:
string name; name = change_base ("main.c", "test"); // name now is "test.c"
The extension (the second argument) may be specified as an empty string (""); in this case change_ext() removes the extension. Also, the extension may be specified as one dot ("."); in this case change_ext() removes the extension but leaves the dot.
Example:
char name; name = change_ext ("main.c", "o"); // name now is "main.o" name = change_ext (name, ""); // name now is "main" name = change_ext (name, "."); // name now is "main."
Example:
string name; // name is now "/bin/prog" name = change_path ("c:/usr/local/bin/prog", "/bin"); name = change_path (name, ""); // name is now "prog"
However, on completion of the icmake process the original directory is always restored.
A string containing the new working directory, always ending in a directory separator, is returned. The string argument may terminate in a final slash. The returned string can be used to inspect whether the requested directory is reached, given that the modifier P_NOCHECK is supplied as first argument.
Two special string arguments are recognized by chdir():
a. A directory argument which consists of one dot (i.e., the string ".") realizes a `change' to the current directory. The return value is then a string holding the current working directory.
b. A directory argument which is an empty string (i.e., the string "") will not produce a directory change. Instead, the directory from which icmake was started originally is returned.
Example:
// print the current working directory printf("Current dir: ", chdir ("."), "\n"); chdir ("/usr/bin"); // change to directory /usr/bin // print startup directory printf ("Startup dir: ", chdir (""), "\n");
Example:
echo (ON); // commands will be displayed echo (OFF); // commands will not be displayed
Example:
list l; string n; int i; l = makelist ("*.c"); for (i = 0; i < sizeoflist (l); i++) if (element (i, l) newer "main") printf ("Source file ", element (i, l), " is more recent than main\n");
The character which is returned is found in the second (string) argument at the offset position specified in the first (int) argument. This index is zero-based: the first character of the string has index 0.
Example:
string s; int count; i; count = 0; s = "Hello world"; for (i = 0; element(i, s); i++) count++; printf("String '", s, "' contains ", count, " characters.\n");
a. The first argument is an optional mode (an int). It may be P_CHECK (0) or P_NOCHECK (2). These predefined constants determine whether the exit status of the command should be checked or not. If the exit status should be checked, and a non-zero value is returned by the called program, the processing of the icmake file is aborted. If the first argument is omitted (i.e., if the first argument is not an int), P_CHECK is assumed.
b. The second argument is the command to run (a string). This is the name of the program to be activated.
c. The following arguments are the arguments which should be passed to the called program. These arguments may be ints, lists or strings.
Each command is composed of the program name (the second argument), followed by the current setting of the command head (see cmdhead(), followed by all arguments and terminated with the command tail (see cmdtail()). Each argument to the command is prefixed with the argument head (see arghead()) and postfixed by the argument tail (see argtail()).
a. The first argument is an optional mode (an int). It may be P_CHECK (0) or P_NOCHECK (2). These predefined constants determine whether the exit status of the command should be checked or not. If the exit status should be checked, and a non-zero value is returned by the called program, the processing of the icmake file is aborted. If the first argument is omitted (i.e., if the first argument is not an int), P_CHECK is assumed.
b. The second argument is the command to run (a string). This is the name of the program to be activated.
c. The third argument is the command head (a string). This string is used as first argument to the program name. The string may be empty (i.e., ""), in which case no command head is used.
d. The fourth argument is the argument head (a string). This string is prefixed to all following arguments. The string may be empty, in which case no argument head is used.
e. The following arguments are the arguments which should be passed to the called program. These arguments may be ints, lists or strings.
f. The next to the last argument is the argument tail (a string). This string is postfixed to each argument passed to the called program. The string may be empty, in which case no argument tail is used.
g. The last argument is the command tail (a string). The command run by the execute() function is postfixed with this string. The string may be empty, in which case no command tail is used.
After execution, execute() resets the command head, command tail, argument head and argument tail to empty strings. Both the exec() and execute() functions terminate the making process if error checking is turned on (mode flag P_CHECK) and if the run command exits with a non-zero exit value. If error checking is off, the exit status of the child process is returned.
if (exists ("main.c")) printf ("file main.c found\n"); else printf ("file main.c not found\n");
Example:
// showing the file info.doc on the screen: int offset; list l; for ( offset = 0; l = fgets("info.doc", offset); offset = (int)element(1, l) ) printf(element(0, l));
fprintf() acts analogously to printf() (see below), but the information is written to file rather than to screen.
The arguments beyond the first argument of fprintf() define the information to print and may be ints, lists or strings. Example: fprintf ("file.txt", 1, " line written to file.txt\n");
// prints 'main' printf(get_base ("/path/main.c")); // prints 'No basename: ' printf("No basename: ", get_base ("/"));
Under Unix, this function waits until a key and the enter key are pressed. Example:
printf(getch()); // prints a character // (or an empty string)
printf(get_ext ("/path/main.c")); // prints 'c' printf(get_ext ("main")); // returns empty string
printf(get_path ("/path/main.c")); // prints '/path/' get_path ("main.c"); // returns an empty string
// this function kills the current process.. // analogous to exit() void harakiri() { exec ("kill", "-9", getpid ()); } // this function returns a name for a temporary file // based on the process ID number file names are, // e.g., "/tmp/_TMPFILE.1256" string tempfilename () { return ("/tmp/" + "_TMPFILE." + (string)getpid()); }
printf(gets()); // prints a string // (or an empty string)
The first int argument specifies the type of entries to search for. It may be O_FILE (when searching for files), O_DIR (when searching for directories) or O_SUBDIR (when searching for subdirectories). The difference between the searching for directories and the searching for subdirectories lies in the fact that the current directory, denoted by ".", and the parent directory, denoted by "..", are not considered subdirectories but are considered directories. This argument may be absent, in which case O_FILE is assumed.
A fourth type is O_ALL. When this type is given, makelist() searches for all directory entries irrespective of their type; e.g., under DOS, hidden and system files are matched as well as normal files or (sub)directories.
The behavior of makelist() is dependent on the used platform, e.g., to search for all files or (sub)directories under DOS, the file mask "*.*" must be given. The file mask "*" will fail to find files or (sub)directories with an extension. Furthermore, makelist() behaves under DOS similar to the C run-time functions _dos_findfirst() and _dos_findnext(); e.g., makelist(O_DIR, ".") returns a list containing the name of the current directory.
In a similar vein, the filemask "*" will, under Unix, fail to find files or (sub)directories starting with a dot. Example:
list l; l = makelist ("*.c"); printf ("All found *.c files are: ", l, "\n"); l = makelist (O_SUBDIR, "*"); printf ("All found subdirectories are: ", l, "\n");
An optional first int argument, which specifies the type of directory entry (O_FILE, O_DIR or O_SUBDIR) may be present. Example:
list l; l = makelist ("*.c", newer, "libprog.a"); printf ("All .c files newer than libprog.a are: ", l, "\n");
main() { putenv("term=vt320"); // set variable system("set"); // show settings }
list l; int i; list = makelist ("*.c"); i = sizeoflist (l); printf ("There are ", i, " names in the list.\n");
a. The first element is a string representation of the mode of the file or directory. This string can be converted to an int where the following bits represent the modes:
The second element is the file size, also represented as a string.
list l; int i; l = strtok("Hello-world\n", "-"); printf("Two elements: ", l, "\n"); l = strtok("Hello-world\n", ""); printf("A string of ", sizeof(l), " characters\n");
The first argument specifies whether a failure of the system() function should terminate the making process. The value of this int may be P_CHECK or P_NOCHECK. This argument may be absent, in which case P_CHECK is assumed. The return value of system() is zero when the command could successfully be executed. A return value which is not zero can be received by the makefile only when P_NOCHECK is given as first argument.
system() succeeds if the command could be executed and if the return value of the command itself is zero.