嵌入式web——CGIC 中文文档

1. 什么是 cgic?

cgic 是一个 c 语言库,用于基于 CGI 标准规范的 www 应用程序的创建。
cgic 执行以下任务:
(1)解析数据,校正有缺陷和不一致的浏览器
(2)透明的接受由 GET 和 POST 传递的表单数据
(3)接受上传完毕的文件和普通的表单数据
(4)提供设置和恢复 cookies 的函数
(5)以一致的方式处理表单字段的换行符
(6)提供字符串,整数,浮点数,单选,多选 函数来恢复表单数据
(7)对于数字字段提供边界检查
(8)加载 CGI 环境变量到 C 字符串,这些字符串总是不为空
(9)在调试环境下,提供捕获 CGI 状态的方法,包括文件上传和 cookies

cgic 在兼容 CGI 的服务器环境下是可移植的,并且在 posix/Unix/Linux/Windows 环境下,不用修改编译文件。

2. 构建 cgic:一个简单的应用

一个简单的例子 “cgictest.c”, 包含在 cgic 库里面。这个 CGI 程序显示一个输入表单, 接收一个提交,然后显示提交的内容。在这个过程中,基本上所有的 cgic 的特性都被测试了。
在一个 Unix 系统上,可以通过输入‘make cgictest.cgi’来构建 cgictest。cgic.c,cgictest.c 将会编译,然后链接产生这个 cgictest 应用程序。在非 unix 操作系统上,你需要创建一个工程包含 cgic.c 和 cgictest.c,然后编译它。

3. 怎样编写一个 cgic 应用程序

注:所有的 cgic 应用程序必须连接到 cgic.c 模块自身。怎样连接取决于你的操作系统,在 unix 下, 提供的 Makefile 文件作为一个使用范例 。

因为所有的 CGI 应用程序必须执行确定的初始化任务,例如解析表单数据和检查环境变量,cgic 库提供了它自己的 main() 函数。当你用 cgic 库来写应用时,你应该实现一个 cgiMain() 函数来作为程序的开始,当初始化 cgi 的工作成功结束之后。cgic 库将会调用 cgiMain() 函数。你的程序必须包含 “cgic.h” 头文件。

重要:如果你写了你自己的 main() 函数,你的程序将不能顺利链接。你的代码应该从 cgiMain() 开始。cgic 库为你提供了 main()。(想要不同行为的人可以修改 cgic.c 文件)

(对照 cgictest.c)
注意这个 DEBUG #ifdef。如果 DEBUG 被定义在编译时期,或者 “#define DEBUG 1" 到程序里面,或者在 Makefile 文件 中设置,或者其他的开发环境,那么 LoadEnvironment() 函数将被调用,这个函数调用 cgiReadEnvironment() 来恢复一个被捕获的 CGI 环境用于调试的目的。另外可以看 capture 程序的讨论,这个程序是用来在 CGI 调试的时候使用的。因为这是 一个测试程序,cgiFormSubmitClicked() 函数也被调用来检查按钮的点击状态,这个按钮用来重新载入被保存的 CGI 环境。
一个最终的 CGI 程序一般情况从不允许终端用户来做这个决定。

4. 设置 Cookies

下一步,一个 cgiHeader 函数将被调用。这个特殊的程序演示了许多功能,包括 cookies 的设置。如果程序员希望设置一个 cookie,这个 cookie 设置函数将被第一个调用,在其他的标题(Headers)之前被输出。在 cgictest.c 中调用 CookieSet() 函数来完成。因为这是一个测试程序,cgiFormString() 函数被用来从用户之前填入的表单中获取名字和值。通常,cookie 名字和值被用来满足程序员的需求并且提供一种之后标识相同用户的方法。

cgiHeaderCookiesSetString() 函数通过请求浏览器存储的内容来设置 cookie。这不能保证一定会成功。许多浏览器完全 拒绝 cookies,另外,浏览器不一定会保存他们直到服务器请求的到来,如果还在的话就完好无损的返回这些值。当使用 cookies 的时候代码总是要有防御性的。
这个 cname 和 cvalue 参数当然就是 cookie 的名称和值,第三个参数是时间,以秒计数,表示浏览器应该存储的时间,之后 就过期。在这个例子中,被设置成 86400 秒,表示一天。浏览器可能遵守也可能不遵守这个设置,正如 cookie 的其他一切 一样。
第四个参数标识了在网站中的” 路径 “,对这个路径来说 cookie 是有效的。一个 cookie 要在每一次访问站点的时候以 / 路径的形式被发送回去。
在这个例子里,cookie 只跟 CGI 程序本身有关,所以 cgiScriptName 被发送。
类似的,一个 cookie 被认为跟一个单一站点相关或者整个域,例如 www.boutell.com 或者整个
.boutell.com 域。这个例子中,当前的这个程序 在它上面跑的这个站点是仅有的相关站点,所以 cgiServerName 被当做域。

5. 输出 Content Type Header

下一步,cgiHeaderContentType() 函数被调用来标识输出文档的 MIME 类型,这个例子中是”text/html“。其他一些普通的MIME 类型是”image/gif“,”image/jpeg“ 和 ”audio/wav“。
注意,cgiHeaderStatus() 或者 cgiHeaderLocation() 被调用来输出一个错误代码或者重定向到一个不同的 URL。在程序的单次执行中,只能有一个 cgiHeader 函数可以被调用。

重要:一个 cgiHeader 函数,通常是 cgiHeaderContentType(),必须在输出任何其他应答之前调用。否则,结果将是一个不合法的文档,浏览器的行为不可预测。你当然也可以输出你自己的 ContentType 和其他的标题信息到 cgiOut,如果你喜欢。 cgiHeader 函数只是提供了一种便利。

6. 处理表格提交

像许多 CGI 程序一样,cgitest 基于不同的提交按钮是否被按下来判断应该怎样处理。当 testcgic 或者 saveenvironment 按钮被按下,cgitest 调用 HandleSubmit 函数,它将调用其他的函数来处理表单的不同部分。

7. 处理文本输入

cgitest 中 Name() 这个函数的目的是取回并且显示用户输入的名字。因为程序员决定名字被允许的最大长度是 80 个字符, 一个 81 字符缓冲区被声明(接收最后的 null 字符)。
接着,cgiFormStringNoNewlines() 函数被调用来取出名字并且确保回车不出现在这个名字中(忽略一些网页浏览器的错误行为)。
第一个参数是表格中的输入字段的名称,
第二个参数是数据存储的 buffer,
第三个参数是 buffer 的长度。cgic 绝不会超出 buffer 大小,并且总是会提供一个null 结尾的字符串作为响应。如果buffer太小,字符串被截断。如果这个不可接受的,可以用cgiFormStringNoNewlines() 函数的返回值同样可以说明截断是否出现。
参考详细的 cgiFormStringNoNewlines() 描述。

8. 处理输出

注意函数 Name() 写他的 HTML 到 cgiOut 而不是 stdout。用户实际提交的名字可能包含对 HTML 来说有特殊意义的字符,特别是 <,>,& 字符。cgiHtmlEscape() 函数用来输出用户输入的名字,他会将名字中这些特殊字符的出现转换成 <,>,&。
重要:cgiOut 通常等价于 stdout,并且没有额外的执行开销。推荐使用 cgiOut 作为输出是确保对” 在改版过后的 cgic 库对于特殊环境下的每一个 cgi 连接不提供 stdin 和 stdout“情况的可移植性。
注意,对于文本输入域,回车是被期望的,这时应该用 cgiFormString() 函数替代。cgiFormString 确保换行符总是被一个单回车符代表(ascii 十进制 13)。参考 cgictest.c 中的 Address() 函数的源代码中例子。

9. 处理复选框

考虑 Hungry() 函数,它检查用户是否选中了 hungry 复选框。
这个函数使用了 cgiFormCheckboxSingle() 函数,它检查是否一个单复选框被勾选。cgiFormCheckboxSingle() 接受复选框的 名字属性作为它唯一的参数,选中返回 cgiFormSuccess,否则返回 cgiFormNotFound。如果多个复选框带有相同的名字,考虑使用 cgiFormCheckboxMultiple(),cgiFormStringMultiple() 函数。

10. 处理数字输入

现在看 Temperature() 函数,它取回一个温度的度数(一个浮点数),并且确保位于一个特定的边界内。cgiFormDoubleBounded() 函数用来取出温度,第一个参数是表格中温度字段名,第二个参数指向接受结果的变量地址,接 下来的两个参数是下限和上限,最后一个是默认值,当用户没有提交这个值时返回默认值。这个函数总是返回一个区间内的合理值,值如果超出下限或者上限,则被限制在边界值。然而,cgiFormDoubleBounded 函数的返回值可以确定用户的输入是否在区间,是否为空,等等。参考 cgiFormDoubleBounded() 函数的详细描述。如果区间检查没有必要,考虑使用 cgiFormDouble() 代替。注意,对于整数输入,可用函数 cgiFormInteger 和 cgiFormIntegerBounded ,他们的行为和上面的浮点数函数类似。

11. 处理单选输入

HTML 的 列表中选择了哪个颜色。先声明一个颜色数组,然后调用 cigFormSelectSingle () 函数测试哪个选项被选中。
第一个参数表示表单中字段名,
第二个参数指向颜色数组,
第三个参数表示数组元素个数,
第四个参数指向一个下标变量,
最后一个参数表示下标默认值,用于浏览器没有提交选择时。
cigFormSelectSingle() 总是标识一个合理的选项值。然而,如果程序员希望确认实际被提交的值是一个合法的值,等等,
可以咨询 cgiFormSelectSingle() 的返回值。查看完整的 cgiFormSelectSingle() 函数的描述获取更多信息。
注意单选按钮组合 列表和带有相同名字的复选框组,请看下面马上讨论的 cgitest.c 中NonExButtons() 函数。

12. 处理多选输入

看看 NonExButtons() 函数前半部分:
这个函数调用 cgiFormCheckboxMultiple() 函数来识别一个或多个被选中的带有相同名字的复选按钮。这个函数与 cgiFormSelectMultiple() 有相同的功能。也就是说,处理 < SELECT> 标签的 MULTIPLE 属性类似于处理带有相同名字的复选按钮选项组。
cgiFormCheckboxMultiple() 函数的
第一个参数是复选框选项组字段的名称,
第二个参数指向一个合法值得数组,这些会对应到复选框的 VALUE 属性(或者 标签的 MULTIPLE 属性和具有相同名字的复选按钮组同样适用。
cgiFormStringMultiple() 函数的第一个参数是输入字段名或者复选框选项组字段名。第二个参数是字符串数组的地址。
看看这个函数的简单调用:cgiFormStringMultiple("vote", &responses);
要怎么知道有多少个响应呢?
调用之后,这个字符串数组的最后一个值时 null。可以通过一个简单的循环计数来统计响应个数。

重要:cgiFormStringMultiple() 函数返回一个指向动态内存的指针。你不应该改变响应数组中的字符串或是数组本身,如果有修改的需求,应该拷贝字符串。当你完成了对响应数组的使用,你要调用 cgiStringArrayFree() 来释放内存。否则找出内存泄露。不要调用 free() 函数,如果你这样做,单个字符串不会被释放。

13. 访问已上传文件

已上传文件作为表单提交的一部分,cgic 提供了函数访问它。
重要:你必须设置 form 标签的 enctype 属性为 multipart/form-data 以让这个功能可用。
作为一个例子,看 cgictest.c 中的 ShowForm() 函数。
cgictest.c 的 File 函数关心的是接收已上传的文件:
首先,函数检测用户提交的文件名。
非常重要:这个文件名不保证和用户电脑上真实的文件名有任何的关系,可能被恶意的目的故意操纵,并且不应该被用于任何的用途除非你确定对于你刻意的使用来说他的内容是安全的,至少不要覆盖一个对你来说重要的文件,特别是如果你想要使用它作为服务端上的一个文件名。
cgic 库自身不会使用这个文件名作为临时存储。如果 cgiFormFileName() 函数不成功,表示没有文件上传。
下一步,cgiFormFileSize() 函数被调用来检测上传文件的大小,单位字节。
这个函数接着查询上传文件的内容类型。用户上传的文件有他们自己的内容类型信息,这可以用来检测一个文件是图片,HTML 文件档,word 文档,还是其他的文件类型。
然而,就像文件名一样,任何其他的由浏览器所做的声明信息,都不能盲目的相信。浏览器可能上传一个名叫picture.jpg,类型是 image/jpeg 的文件,但是这不保证实际文件包含一个合法的能显示的JPEG 图片。
浏览器提交的内容类型可以用 cgiFormFileContentType() 函数来查询。cigc 提供访问已上传文件的方法。首先程序传递一个 cgiFilePtr 对象的地址以调用 cgiFormFileOpen,如果函数调用成功,
这个 cgiFilePtr 对象变成可用,可以被接下来的 cgiFormFileRead 使用。注意,实际读取的字节数可能少于请求的字节数, 特别是在 cgiFormFileRead 返回 cgiFormEOF 之前的最后一次成功调用的时候。当 cgiFormFileRead 不再返回 cgiFormSuccess,
程序调用 cgiFOrmFileClose 释放 cgiFilePtr 对象。
上传的文件可能包含任何的数据,包括二进制数据,null 字符,等等。这个例子使用cgiHtmlEscapeData 函数输出带有 HTML 特殊字符的数据。大多数程序会保存上传信息到服务端文件或者数据库。

14. 提取所有表单条目

有时,程序员预先不知道所有的表单字段。这种情况下,使用 cgiFormEntries 函数是方便的。cgitest.c 中的 Entries 函数 ,演示了 cgiFormEntries 的使用:
cgiFormEntries 函数获取表单字段名数组。这个数组是字符串指针数组,null 指针标志列表的结尾。上面的代码演示了一种循环遍历返回的字符串的方法。注意,最后 cgiStringArrayFree 函数的调用,这是必需的用来释放存储字符串和字符串数组内存。

15. 取出 Cookies

cgictest.c 中 Cookies() 函数显示了跟随当前的表单一起提交的 cookies 和他们的值的列表。
非常重要:你的浏览器可能永远不提交 cookies,无论你往测试表单填入什么值。
许多浏览器被配置成不接收或者发送 cookies。有些配置成尽可能少的发送 cookies,只需满足能进入热门网站就行。 用户经常会拒绝你的 cookies,确保你的代码在这种情况下仍然可以工作。
上面的代码使用 cgiCookies 函数取出当前设置的 cookies 列表,这个列表是一个以 null 指针结尾的字符串数组。
cgiCookieString() 函数用来获取每个 cookie 相关的值。这个函数的功能与之前讨论的 cgiFormString 很像。

注意,设置 cookie 作为当前表单提交处理的一部分,不会立即出现在这个列表上,因为它还没有被浏览器发回。要使它出现在以后的提交中,这需要浏览器选择接受并重新发送回 cookie。

16. 显示一个提交给当前程序的表单

CGI 程序员经常需要显示 HTML 页面作为 CGI 程序输出的一部分,这些 HTML 页面经常包含表单,这些页面要能够提交字段回到发送他们的程序。服务器要易于配置,这可以通过方便的使用 cgiScriptName 环境变量来实现,像下面显示的那样。这是 cgictest.c 中 SHowForm 函数的源代码。
注意 FORM 标签中 enctype="multipart/form-data" 的使用。如果表单包含文件上传,这是绝对需要的,像上面的例子一样。
大多数浏览器,如果没有设置这个属性,甚至都不会尝试上传文件。

17. 检验 CGI 环境变量

CGI 标准说明一系列由服务器设置的环境变量。然而,当环境变量没有设置的时候,服务器无法预测变量的值是 null 还是指向空字符串。同时,为了允许程序员还原已保存的环境变量,cgic 库需要有一种方法隔离程序员和实际的环境变量。
cgic 总是使用环境变量的拷贝来代替 getevn() 函数获取环境变量的值,例如HTTP_USER_AGENT(使用的浏览器软件),这个拷贝是一个合法的 C 字符串(绝不会是 null,虽然他们可能指向一个空字符串)。例如,cgiUserAgent 变量包含浏览器软件的名字。cgiReferrer 变量代表引用的 URL。

18. cgic 应用程序如何产生图像

cgic 可以结合 gd 图形库,来快速的生成 GIF 图像。
下面的简短例子暗示了可能行:
注意这个程序需要链接 cgic.o 和 libgd.a。通常,这种类型的程序响应是一个 cgiPathInfo 值或者表单字段的集合,通过返回的 HTML 页面内含图片的引用,轮流显示来产生一张 GIF 图片。

19. 调试 CGI 程序,使用 capture

调试 CGI 程序是一项痛苦的工作,因为 CGI 程序运行在网页服务器创建的特殊环境下,这导致在调试器里运行他们是困难的。
然而,cgic 库提供了一种捕获当前有效的环境变量到文件的方法,同时提供了重新载入已保存环境变量的方法。
capture.c 程序可以用来捕获 CGI 环境变量。只要修改 capture.c 文件中的 cgiMain() 函数的第一行,使其保存 CGI 环境变量到你系统上的适当的文件中并且用 make capture 来编译它。然后放置 capture 程序到 cgi 目录下并且设置你要测试的表单动作或者其他的链接指向它。当表单提交或者其他的链接生效的时候,capture 程序会把那个时刻有效地 CGI 环境变量写到你代码中指定的文件中去。然后可以调用 cgiReadEnvironment() 函数从相同的文件中(你在 cigMain() 函数开始处设置的)还原捕获的环境变量。然后你可以在你选择的调试器中运行你的程序,并且他会像由真正的网页服务器启动的一样来执行,包括文件上传,cookies 和所有其他 cgic 范围内的现象。
重要:确认你指定的是绝对路径,因为 CGI 脚本运行的当前工作目录可能跟你想得不一样。
更重要:如果你使用 getenv() 函数代替 cgic 中环境变量的拷贝,当使用已保存的 CGI 环境变量运行的时候,你将不会取得你期望的值。所以,总是使用 cgic 变量代替 getenv() 函数。

20. cgic 函数手册

**(1)cgiFormResultType cgiFormString(char name, char result, int max)

cgiFormString 试图检索发送给指定字段的字符串。文本会被拷贝到指定的 result buffer 中,最多但不超过 max-1 个字节。 null 字符标记字符串的结束。不管浏览器提交的换行格式是什么,cgiFormString 总是把每一个换行符都编码成一个单一 的换行符(ascii 十进制 10)。
最终的字符串总是可能比调用 cgiFormStringSpaceNeeded 的稍微短一点,但是不可能更长。
成功取出字符串,返回 cgiFormSuccess;
获取字符串但是为了适应 buffer 而被截断,返回 cgiFormTruncated;
取回的字符串是空字符串,返回 cgiFormEmpty;
没有相应的输入字段被提交,返回 cgiFormNotFound,并且一个空字符串被拷贝到 result buffer。

**(2)cgiFormResultType cgiFormStringNoNewlines(char name, char result, int max)

这个函数几乎和 cgiFormString() 一样,除了出现在输入中的任何回车或者换行符将被去掉。建议使用这个函数在单行的文本输入字段,因为有一些浏览器会提交回车和换行符当他们不该这样的时候。

**(3)cgiFormResultType cgiFormStringSpaceNeeded(char name, int length)

cgiFormStringSpaceNeeded() 被用来确定输入文本缓冲区需要多少长度来接收指定输入字段的内容。这对于不管任意输入长度都分配足够内存的程序员来说是有用的。由 cgiFormString() 调用获取的字符串的实际长度可能稍微更短一点,但是不会更长。
如果成功,函数设置 * length,单位字节,包含 null 结束符,返回 cgiFormSuccess。
如果指定的字段没有提交,该函数设置 * length 的值为 1,返回 cgiFormNotFound。1 被用来确保空字符串的空间(null 结束符),如果 cgiFormString 被调用,则忽略这个返回值。

**(4)cgiFormResultType cgiFormStringMultiple(char *name, char *ptrToStringArray)

cgiFormStringMultiple 在表单中有好几个输入元素具有相同的名字的特例情况非常有用,不管因为什么,程序员不想使用下面提供的复选框,单选按钮和选项菜单等函数。
这偶尔是需要的,如果程序员事先不知道在一个表单中什么值可能出现在一个多选列表或者复选框组。结果是一个字符串数组,以 null 标记结尾。这个数组由 cgic 库分配空间。
重要:完成这个数组的工作后,必须调用 cgiStringArrayFree() 来释放数组。
如果这个名字至少出现一次,返回 cgiFormSuccess。
如果没有出现,返回 cgiFormNotFound。
如果没有足够的空间分配数组保存返回值,返回 cgiFormMemory。
除了最后一种情况,ptrToStringArray 指向一个以 null 指针结尾的合法数组。在内存不足的情况下,ptrToStringArray 被设置成 null 指针。

**(5)cgiFormResultType cgiFormEntries(char *ptrToStringArray)

当程序员事先不知道所有相关的表单字段名称的时候 cgiFormEntries 是有用的。结果被设置成一个指向字符串的数组,最后一个值是 null。这个数组由 cgic 库分配。
重要:完成这个数组的工作后,必须调用 cgiStringArrayFree() 来释放数组。这个函数返回 cgiFormSuccess,除非发生了内存不足的错误事件。在成功的情况下,ptrToStringArray 设置成指向一个字符串的数组,null 指针结尾。在内存不足的情况ptrtoStringArray 设置成 null 指针,且返回 cgiFormOutOfMemory。

**(6)void cgiStringArrayFree(char stringArray)

cgiStringArrayFree() 用来释放由 cgiFormStringMultiple(), cgiFormEntries(), or cgiFormCookies() 函数创建的字符串数组。

(7)cgiFormResultType cgiFormInteger( char *name, int *result, int defaultV)

cgiFormInterger() 尝试获取发送给指定输入字段的整型值。result 设置为被提交的值。
成功获取值,返回 cgiFormSuccess;
如果值是空字符串,返回 cgiFormEmpty;
如果提交的不是整型值,返回 cgiFormBadType;
如果没有该输入字段,返回 cgiFormNotFound 。
在后三种情况,
result 设置为指定的默认值。

**(8)cgiFormResultType cgiFormIntegerBounded(char name, int result, int min, int max, int defaultV)

cgiFormIntegerBounded() 尝试获取发送给指定输入字段的整型值,并且限定结果在指定的区间内。result 被设置为提交的值。
如果成功获取到值,返回 cgiFormSuccess;
如果值越界且被相应的调整,返回 cgiFormConstrained;
如果提交的是一个空字符串,返回 cgiFormEmpty;
如果提交的不是整型值,返回 cgiFormBadType;
如果没有该输入字段,返回 cgiFormNotFound。
在后三种情况,
result 设置为指定的默认值。

**(9)cgiFormResultType cgiFormDouble(char name, double result, double defaultV)

cgiFormDouble 尝试获取发送给指定输入字段的浮点值,result 设置为被提交的值。
成功获取值,返回 cgiFormSuccess;
如果值是空字符串,返回 cgiFormEmpty;
如果提交的不是数字,返回 cgiFormBadType;
如果没有该输入字段,返回 cgiFormNotFound。
在后三种情况,
result 设置为指定的默认值。

**(10)cgiFormResultType cgiFormDoubleBounded(char name, double result, double min, double max, double defaultV)

cgiFormDoubleBounded() 尝试获取发送给指定输入字段的浮点值,并且限定结果在指定的区间内。result 被设置为提交的值。
如果成功获取到值,返回 cgiFormSuccess;
如果值越界且被相应的调整,返回 cgiFormConstrained;
如果提交的是一个空字符串,返回 cgiFormEmpty;
如果提交的不是数字,返回 cgiFormBadType;
如果没有该输入字段,返回 cgiFormNotFound 。
在后三种情况,
result 设置为指定的默认值。

**(11)cgiFormResultType cgiFormSelectSingle(char *name, char *choicesText, int choicesTotal, int result, int defaultV)

cgiFormSelectSingle() 获取与 < SELECT> 元素相关的选项号码,这个元素不允许多选。name 用来标识 < SELECT > 元素的 NAME 属性,
choicesText 指向标识每个选项的字符串数组;choicesTotal 表示选项的个数。如果有被选中的选项,*result 设置为实际选中的选项
在选项数组中的位置。如果没有选择被提交或者是一个非法选项,那么就设置为默认值。
成功获取值,返回 cgiFormSuccess。
如果没有选项被提交,返回 cgiFormNotFound。
如果提交的选项在 choicesText 中没有匹配的选项,返回 cgiFormNoSuchChoice。

**(12)cgiFormResultType cgiFormSelectMultiple(char *name, char **choicesText, int choicesTotal, int result, int invalid)

cgiFormSelectMultiple() 获取与 < SELECT> 元素相关的选项号码,这个元素允许多选。name 用来标识 < SELECT > 元素的 NAME 属性,
choicesText 指向标识每个选项的字符串数组,choicesTotal 表示选项的个数。result 是个数和 choicesText 数组个数相同的
整数数组。对于每一个选项,选中设为 1,否则为 0.
如果至少有一个合法选项被成功获取,返回 cgiFormSuccess;
如果没有合法的选项被提交,返回 cgiFormNotFound。
*invalid 设置为被提交的非法选项的个数,这个值应该为 0,除非表单和选项数组不一致。

*(13)cgiFormResultType cgiFormSubmitClicked(char name)

通常需要知道一个指定的提交按钮是否被点击,不同的名字属性区分不同的提交按钮。cgiFormSubmitClicked 函数可替换为cgiFormCheckboxSingle 函数,它适合用来测试一个特定的提交按钮是否被使用。

*(14)cgiFormResultType cgiFormCheckboxSingle(char name)

cgiFormCheckboxSingle 用来确认一个指定名字的复选框是否被选中。
如果这个按钮被选中,返回 cgiFormSuccess;
如果没有被选中,返回 cgiFormNotFound。
cgiFormCheckboxSingle 用于具有唯一名字的单复选框。下面的函数用于解决具有相同名字的多复选框和单选按钮。

**(15)cgiFormResultType cgiFormCheckboxMultiple(char *name, char **valuesText, int valuesTotal, int result, int invalid)

cgiFormCheckboxMultiple() 检测在一个具有相同名字的复选框组里面哪一个被选中。这跟单选按钮是不同的。valuesText 指向标识每个复选框的 VALUE 属性的字符串数组。ValuesTotal 是复选框的总数。result 是个数和 valuessText 数组个数相同的整数数组。对每一个选项,选中设为 1,否则为 0。
如果至少有一个合法复选框被选中,返回 cgiFormSuccess。
如果没有合法的复选框被选中,返回 cgiFormNotFound。
*invalid 设置为被提交的非法选项的个数,这个值应该为 0,除非表单和 valuesText 数组不一致。

**(16)cgiFormResultType cgiFormRadio(char *name, char *valuesText, int valuesTotal, int result, int defaultV)

如果有做选择的话,cgiFormRadio 用于检测,带有相同名字的 radio boxes 组中的哪一个被选中。valuesText 指向标识每个radio box 的 VALUE 属性的字符串数组,ValuesTotal 是 radio boxes 组中的 radio box 个数。
如果有做选择的话,*result 设为 valuesText 数组中被选中的位置。
如果没有 radio box 被选中或者是一个非法的选项,那么设为默认值。
如果组里面有一个 radio box 被选中,返回 cgiFormSuccess。
如果没有 box 被选中,返回 cgiFormNotFound。
如果被提交的 radio box 不匹配 valuesText 中的任何一个,返回 cgiFormNoSuchChoice。

**(17)cgiFormResultType cgiFormFileName(char name, char fileName, int max)

cgiFormFileName 尝试根据指定的带有 file 类型的表单输入字段,取出用户上传文件的名字。
如果在服务端直接使用,永远不要相信这个文件名是合理和安全的。
文本会被拷贝到 fileName buffer 中,最多但不超过 max-1 个字节,null 结尾。
成功取出字符串并且不为空,返回 cgiFormSuccess;
字符串被成功取回但为空,表示没有上传文件,返回 cgiFormNoFileName。
获取字符串但是为了适应 buffer 而被截断,返回 cgiFormTruncated;
如果没有相应的输入字段被提交,返回 cgiFormNotFound,并且一个空字符串被拷贝到结果。

**(18)cgiFormResultType cgiFormFileSize(char name, int sizeP)

cgiFormFileSize 尝试根据 name 参数指定的具有 file 类型的输入字段,获取浏览器上传文件的大小,单位字节。
如果成功,大小存在 * sizeP,并且函数返回 cgiFormSuccess。
如果这个表单字段不存在,函数返回 cgiFormNotFound。
如果表单字段存在但没有文件上传,返回 cgiFormNotAFile。

**(19)cgiFormResultType cgiFormFileContentType(char name, char contentType, int max)

cgiFormFileContentType 尝试根据 name 参数指定的具有 file 类型的输入字段,获取浏览器上传文件的内容类型。不保证这个
内容类型是精确的。类型字符串拷贝到 contentType buffer 中,最多但不超过 max-1 个字节,null 结尾。
成功取出字符串并且不为空,返回 cgiFormSuccess;
字符串被成功取回但为空,表示没有上传文件,或者浏览器不认识内容类型,返回cgiFormNoContentType。获取字符串但是为了适应 buffer 而被截断,返回 cgiFormTruncated;
如果没有相应的输入字段被提交,返回 cgiFormNotFound,并且一个空字符串被拷贝到结果。

**(20)cgiFormResultType cgiFormFileOpen(char name, cgiFilePtr cfpp)

cgiFormFileOpen 尝试根据指定的带有 file 类型的输入字段打开实际上传的文件。
如果成功,这个函数设置 * cfpp 为一个有效地 cgiFilePtr 对象,返回 cgiFormSuccess;
如果失败,设置 * cfpp 为一个 null 指针,返回 cgiFormNotFound,cgiFormNotAFile, cgiFormMemory or cgiFormIO 中的合适值。

**(21)cgiFormResultType cgiFormFileRead(cgiFilePtr cfp, char buffer, int bufferSize, int gotP)

cgiFormFileRead 尝试从之前由 cgiFormFileOpen 产生的 cgiFilePtr 对象读取最多 bufferSize 字节数据。如果成功的读取到了数据,
拷贝到 buffer,实际读取字节数保存到 * gotP。
如果有任何数据被成功读取,返回 cgiFormSuccess。
到达文件尾,函数返回 cgiFormEOF。
如果发生 I/O 错误事件,返回 cgiFormIO。如果 cfg 是空指针,函数返回 cgiFormOpenFailed。

(22)cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)

cgiFormFileClose 关闭一个 cgiFilePtr 对象,释放内存和其他系统资源。
这个函数返回 cgiFormSuccess 除非 cfg 是空指针。在 cfg 为 null 的情况下,返回 cgiFormOpenFailed。

*(23)void cgiHeaderLocation(char redirectUrl)

如果程序员想要重定向用户到不同的 URL,可以调用 cgiHeaderLocation(),在这种情况,没有额外的输出。
如果你想设置 cookies,你必须在调用 cgiHeaderLocation 之前调用 cgiHeaderCookieSetString() 和 cgiHeaderCookieSetInteger()

*(24)void cgiHeaderStatus(int status, char statusMessage)

如果程序员想要输出一个 HTTP 错误状态码而不是一个文档,应该调用 cgiHeaderStatus()。第一个参数是状态码,第二个参数是状态信息用于显示给用户。如果你想设置 cookies,你必须在调用 cgiHeaderStatus 之前调用 cgiHeaderCookieSetString() 和 cgiHeaderCookieSetInteger()

*(25)void cgiHeaderContentType(char mimeType)

如果程序员希望输出一个新的文档作为用户请求的响应,需要调用 cgiHeaderContentType()。这是普通情况。 这单独的参数是响应的 MIME 文档类型。典型值是 “text/html” 对应于 HTML 文档,“text/plain”对应于普通的 ASCII 文本没有HTML 标签,“image/gif” 对应于 GIF 图像,“audio/basic” 对应于音频格式。
如果你想设置 cookies,你必须在调用 cgiHeaderContentType() 之前调用cgiHeaderCookieSetString() 和 cgiHeaderCookieSetInteger()

**(26)void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive, char path, char domain)

如果程序员希望存储一些信息在用户的客户端,以便于后续访问相关网站时这些信息再次传回服务器,就应该调用cgiHeaderCookieSetString()。第一个参数是 cookie 的名字,为了在所有的浏览器都有好的结果,使用一个没有空格 和特殊标点符号的短名字。再次强调,为了最好的结果,使用一个短字符串。建议 cookies 用来存储一个唯一的标识信息,这样在服务器端可以在数据库中查询更加详细的信息。试图在浏览器保存一些复杂的信息更可能失败。第三个参数是cookie 在浏览器端的保存时间,单位秒。86400 是一天,365*86400 大概是一年。第四个参数是这个网站的部分 URL,在这里面 cookie 才是有意义的。如果想要每次访问整个网站,cookie 都被发送回服务器,设置这个参数为 /。最后的参数是网站的名字或者是整个域,对于它们来说,cookie 应该被提交。如果你选择对于整个域,cookie 都要被传回,这个参数 必须以一个 dot(.)开始,例如. boutell.com。cgic 变量 cgiServerName 对于第四和第五个参数是很方便的值。
参考cgiHeaderCookieSetInteger, cgiCookieString, cgiCookieInteger and cgiCookies.

**(27)void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive, char path, char domain)

cgiHeaderCookieSetInteger 和 cgiHeaderCookieSetString 是一样的,除了值是一个整数而不是字符串。参考cgiHeaderCookieSetString 获取更完整的信息。

**(28)cgiFormResultType cgiCookieString(char name, char result, int max)

cgiCookieString 尝试获取发送给指定 cookie 的值(浏览器端持续存储的),该文本被拷贝到 result buffer。最多但不多于 max-1 个字节,null 结尾。
成功获取到字符串值,返回 cgiFormSuccess。
成功获取到值,但是为了适应 buffer 被截断,返回 cgiFormTruncated;
字符串被取出但为空,返回 cgiFormEmpty。
如果这样的 cookie 没有提交,返回 cgiFromNotFound。在最后一种情况,一个空字符串拷贝到 result。

**(29)cgiFormResultType cgiCookieInteger(char name, int result, int defaultV)

cgiCookieInteger() 尝试获取发送给指定 cookie 的整型值(浏览器端持续存储的),result 设为被提交的值。
成功获取值,返回 cgiFormSuccess;
如果值是空字符串,返回 cgiFormEmpty;
如果提交的不是整型值,返回 cgiFormBadType;
如果没有该输入字段,返回 cgiFormNotFound。
在后三种情况,
result 设置为指定的默认值

**(30)cgiFormResultType cgiCookies(char *name, char *ptrToStringArray)

当程序员事先不知道所有相关的 cookies 的时候,cgiCookies 是有用的(浏览器端持续存在的字符串)。
结果指向的值是一个字符串数组的指针,最后一个值是 null 指针。这个数组由 cgic 库分配。
重要:当完成了该数组的工作后,你必须调用 cgiStringArrayFree() 释放该数组指针指向的内存。
函数返回 cgiFormSuccess,除非发生了内存不足的错误事件。
如果成功,ptrToStringArray 设为指向一个合法字符串数组的指针,最后一个是 null。
在 out-of-memory 的情况下,ptrToStringArray 设为 null pointer,
且返回 cgiFormOutOfMemory。

*(31)cgiFormResultType cgiHtmlEscape(char s)

cgiHtmlEscape 输出一个 null 结尾的字符串到 cgiOut,转换任何 ‘<, &,>‘ 为正确的符号(<,>,&),以便于他们不会妨碍 HTML 标记。
返回 cgiFormSuccess 或者如果发生了 I/O 错误事件,返回 cgiFormIO。

*(32)cgiFormResultType cgiHtmlEscapeData(char data, int len)

cgiHtmlEscapeData 和 cgiHtmlEscape 是一样的,除了数据不是 null 结尾的。这个版本的函数输出 len 长度字节数。

*(33)cgiFormResultType cgiValueEscape(char s)

cgiValueEscape() 输出一个 null 结尾的字符串到 cgiOut,转换 引号(") 字符到正确的符号以便于不妨碍 HTML 属性值的引用标记。当输出一个字符串作为一个输入标签的 value 属性的一部分的时候,或者一个 link 和表单标签的 href 属性的时候它是有用的,函数返回 cgiFormSuccess,或者 cgiFormIO,如果发生 I/O 错误事件。

*(34)cgiFormResultType cgiValueEscapeData(char data, int len)

cgiValueEscapeData 和 cgiValueEscape 是一样的,除了数据不是 null 结尾的。这个版本的函数输出 len 长度字节数。

*(35)cgiEnvironmentResultType cgiWriteEnvironment(char filename)

cgiWriteEnvironment() 被用来写所有的 CGI 环境变量包括表单数据,到指定的输出文件。然后 cgiReadEnvironment() 用来从指定的输入文件还原环境变量用于调试。当然,只有你使用 cgic 拷贝 CGI 环境变量并且使用 cgiIn 和 cgiOut 而不是stdin 和 stdout,这些才会如你所期望的那样运行。这些函数用来捕获服务器运行期间真实的 CGI 状态,然后在调试环境下创建他们。
如果成功的话这两个函数都返回 cgiEnvironmentSuccess;
如果发生 IO 错误返回 cgiEnvironmentIO;
如果发生了内存不足的错误返回 cgiEnvironmentMemory。

*(36)cgiEnvironmentResultType cgiReadEnvironment(char filename)

参考 cgiWriteEnvironment。

(37)int cgiMain()

程序员必须实现这个函数,这个程序完成一个唯一的任务并且由 cgic 库中的真正的 main() 函数调用。cgiMain 的返回值就是程序的返回值。在这个函数里面会调用很多 cgiForm 函数。参考” 如何写一个 cgic 应用程序 “查看细节。

21. cgic 变量手册

这部分提供程序员可以利用的 cgic 库提供的全局变量的使用手册。为了 cgic 库的 CGI 调试功能的
可移植性,应该使用这些变量代替 stdin,stdout,getevn()。
这些变量的大部分与 CGI 环境变量相同。最大的不同是 cgic 环境字符串绝不会是空指针。如果没有内容,用空字符串代替。

*(1)char cgiServerSoftware

指向服务器软件名,如果不知道,指向一个空字符串。

*(2)char cgiServerName

指向服务器名字,如果不知道,指向一个空字符串。

*(3)char cgiGatewayInterface

指向网关接口名字(通常是 CGI/1.1),如果不知道,指向一个空字符串。

*(4)char cgiServerProtocol

指向使用的协议(通常是 HTTP/1.0),如果不知道,指向一个空字符串。

*(5)char cgiServerPort

指向服务器监听 HTTP 连接的端口号(通常是 80),如果不知道,指向一个空字符串。

*(6)char cgiRequestMethod

指向请求使用的方法(通常是 GET 或 POST),如果不知道,指向一个空字符串(不该发生)。

*(7)char cgiPathInfo

大部分的网页服务器能够识别出请求 URL 中在 CGI 程序名前的附加路径信息,并且传递这些信息
到 CGI 程序中。cgiPathInfo 指向附加路径信息。

*(8)char cgiPathTranslated

大部分的网页服务器能够识别出请求 URL 中在 CGI 程序名前的附加路径信息,并且传递这些信息
到 CGI 程序中。cgiPathTranslated 指向附加路劲信息,这些信息被本地服务器转换成文件系统路径。

*(9)char cgiScriptName

指向被调用的脚本程序名字。

*(10)char cgiQueryString

包含用户使用 GET 方法或者一个



标签提交的查询信息。没有必要直接去解析这个信息,除非使用的是 < ISINDEX > 标签。
通常,cgic 库会自动的解析,只需调用 cgiForm 家族的函数来获取相关表单输入字段的值就可以了。

*(11)char cgiRemoteHost

指向浏览器的完全解析主机名,如果不知道,指向一个空字符串。

*(12)char cgiRemoteAddr

指向浏览器的点分十进制 ip 地址,如果不知道,指向一个空字符串。

*(13)char cgiAuthType

如果有的话,指向请求使用的身份验证类型,如果不知道,指向一个空字符串。

*(14)char cgiRemoteUser

指向已经通过身份验证的用户名。如果没有经过身份验证,为空字符串。确切的信息取决于使用的身份验证类型。

*(15)char cgiRemoteIdent

指向用户自愿给出的使用用户鉴别协议的用户名;如果不知道,为空字符串。这个信息不是安全的。鉴别进程可能被用户安装在不安全的系统,例如 windows。

*(16)char cgiContentType

指向用户提交的信息的 MIME 类型;如果没有消息被提交,为空字符串。如果这个字符串是 application/x-www-form-urlencoded 或者
multipart/form-data,cgic 库会自动的检查提交的表单数据。如果这个字符串是其他的非空字符串,代表被提交的数据是其他类型。
目前这很少见,因为大部分浏览器仅仅提交表单和上传文件,这些 cgic 可以直接解析。

*(17)char cgiCookie

指向网页浏览器提交的原生 cookie 数据(浏览器端持续存储)。程序员应该使用 cgiCookies,cgiCookieString,和 cgiCookieInteger 来检测数据。

*(18)char cgiAccept

指向浏览器可接受的以空格分隔的 MIME 内容类型列表,或者是一个空字符串。不幸的是,现在大多数的浏览器不在表单提供这个变量。

*(19)char cgiUserAgent

指向使用的浏览器的名字,如果这些信息不可用,指向一个空字符串。

*(20)char cgiReferrer

指向用户前一个访问页面的 URL。这经常是表单的 URL,把用户带到你的程序。报告这个信息完全取决于浏览器,它可能选择不这样做,或者选择不诚实的做。然而,这个变量通常是准确的。经常使用的另外一个拼写 cgiReferer 作为一个宏提供。

(21)int cgiContentLength

接收的表单的或者查询数据的字节数。注意,如果提交的是一个表单或者查询信息,cigc 库会直接从 cgiIn 或者 cgiQueryStirng 读取并解析这些信息。
程序员不该这样做,并且这种情况下 cgiIn 会指向文件的末尾。

*(22)FILE cgiOut

指向 CGI 输出。cgiHeader 函数,例如 cgiHeaderContentType,要首先被调用来输出 mime 头。接着,HTML 页面,GIF 图像或者其他的网页文档被输出到 cgiOut, 使用标准 C I/O 函数,如 fprintf 和 fwrite。cgiOut 通常等价于 stdout。然而,考虑到对未来 cgic 的版本在特殊环境下的兼容性,建议使用 cgiOut。

*(23)FILE cgiIn

在大多数情况下,cgic 函数被设计成提供合理的结果,即使浏览器或者用户做了不合理的事情。然而,有时候精确的知道发生了什么不合理的事情是重要的,特别是当赋予一个默认值或者限制一个值不是很好的解决办法的时候。下面的返回码用于这种情况是有效的。

22. cgic 返回码手册

(1)cgiFormSuccess

表示函数成功执行了最少一个动作(或者在适用的情况下表示获取到至少一个值)

(2)cgiFormTruncated

表示截断了一个取出的字符串,以避免缓冲区越界。

(3)cgiFormBadType

表示用户提交的数字型值实际上不是合法值。

(4)cgiFormEmpty

表示一个取出的字段值没有数据。

(5)cgiFormNotFound

表示一个指定的字段没有被提交

(6)cgiFormConstrained

表示一个数字型值超过了指定的区间,被强制转换成上边界或者下边界。

(7)cgiFormNoSuchChoice

表示用户提交的一个单选字段(如单选按钮组)的值不是可接受的值,这通常表示在表单和程序之间有矛盾。

(8)cgiFormEOF

表示 cgiFormFileRead 函数调用的时候,cgiFilePtr 对象已经指向上传文件数据的末尾。

(9)cgiFormIO

当 cgiFormFileRead 读取上传文件数据遇到一个 I/O 错误时返回这个错误。

(10)cgiFormNotAFile

试图使用一个文件相关的函数操作一个非上传文件表单字段时,返回这个错误作为响应。

(11)cgiFormNoContentType

试图获取上传文件的内容类型,浏览器没有指定内容类型时,返回这个错误作为响应。

(12)cgiFormNoFileName

试图获取上传文件的名字,浏览器没有指定名字时,返回这个错误作为响应

(13)cgiFormOpenFailed

试图从一个空的 cgiFilePtr 对象读数据,通常是程序员检测 cgiFormFileOpen 的调用失败。

(14)cgiEnvironmentMemory

表示试图从 / 向一个捕获文件读 / 写 CGI 环境变量时,发生了内存不足的错误。

(15)cgiEnvironmentSuccess

表示试图从 / 向一个捕获文件读 / 写 CGI 环境变量成功。

(16)cgiEnvironmentIO

表示试图从 / 向一个捕获文件读 / 写 CGI 环境变量时,发生了 I/O 的错误。

(17)cgiEnvironmentWrongVersion

表示试图读取由 2.0 以前的 CGIC 版本保存的用于调试的 CGI 环境变量。(版本不对)

posted @ 2022-08-04 10:56  不明白就去明白  阅读(963)  评论(0编辑  收藏  举报