ICMake 第 3 部分
除了 C 编程语言的运算符之外,icmake 还识别一些“特殊”运算符。这些运算符是
“younger”运算符用于比较两个表示文件名的字符串。使用 younger 的表达式将被评估为非零或零,并可以在条件中使用。“newer”运算符是 younger 的别名。
如果左操作数表示的文件名比右操作数表示的文件名更新,则使用 younger 运算符的表达式将产生非零值。
例如,如果文件 main.c 比 main 更新,则以下代码会打印一条消息
if ("main.c" newer "main") printf ("main.c is more recent than main\n");
“older”操作数比较两个文件,如果左操作数表示的文件比右操作数表示的文件旧,则产生非零值。当使用 older 或 younger 比较文件的日期,并且不存在具有该名称的文件时,则假定文件的年龄为无限大。此实现的一个结果是,如以下代码示例所示,如果“try”不存在,则会显示一条消息
if ("try.c" younger "try") printf ("try.c should be compiled!!\n");
虽然 icmake 不允许对不同类型使用运算符,但可以将一种类型转换为另一种类型。将一种类型转换为另一种类型称为“类型转换”。
类型转换用括号中的类型名称表示,放在应转换的操作数之前,例如,(int)x 将操作数 x 转换为整数表示。类型转换并非在所有类型上都允许。例如,列表变量不能转换为 int。
允许以下类型转换
整数可以转换为字符串。例如
string stringvar; stringvar = (string) 14; // now, stringvar is "14"
字符串可以转换为 int。这是上面列表中显示的类型转换的相反操作。
字符串可以转换为列表。当文件名应添加到列表或从列表中删除时,这可能特别有用,例如,在下面的列表中,文件名“main.c”(一个字符串)从列表 cfiles 中删除
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
请注意,字符串“main.c”必须转换为列表类型,才允许从列表中减去。
其他类型转换,特别是从字符串到 ascii 表示形式的转换,可以通过专用函数实现(例如,请参阅下一节中的 ascii() 函数)。
icmake 内置了许多函数,可用于执行特殊操作,例如扫描目录中的文件、显示信息等。在此,描述了所有内置函数。
arghead: int arghead(string): 此函数将参数头设置为 string。“参数头”与下面描述的函数 exec() 和 execute() 一起使用。
argtail: int argtail(string): 此函数将参数尾设置为 string。“参数尾”与下面描述的函数 exec() 和 execute() 一起使用。
ascii: int ascii(string): 此函数返回字符串中第一个字符的 ascii 码,以字符串形式提供参数,例如,ascii(“A”) 返回 65。
ascii: string ascii(int): 重载函数,它接受一个 int 参数,返回数字 ascii 码的字符串表示形式。例如,ascii(65) 返回 “A”。
change_base: string change_base (string, string): 此函数将作为其第一个参数提供的字符串中的基本名称更改为作为第二个参数提供的基本名称。返回带有更改后的基本名称的字符串。
示例
string name; name = change_base ("main.c", "test"); // name now is "test.c"
change_ext: string change_ext(string, string): 此函数将作为其第一个参数提供的字符串中的扩展名更改为作为第二个参数提供的扩展名。返回修改后的字符串。
扩展名(第二个参数)可以指定为空字符串(“”);在这种情况下,change_ext() 会删除扩展名。此外,扩展名可以指定为一个点(“.”);在这种情况下,change_ext() 会删除扩展名,但保留点。
示例
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."
change_path: string change_path(string, string): 此函数将作为其第一个参数提供的字符串中的路径更改为作为第二个参数提供的路径。
示例
string name; // name is now "/bin/prog" name = change_path ("c:/usr/local/bin/prog", "/bin"); name = change_path (name, ""); // name is now "prog"
chdir: string chdir(int, string): 此函数将当前工作目录更改为提供的名称。第一个 int 参数可以是 P_CHECK 或 P_NOCHECK。此参数是可选的。如果省略,则假定为 P_CHECK。如果使用 P_CHECK 无法更改工作目录,则会导致 icmake 进程终止。
但是,在 icmake 进程完成时,始终会恢复原始目录。
返回一个包含新工作目录的字符串,始终以目录分隔符结尾。字符串参数可以以最后的斜杠结尾。鉴于修饰符 P_NOCHECK 作为第一个参数提供,返回的字符串可用于检查是否已到达请求的目录。
chdir() 识别两个特殊的字符串参数
a. 由一个点组成的目录参数(即字符串“.”)实现“更改”到当前目录。然后,返回值是一个字符串,其中包含当前工作目录。
b. 空字符串(即字符串“”)的目录参数不会产生目录更改。相反,返回 icmake 最初启动的目录。
示例
// 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");
cmdhead: int cmdhead(string): 此函数将命令头设置为 string。“命令头”与下面描述的函数 exec() 和 execute() 一起使用。
cmdtail: int cmdtail(string): 此函数将命令尾设置为 string。“命令尾”与下面描述的函数 exec() 和 execute() 一起使用。
echo: int echo(int): 此函数确定在执行命令之前是否将显示该命令。该函数的参数确定显示模式:当为零时,禁止显示;否则,命令在执行前显示。两个预定义的常量可用于作为 echo() 的参数:常量 ON 和 OFF。这些常量的值分别为 1 和 0。最初,回显处于打开状态。
示例
echo (ON); // commands will be displayed echo (OFF); // commands will not be displayed
element: string element(int, list): 此函数从列表中检索字符串。列表中名称的顺序号由第一个参数给出。请注意,此索引是基于零的,即,列表中的第一个元素的索引为 0。列表中最后一个元素的索引为 sizeoflist(list) - 1。
示例
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");
element: string element(int, string): 此函数从作为其第二个参数给出的字符串中检索一个字符的子字符串。
返回的字符位于第二个(字符串)参数中,偏移位置在第一个(int)参数中指定。此索引是基于零的:字符串的第一个字符的索引为 0。
示例
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");
exec: int exec(int, string, ...): 此函数通过派生子进程来执行命令。参数是
a. 第一个参数是可选模式(int)。它可以是 P_CHECK (0) 或 P_NOCHECK (2)。这些预定义的常量确定是否应检查命令的退出状态。如果要检查退出状态,并且调用的程序返回非零值,则 icmake 文件的处理将中止。如果省略第一个参数(即,如果第一个参数不是 int),则假定为 P_CHECK。
b. 第二个参数是要运行的命令(字符串)。这是要激活的程序的名称。
c. 以下参数是应传递给调用程序的参数。这些参数可以是 int、列表或字符串。
每个命令都由程序名称(第二个参数)组成,后跟命令头的当前设置(请参阅 cmdhead()),后跟所有参数,并以命令尾(请参阅 cmdtail())终止。命令的每个参数都以参数头(请参阅 arghead())为前缀,并以参数尾(请参阅 argtail())为后缀。
execute: int execute(int, string, string, string, ..., string, string): 此函数通过派生子进程来执行命令。参数是
a. 第一个参数是可选模式(int)。它可以是 P_CHECK (0) 或 P_NOCHECK (2)。这些预定义的常量确定是否应检查命令的退出状态。如果要检查退出状态,并且调用的程序返回非零值,则 icmake 文件的处理将中止。如果省略第一个参数(即,如果第一个参数不是 int),则假定为 P_CHECK。
b. 第二个参数是要运行的命令(字符串)。这是要激活的程序的名称。
c. 第三个参数是命令头(字符串)。此字符串用作程序名称的第一个参数。该字符串可以为空(即“”),在这种情况下,不使用命令头。
d. 第四个参数是参数头(字符串)。此字符串作为所有后续参数的前缀。该字符串可以为空,在这种情况下,不使用参数头。
e. 以下参数是应传递给调用程序的参数。这些参数可以是 int、列表或字符串。
f. 倒数第二个参数是参数尾(字符串)。此字符串作为传递给调用程序的每个参数的后缀。该字符串可以为空,在这种情况下,不使用参数尾。
g. 最后一个参数是命令尾(字符串)。execute() 函数运行的命令以此字符串为后缀。该字符串可以为空,在这种情况下,不使用命令尾。
执行后,execute() 会将命令头、命令尾、参数头和参数尾重置为空字符串。如果错误检查已打开(模式标志 P_CHECK)并且运行的命令以非零退出值退出,则 exec() 和 execute() 函数都会终止制作过程。如果错误检查已关闭,则返回子进程的退出状态。
exists: int exists(string): 此函数测试文件是否存在。文件名作为参数提供。当文件存在时,返回非零值;否则,返回零。
if (exists ("main.c")) printf ("file main.c found\n"); else printf ("file main.c not found\n");
fgets: list fgets(string, int): 此函数从文件(其名称作为其第一个(字符串)参数给出)中读取一行文本。读取从第二个(int)参数中指定的偏移位置开始。返回一个列表,其中包含作为其第一个元素的读取字符串,包括最终的换行符(因为它由 C++ 函数 fgets() 返回)。返回列表的第二个元素包含读取行后文件偏移量的字符串表示形式。此字符串可以强制转换为 int。
示例
// 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: int fprintf(string, ...): 此函数将信息附加到文件(其名称作为其第一个字符串参数给出)。其余参数定义写入文件的信息。信息始终附加到现有文件,该文件以文本模式打开。
fprintf() 的作用类似于 printf()(请参阅下文),但信息是写入文件而不是屏幕。
fprintf() 的第一个参数之后的参数定义要打印的信息,并且可以是 int、列表或字符串。示例:fprintf ("file.txt", 1, " line written to file.txt\n");
get_base: string get_base(string): 此函数返回存储在字符串参数中的文件名的基本名称。如果参数不包含基本名称,则返回空字符串。当在字符串中指定磁盘或根目录时,会发生这种情况。如果文件名规范的语法规则被违反,也可能发生这种情况。示例
// prints 'main' printf(get_base ("/path/main.c")); // prints 'No basename: ' printf("No basename: ", get_base ("/"));
getch: string getch(): 此函数返回一个字符作为迷你字符串。该字符是从标准输入流(通常是键盘)读取的。
在 Unix 下,此函数会等待直到按下键和 Enter 键。示例
printf(getch()); // prints a character // (or an empty string)
get_ext: string get_ext(string): 此函数返回函数字符串参数中的扩展名。如果参数不包含扩展名,则返回空字符串。示例
printf(get_ext ("/path/main.c")); // prints 'c' printf(get_ext ("main")); // returns empty string
get_path: string get_path(string): 此函数返回存储在函数字符串参数中的路径。如果字符串不包含磁盘说明符,则返回空字符串。该函数返回可以从字符串参数派生的最长可能路径名。示例
printf(get_path ("/path/main.c")); // prints '/path/' get_path ("main.c"); // returns an empty string
getpid: int getpid(): 此函数返回当前正在执行的 icmake 程序的进程号。示例
// 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()); }
gets: string gets(): 此函数将 C 函数 gets() 的返回值作为字符串返回。该函数接受字符,包括允许更正的退格键,直到按下 Enter 键。输入的字符以字符串形式返回。示例
printf(gets()); // prints a string // (or an empty string)
makelist: list makelist(int, string): 此函数创建一个字符串列表,表示与参数的展开形式匹配的文件名。参数是可选的 int,用于指定要搜索的目录条目的类型,以及指定文件掩码的字符串。返回的列表可以包含零个或多个名称。
第一个 int 参数指定要搜索的条目类型。它可以是 O_FILE(搜索文件时)、O_DIR(搜索目录时)或 O_SUBDIR(搜索子目录时)。搜索目录和搜索子目录之间的区别在于,当前目录(用“.”表示)和父目录(用“..”表示)不被视为子目录,但被视为目录。此参数可以是缺席的,在这种情况下,假定为 O_FILE。
第四种类型是 O_ALL。当给出此类型时,makelist() 会搜索所有目录条目,而不管其类型如何;例如,在 DOS 下,隐藏文件和系统文件以及普通文件或(子)目录也会匹配。
makelist() 的行为取决于所使用的平台,例如,要在 DOS 下搜索所有文件或(子)目录,必须给出文件掩码“*.*”。文件掩码“*”将无法找到带有扩展名的文件或(子)目录。此外,makelist() 在 DOS 下的行为类似于 C 运行时函数 _dos_findfirst() 和 _dos_findnext();例如,makelist(O_DIR, ".") 返回一个包含当前目录名称的列表。
类似地,在 Unix 下,文件掩码“*”将无法找到以点开头的文件或(子)目录。示例
list l; l = makelist ("*.c"); printf ("All found *.c files are: ", l, "\n"); l = makelist (O_SUBDIR, "*"); printf ("All found subdirectories are: ", l, "\n");
makelist: 此外,函数 makelist() 具有重载版本。这些版本除了第一个可选的 int(指示要搜索的条目类型)外,还需要三个参数。这些版本的参数是(字符串)掩码;比较运算符,必须是 younger、newer 或 older;以及(字符串)referencefile。该函数返回与掩码匹配且比 referencefile 旧或新的文件列表。
可选的第一个 int 参数(指定目录条目的类型(O_FILE、O_DIR 或 O_SUBDIR))可以存在。示例
list l; l = makelist ("*.c", newer, "libprog.a"); printf ("All .c files newer than libprog.a are: ", l, "\n");
printf: int printf(int, ...): 此函数显示信息。参数定义要打印的信息,并且可以是 int、列表或字符串。列表打印为一系列所有元素,元素之间用空格分隔。
putenv: void putenv(string): 此函数可用于在 icmake 程序执行期间设置环境变量。环境变量在整个 icmake 运行期间保持活动状态。示例
main() { putenv("term=vt320"); // set variable system("set"); // show settings }
sizeof: int sizeof(list): 此函数执行与 sizeoflist() 相同的操作。
sizeoflist: int sizeoflist(list): 此函数确定列表中包含的名称数量。示例
list l; int i; list = makelist ("*.c"); i = sizeoflist (l); printf ("There are ", i, " names in the list.\n");
stat: list stat(int, string): 此函数尝试检索由第二个字符串参数指定文件的文件属性。第一个 int 参数可以是 P_CHECK 或 P_NOCHECK。如果省略,则假定为 P_CHECK。当 stat() 无法检索文件属性并且为第一个参数给出 P_CHECK 时,制作过程将中止。返回的列表包含以下信息
a. 第一个元素是文件或目录模式的字符串表示形式。此字符串可以转换为 int,其中以下位表示模式
当条目是目录时,设置位 S_IFDIR。
当条目是字符特殊文件时,设置位 S_IFCHR。
当条目是常规文件时,设置位 S_IFREG。
当条目可读时,设置位 S_IREAD。
当条目可写时,设置位 S_IWRITE。
当条目可执行时,设置位 S_IEXEC。
第二个元素是文件大小,也表示为字符串。
strlen: int strlen(string): 此函数返回作为其参数提供的字符串中的字符数。其工作原理类似于 C 中的函数 strlen()。
strlwr: string strlwr(string): 此函数返回作为参数提供的字符串的副本,以小写形式返回。
strupr: string strupr(string): 此函数返回作为参数提供的字符串的副本,以大写形式返回。
strtok: list strtok (string, 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");
substr: int substr(string, string) 此函数在作为第一个参数给出的字符串中搜索作为第二个参数给出的字符串。返回第二个字符串在第一个字符串中出现的位置。当第二个字符串未在第一个字符串中出现时,返回 -1 值。
system: int system(int, string): 此函数激活操作系统的命令解释器以运行由参数定义的命令。该字符串包含要执行的命令以及(如果需要)其参数。
第一个参数指定 system() 函数的失败是否应终止制作过程。此 int 的值可以是 P_CHECK 或 P_NOCHECK。此参数可以是缺席的,在这种情况下,假定为 P_CHECK。当命令可以成功执行时,system() 的返回值为零。只有当给出 P_NOCHECK 作为第一个参数时,makefile 才能接收非零返回值。
如果命令可以执行并且命令本身的返回值为零,则 system() 成功。