用户操作
[即时聊天] [发私信] [加为好友]
heiyelurenID:heiyeshuwu
893640次访问,排名39好友93人,关注者147
既然决定远行,便只顾风雨兼程。
heiyeshuwu的文章
原创 282 篇
翻译 3 篇
转载 210 篇
评论 517 篇
heiyeluren的公告

联系方式:


访问统计: free hit counter code
FeedSky订阅:
FeedSky订阅
最近评论
heiyeluren:注册发帖后才能下载。。。。不知道论坛咋设置。。。 :-)
kong:注册了也不能下载.....提示您的操作将会导致您的 金钱 低于系统规定的下限值 0
赵舜尧:感谢博主的分享,期待你的“下章接着讲述数据分割和散列方面的内容”
adobe cs4:呃,LAMP经典应用……
俺的
Adobe cs4也是
bluehouse1985:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
文章分类
收藏
    相册
    技术图片
    搜索引擎
    ::eYou::
    kevin world
    lewis - 老吕
    qyb - BT的花
    Realzay的blog
    叶金荣
    天堂地狱鬼-dulao5's Blog
    沙漠之周
    狐狸糊涂
    老韩
    與子觀化
    ::Yahoo::
    glemir’s blog
    happy_fish - 分布式文件系统FastDFS
    LinZi's Blog
    Rainx
    stauren
    互联网,请记住我 - 162同学的技术博客
    冰的河
    小蚂蚁同学滴测试博客
    张彪同学
    随网之舞 - kaven的DHTML博客
    风雪之隅
    ::朋友::
    【推荐】中文分类网
    DDR的博客
    kevin world
    miky
    PHPCup.cn论坛
    俺兄弟的blog
    冰河的技术博客:心随风动
    好旅网
    小少的技术博客
    无尘居
    晋陵路人的Blog
    李天华同学滴技术博客
    沙狐部落
    轻量级的editor
    ::网友::
    blankyao同学
    Code & Stock.
    LionD8的Blog
    MooPHP - 轻量级PHP框架
    Phzzy
    张贺同学的博客
    技术大牛老余的博客
    抚琴居
    旋木木同学滴博客
    矛盾网
    程序人生
    邢红瑞的blog
    阿健的博客
    :PHP博客:
    .: Easy style :.
    [琴剑楼]
    CoolCode.cn
    Haohappy的Blog
    Hightman
    iwind的blog
    Javascript开发站
    JD Space
    Nio's Weblog
    Open Source PHP
    PHP面对对象
    SourceForge.net
    trip的专栏
    UGIA.cn
    windix's blog
    Windix's Weblog
    一个藏袍
    俊麟 Michael`s blog
    偶然的blog
    刘敏的blog
    大龄青年的Blog
    廖宇雷的blog
    懒猫开始新生活blog
    某人的栖息地
    王春生的博客
    神仙
    :牛人blog:
    DBA notes
    http://blog.csdn.net/tingya/
    侯捷网站
    孟岩
    搜索引擎研究
    方舟
    王咏刚的BLOG
    竹笋炒肉
    荣耀
    车东[Blog^2]
    透明思考
    陈硕的Blog
    DHTML
    DHTMLGoodies
    FCKEditor
    Google Code
    Google Web Toolkit
    HTML Goodies
    HTML.it
    HTMLAre
    HTMLdog
    JavaScript Kit
    jQuery
    KindEditor
    Prototype
    TinyMCE
    W3 Schools
    Yahoo JavaScript Developer Center
    Yahoo! Developer Network
    Yahoo! UI Library (YUI)
    网页设计师Web标准
    Java国内站
    ChinaJavaWorld.com技术论坛
    IBM developerWorks 中国: Java
    Java中文站
    Java开源大全
    Java爱好者
    JR - Java翻译站
    J道-JDON
    Matrix: 与Java共舞
    中国Java开发网
    中文java技术网
    PHP国内站点
    CSDN PHP论坛
    Discuz!
    FleaPHP
    Google--PHP用户组
    IBM DeveloperWorks
    JavsScript技术讨论
    Nirvana Studio
    OpenPHP.cn
    PHPChina
    TiM Club
    中文 PFC 1.0 手册--PHP5的开发包
    中文 PFC 1.0 手册--PHP5的开发包
    中文PHP网
    太平洋--PHP开发区
    爱MySQL
    超越PHP
    PHP国外站点
    ADOdb
    Agavi Framework
    Cake PHP
    MySQL Performance Blog
    MySQL Performance Blog
    Nonaweb
    PEAR
    PECL
    PECL Windows
    PHP Builder
    PHP Classes
    PHP Classes
    PHP New Download
    PHP Security Consortium
    php.MVC
    php.MVC
    PHPkitchen(OO & MVC)
    phpPatterns
    PHP国外图书下载
    smart template
    Smarty
    SourceForge.net
    Symfony Framework
    Zend
    Zend Framework
    Unix C/C++
    Free Gentux
    周立发的blog(Linux C)
    Unix/Linux
    BSD智库
    ChinaUnix
    FreeBSDChina
    FreeLAMP
    IBM开发者Linux专区
    Linux Byte
    LinuxKit
    LinuxTS
    Linux伊甸园
    Linux技术中坚站
    Linux非常空间
    Love Unix
    NetBSD&OpenBSD中文用户组
    NetBSD中国社区
    Oracle中国用户讨论组
    OurLinux
    Unix中文
    Unix中文
    Unix中文宝库
    中国Linux公社
    中国Unix用户技术论坛
    中文FreeBSD用户组
    永远的Unix
    炎黄角马
    程序设计
    CSDN
    IBM开发者中心
    Microsoft TechNet: 主页
    MSDN 中文网站
    PHP中文站
    Sun技术社区
    中国IT认证实验室--企业应用技术
    中国协议分析网
    喜悦国际村
    太平洋电脑网---开发特区
    实用网站
    veBook(国外大量免费图书下载网站)
    Whois.net
    中国Web信息博物馆
    中国互联网络信息中心whois查询
    服务器系统信息查看
    网络安全
    AnySide.com
    CGI Secutiry
    K-OTik Security Monitoring
    Linux Security
    Packet Storm Security
    PHP Secure
    RFC中文文档索引
    Safemode.org
    SecuriTeam.com
    Security Corporation
    SecurityFocus
    SecurityTracker
    Zone-h (区域黑客,每天公布各国被黑的网站)
    中华安全网
    中国信息安全组织
    国家计算机网络应急处理中心
    安全天使
    安全焦点
    幻影旅团
    绿盟科技
    网络安全评估中心(cnns )
    在线手册
    Apache2.0中文文档
    Beyond Linux From Scratch
    Debian参考手册
    FreeBSD Porter 手册
    FreeBSD使用手册
    Linux C函数中文参考手册
    MySQL 4.1.0 中文参考手册
    NetBSD在线手册
    OpenBSD在线FAQ
    PHP ADODB 1.99版手册中文翻译(Tripc)
    PHP中文手册(国内)
    PHP中文手册(国外)
    PostgreSQL中文文档
    Red Hat Linux 9入门指南
    Red Hat Linux 9安装指南
    Red Hat Linux 9定制手册
    中国OSS技术手册中心
    技术文档手册中心-ChinaUnix
    存档
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    转载 [转]w3c-libwww入门教程收藏

    新一篇: [转]DB2,PostgreSQL & MySQL体系结构概述和对比 | 旧一篇: HTTP/FTP客户端开发库:libwww、libcurl、libfetch 以及更多

     

    【libwww介绍】

    官方网站:http://www.w3.org/Library/
    更多信息:http://www.w3.org/Library/User/
    运行平台:Unix/Linux,Windows
    以下资料来源:http://zh.wikipedia.org/wiki/Libwww

    简介:
    Libwww 是一个高度模组化用户端的网页存取API ,用C语言写成,可在 Unix 和 Windows 上运行。 It can be used for both large and small applications including: browsers/editors, robots and batch tools. There are pluggable modules provided with Libwww which include complete HTTP/1.1 with caching, pipelining, POST, Digest Authentication, deflate, etc. The purpose of libwww is to serve as a testbed for protocol experiments. 蒂姆·伯纳斯-李 在 1992 年十一月创造出了 Libwww,用於展示网际网路的潜能。使用 Libwww 的应用程式,如被广泛使用的命令列文字浏览器 Lynx 及 Mosaic web browser 即是用 Libwww 所写成的。 Libwww 目前为一开放原始码程式,并於日前移至 W3C 管理。基於其为开放原始码的特性,任何人都能为 Libwww 付出一点心力,这也确保了 Libwww 能一直进步,成为更有用的软体。

     

    以下文章来源:
    http://bbs.nju.edu.cn/bbsanc?path=/groups/GROUP_3/CPlusPlus/D74D7D299/M.1090339010.A


    w3c-libwww入门教程
    丁建华 <jhding@tom.com> 2004.7.20

        如果你 google 一下 libwww, 会发现三个不同的软件包: w3c-libwww,
    perl-libwww, glibwww. 它们都是用来处理与 www 有关的各种协议的, 比如
    http, ftp, news, gopher 等等. 其中 perl-libwww 是用于 perl 语言环境的,
    glibwww 是 gnome 对 w3c-libwww 的一个简单的包装. w3c-libwww 是最古老的,
    而且也可以说是最权威的, (假如存在所谓的权威的话 :)) 因为 w3c-libwww
    的开发目标就是为 w3.org 发布的各种协议提供一个测试平台, 以评估各种
    协议的可行性, 合理性, 兼容性, 可扩充性, 效率与安全等各方面的情况的.
    因此其代码绝对值得一读. 但是初学者常常觉得这个函数库太庞杂, 抓任何一点
    都会带出一大片, 找不到重点, 理不清头绪. 笔者希望本文能够对他们有所帮助.

    ===================================================================
    ??? w3c-libwww 是什么?
    >>> Please visit http://www.w3.org/Library/

    ===================================================================
    ??? 如何搭建一个 w3c-libwww 的调试平台?
    >>> 最好在 Linux 系统上, 把软件包解压, 然后
    <cmd> ./configure --enable-shared=no --prefix=/home/me --with-ssl --with-zlib
    --with-regex </cmd>
    <cmd> make </cmd>
    <cmd> make install </cmd>
    这样会建立 /home/me/bin, /home/me/lib, /home/me/include 等目录. 你需要把
    /home/me/bin 加入你的搜索路径, 以便系统能够找到 libwww-config. 这个配置
    程序可能需要手工修改, 好在它非常简单, 对任何想学习 w3c-libwww 的人来说,
    改这个 sh 程序不成问题.

    ===================================================================
    ??? 如何编译一个程序呢?
    >>> 假如你的程序是 prog.c, 你可以这样编译:
    <cmd> gcc -g -Wall -c -o prog.o `libwww-config --cflags` prog.c </cmd>
    <cmd> gcc -g -o prog prog.o `libwww-config --libs` </cmd>
    这样编译出来的程序是包含调试信息的 static 连接, 你可以对 w3c-libwww 的
    库函数源代码进行调试.

    ===================================================================
    ??? 我能在 Win32 上建立这样的环境吗?
    >>> 如果你有足够的钱购买 M$ 的开发工具, 我建议你关掉电脑出去好好享受生活.
    如果你只能安装免费的 Cygwin, 那么恐怕不能对 w3c-libwww 的源代码进行调试了.
    不过你还是可以用 dll 来做开发: 先用与 Linux 同样的方法在 Cygwin 上装一遍,
    尽管这样装出来的 static 库问题很多, 几乎无法使用, 但是我们还是得到了
    必要的头文件. 然后我们到源代码的 Library/src/windows 目录下面, 用
    <cmd> dlltool -D wwwapp.dll -d wwwapp.def -l libwwwapp.dll.a </cmd>
    生成所有必要 dll 的 import 库, (有几个 export 符号重复, 发现后注释掉.)
    复制到目标 lib 下就可以了. 当然还要修改 libwww-config, 把所有的 wwwapp
    之类的名字都改成 wwwapp.dll 这样的, 也就是把 static 连接都改成 shared
    连接. 就可以了.

    ===================================================================
    ??? 那些 wwwapp.dll 在哪里啊?
    >>> 从 http://www.w3.org 下载 w3c-libwww 的 Win32 发行包并安装. 然后把
    其中的 dll 复制到你的某个搜索路径下面. 我就放在 gtk\2.0\bin 下面.

    ===================================================================
    ??? gtk+ 有一个 pkg-config 可以拿来用吗?
    >>> 可以. 把下面这个文件放到 /lib/pkgconfig 下面就行了.
    <file "/lib/pkgconfig/w3c-libwww.pc">
    prefix=/usr/local
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    target=win32

    Name: w3c-libwww
    Description: w3c libwww (${target} target)
    Version: 5.4.0
    Requires:
    Libs: -L${libdir} -lwwwxml.dll -lxmltok.dll -lxmlparse.dll -lwwwzip.dll -lwww
    init.dll -lwwwapp.dll -lwwwhtml.dll -lwwwtelnt.dll -lwwwnews.dll -lwwwhttp.dl
    l -lwwwmime.dll -lwwwgophe.dll -lwwwftp.dll -lwwwfile.dll -lwwwdir.dll -lwwwc
    ache.dll
    -lwwwstream.dll -lwwwmux -lwwwtrans.dll -lwwwcore.dll -lwwwutils.dll -lmd5 -L
    /usr/lib -lz -lssl -lcrypto
    Cflags: -I${includedir} -I${includedir}/w3c-libwww
    </file>
    你可以用 <cmd> pkg-config --list-all </cmd> 试试, 看能否列出 w3c-libwww.
    当然, 如果你的 prefix 不同, 你可以修改.

    ===================================================================
    ??? 为什么 gcc 顽固地提示某些符号无法解决呢?
    >>> 修改 libwww-config 或者 w3c-libwww.pc , 把其中的库列表用
    -Wl,--start-group 和 -Wl,--end-group 括起来.

    ===================================================================
    ??? Cygwin 下的 gtk+ 开发需要使用 -mms-bitfields -mno-cygwin 选项,
    w3c-libwww 需要吗?
    >>> 为保持良好的可移植性, w3c-libwww 没有使用位域, 因此与 -mms-bitfields
    选项无关, 也不强制要求使用 -mno-cygwin 选项, 但是考虑到与 gtk+ 的集成问题,
    笔者推荐使用 -mno-cygwin .

    ===================================================================
    ??? Linux 下 <cmd> libwww-config --cflags </cmd> 还包含一个 -DHAVE_CONFIG_H,
    这是必要的吗? 在 Cygwin 下情况如何?
    >>> 在 Linux 下是必要的. 在 Cygwin 下则是*不能*要的. 而且你必须从源代码的
    Library/src/windows 目录下面复制一份儿 config.h 到安装目录的
    include/w3c-libwww/windows 下. 可能还需要注释掉下面这个配置项目:
    <code "include/w3c-libwww/windows/config.h">
    /* Define if you have the <direct.h> header file.  */
    /*#define HAVE_DIRECT_H 1*/
    </code>
    然后, 你*必须*使用 -D_CONSOLE 或者 -D_WINDOWS 中的一个. 原因请阅读
    include/w3c-libwww/wwwsys.h . 你可能还需要注释掉这个文件中这行代码:
    <code "include/w3c-libwww/wwwsys.h">
    /* #define MKDIR(a,b)      mkdir(a) */
    </code>
    你可能还需要从系统 include 目录下面复制一份儿 regex.h 到你的安装
    include/w3c-libwww 目录下.

    ===================================================================
    ??? 如何知道我的程序使用了正确的 dll 呢?
    >>> <cmd> cygcheck prog.exe </cmd>

    ===================================================================
    ??? 听说 w3c-libwww 使用 C 语言实现了许多 OO 风格的编程, 是吗?
    >>> http://www.w3.org/Library/User/Style/

    ===================================================================
    ??? w3c-libwww 的文档不提供打包下载, 我必须在线阅读吗?
    >>>
    <cmd> wget -x -r -nc -nH -p -np --cut-dirs=2 http://www.w3.org/Library/User <
    /cmd>

    ===================================================================
    ??? 给个程序试一试, 看看开发调试环境是否已经配置好.
    >>>
    <file "libinit.c">
    /*
    On Linux:
    gcc -g -Wall -o libinit libinit.c `pkg-config --cflags --libs w3c-libwww`

    On Cygwin:
    gcc -c -g -Wall -mno-cygwin -D_CONSOLE -o libinit.o \
            `pkg-config --cflags w3c-libwww` libinit.c
    gcc -g -mno-cygwin -o libinit.exe libinit.o `pkg-config --libs w3c-libwww`
    */
    #include <WWWLib.h>

    /* w3c-libwww 没有提供 Win32 下的缺省 Print 函数, 因此我们必须自己做 */
    int printer(const char *fmt, va_list args)
    {
            return vfprintf(stdout, fmt, args);
    }

    int main(int argc, char *argv[])
    {
            /* Win32 下要使用 HTPrint() 就必须自己设置 callback */
            HTPrint_setCallback(printer);
            HTPrint("Hello w3c-libwww\n");

            HTLibInit("TestApp", "0.0.1");
            HTLibTerminate();
            return 0;
    }
    </file>
    另外, Win32 下 HTTRACE() 无效.

    ===================================================================
    ??? 开发调试环境配置好了. 一个典型的 w3c-libwww 应用是个什么样子?
    >>> 实现某个协议, 按照这个协议发送请求, 或者响应请求.
    <file "req.c">
    #include <WWWLib.h>
    #include <WWWInit.h>

    int printer(const char *fmt, va_list args)
    {
            return vfprintf(stdout, fmt, args);
    }

    /* 实现某个协议的客户端代码, 通常要实现一个状态机 */
    int my_client(SOCKET sock, HTRequest *req)
    {
            HTNet *net = HTRequest_net(req);

            HTPrint("This's my_client\n");
            HTNet_delete(net, HT_OK);
            return HT_OK;
    }

    /* 实现某个协议的服务器端代码, 通常要实现一个状态机 */
    int my_server(SOCKET sock, HTRequest *req)
    {
            HTNet *net = HTRequest_net(req);

            HTPrint("This's my_server\n");
            HTNet_delete(net, HT_OK);
            return HT_OK;
    }

    int main(int argc, char *argv[])
    {
            HTRequest *req;

            HTLibInit("ProtTest", "0.0.1");
            HTPrint_setCallback(printer);

            HTTransport_add("mytp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
            /* 注册我们的协议 */
            HTProtocol_add("myprot", "mytp", 2008, NO, my_client, my_server);

            req = HTRequest_new();

            /* 发出一个按照我们协议的请求 */
            HTLoadAbsolute("myprot://localhost", req);
            HTPrint("\n");
            /* 响应一个按照我们协议的请求 */
            HTServeAbsolute("myprot://localhost", req);

            HTRequest_delete(req);

            HTLibTerminate();
            return 0;
    }
    </file>

    ===================================================================
    ??? 文档上说, 过滤器很重要. 过滤器是如何设置和被调用的?
    >>>
    <file "filter.c">
    #include <WWWLib.h>
    #include <WWWInit.h>

    int printer(const char *fmt, va_list args)
    {
            return vfprintf(stdout, fmt, args);
    }

    int my_client(SOCKET sock, HTRequest *req)
    {
            HTNet *net = HTRequest_net(req);

            HTPrint("This's my_client\n");
            HTNet_delete(net, HT_OK); /* 造成后置过滤器被调用 */
            return HT_OK;
    }

    int my_server(SOCKET sock, HTRequest *req)
    {
            HTNet *net = HTRequest_net(req);

            HTPrint("This's my_server\n");
            HTNet_delete(net, HT_OK); /* 造成后置过滤器被调用 */
            return HT_OK;
    }

    int my_before(HTRequest *req, void *param, int mode)
    {
            HTPrint("This's my_before(%s)\n", (char *)param);
            return HT_OK;
    }

    int my_after(HTRequest *req, HTResponse *resp, void *param, int status)
    {
            HTPrint("This's my_after(%s, %d)\n", (char *)param, status);
            return HT_OK;
    }

    int my_req_before(HTRequest *req, void *param, int mode)
    {
            HTPrint("This's my_req_before(%s)\n", (char *)param);
            return HT_OK;
    }

    int my_req_after(HTRequest *req, HTResponse *resp, void *param, int status)
    {
            HTPrint("This's my_req_after(%s, %d)\n", (char *)param, status);
            return HT_OK;
    }

    int main(int argc, char *argv[])
    {
            HTRequest *req;

            HTLibInit("ProtTest", "0.0.1");
            HTPrint_setCallback(printer);

            HTTransport_add("mytp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
            HTProtocol_add("myprot", "mytp", 2008, NO, my_client, my_server);
            /* 注册全局前置过滤器和后置过滤器 */
            HTNet_addBefore(my_before, NULL, "before-param", HT_FILTER_LAST);
            HTNet_addAfter(my_after, NULL, "after-param", HT_ALL, HT_FILTER_LAST);

            req = HTRequest_new();
            /* 注册请求前置过滤器和后置过滤器 */
            HTRequest_addBefore(req, my_req_before, NULL, "req_before_param",
                                HT_FILTER_LAST, NO);
            HTRequest_addAfter(req, my_req_after, NULL, "req_after_param", HT_ALL,
                               HT_FILTER_LAST, NO);

            HTLoadAbsolute("myprot://localhost", req);
            HTPrint("\n");
            HTServeAbsolute("myprot://localhost", req);

            HTRequest_delete(req);

            HTLibTerminate();
            return 0;
    }
    </file>

    ===================================================================
    ??? 任何具有 OO 风格的, 都必须处理事件. 请给个事件的例子好吗?
    >>>
    <file "timer.c">
    #include <WWWLib.h>
    #include <WWWInit.h>

    int printer(const char *fmt, va_list args)
    {
            return vfprintf(stdout, fmt, args);
    }

    int my_timer(HTTimer *tmr, void *param, HTEventType type)
    {
            static int count = 0;

            HTPrint("I'm my_timer(%s) %d\n", (char *)param, count++);
            if (count >= 3)
                    HTEventList_stopLoop();
            return HT_OK;
    }

    int main(int argc, char *argv[])
    {
            HTTimer *timer;

            HTPrint_setCallback(printer);
            HTLibInit("TestApp", "0.0.1");
            HTEventInit();

            timer = HTTimer_new(NULL, my_timer, "my-timer", 2000, YES, YES);
            HTEventList_newLoop();
            HTPrint("app stopped\n");

            HTEventTerminate();
            HTLibTerminate();
            return 0;
    }
    </file>

    ===================================================================
    ??? 输入输出无疑是非常重要的. 请给个输出流的例子好吗?
    >>>
    <file "out_strm.c">
    #include <WWWLib.h>
    #include <WWWInit.h>
    #include <WWWStream.h>

    struct _HTStream {
            HTStreamClass *isa;
    };

    static int printer(const char *fmt, va_list pArgs)
    {
            return vfprintf(stderr, fmt, pArgs);
    }

    int main(int argc, char *argv[])
    {
            HTStream *out;

            HTPrint_setCallback(printer);
            HTLibInit("Test", "0.0.1");

            out = HTFWriter_new(NULL, stdout, YES);
            HTPrint("I'm %s\n", out->isa->name);
            (out->isa->put_string)(out, "Hello, stream\n");
            (out->isa->flush)(out);
            (out->isa->_free)(out);

            HTLibTerminate();
            return 0;
    }
    </file>

    ===================================================================
    ??? 请给个输入流的例子.
    >>>
    <file "in_strm.c">
    #include <WWWLib.h>
    #include <WWWInit.h>
    #include <WWWStream.h>

    struct _HTInputStream {
            HTInputStreamClass *isa;
    };

    int main(int argc, char *argv[])
    {
            HTStream *out;
            HTInputStream *in;
            HTChannel *chnl;
            HTNet *innet;
            HTHost *host;
            FILE *fp;

            if (NULL == (fp = fopen(__FILE__, "rb")))
                    return (fprintf(stderr, "Can't open source file\n"), 1);
            HTLibInit("Test", "0.0.1");

            host = HTHost_new("localhost", 0);
            innet = HTNet_new(host);
            out = HTFWriter_new(NULL, stdout, YES);
            HTNet_setReadStream(innet, out);
            HTHost_addNet(host, innet);

            chnl = HTChannel_new(INVSOC, fp, YES);

            in = HTANSIReader_new(host, chnl, NULL, 0);
            (in->isa->read)(in);
            (in->isa->close)(in);

            HTChannel_delete(chnl, HT_OK);

            HTLibTerminate();
            fclose(fp);
            return 0;
    }
    </file>

    ===================================================================
    ??? 我怎么没看到 write 啊? 读入的东西是怎么跑到输出上去的呢?
    >>> w3c-libwww 认为, 没有无缘无故的读, 也没有无缘无故的写. 因此你必须
    在读之前设置好输出, 然后在 (in->isa->read)(in) 的时候, 会找到你所设置
    的输出, 并写到这个输出里面. 调用方只管事前设置就行了, 无需事中干预.
    参见 Library/src/HTANSI.c!HTANSIReader_read .

    ===================================================================
    ??? 这些事前设置好像很乱啊.
    >>> 那是为了循序渐进, 每次引入几个概念以便读者逐步认识. 如果不是自己
    开发新协议, 而只是应用 w3c-ilbwww 的话, 可以简单一些.
    <file "sload.c">
    /* CAUTION: For Cygwin Win32 ONLY !!! */
    #include <WWWLib.h>
    #include <WWWInit.h>

    int terminate_handler(HTRequest * request, HTResponse * response,
                          void * param, int status)
    {
            return (HTEventList_stopLoop(), HT_OK);
    }

    int main(int argc, char *argv[])
    {
            char *              url = NULL;
            HTRequest *         request = HTRequest_new();

            HTLibInit("Simple load file", "0.0.1");
            HTTransport_add("local", HT_TP_SINGLE, HTANSIReader_new,
                            HTANSIWriter_new);
            HTProtocol_add("file", "local", 0, YES, HTLoadFile, NULL);
            HTEventInit();
            HTNet_addAfter(terminate_handler, NULL, NULL, HT_ALL, HT_FILTER_LAST);

            url = HTGetCurrentDirectoryURL();
            HTSACat(&url, __FILE__);
            if (HTLoadToFile/*!HTLoadFile*/(url, request, "CON") == YES)
                    HTEventList_loop(request);
            HT_FREE(url);

            HTRequest_delete(request);
            HTEventTerminate();
            HTLibTerminate();
            return 0;
    }
    </file>

    ===================================================================
    ??? 这回连 read 也看不到了.
    >>> 参见 Library/src/HTFile.c!HTLoadFile 和 HTHost.c!HTHost_read .

    ===================================================================
    ??? 从 HTLoadToFile 到 HTLoadFile 又发生了什么呢?
    >>> 按照下列顺序去读 Library/src 下的源代码:
    HTAccess.c!HTLoadToFile(url, request, filename)
    HTAccess.c!HTLoadAbsolute(url, request)
    HTReqMan.c!HTLoad(request, recursive)
    HTNet.c!HTNet_newClient(request)
    HTProt.c!HTProtocol_find(request, access)
    HTProt.c!HTProtocol_client(protocol) 就是 HTLoadFile 了.
    最后的调用语句是这样的: (*(cbf))(INVSOC, request) .

    ===================================================================
    ??? HTNet_newServer(request) 和 HTNet_newClient(request) 主要干什么?
    >>> 首先 HTNet_executeBeforeAll(request), 然后 create_object() 创建一个
    空的 HTNet, 设置好 preemptive, protocol, transport, request, 同时也
    HTRequest_setNet(request, me), 建立 request 和 net 的双向互指关系.
    然后就 (*(cbf))(INVSOC, request), 调用服务(或客户)回调函数.

    ===================================================================
    ??? 为什么要做的这么麻烦啊?
    >>> 比如小学生做应用题, 通常会列出一排算式, 都是具体数字, 每一步得一个
    结果, 实实在在. 但这只是算术, 很初级的东西. 最明显的局限是"可移植性"
    和"可重用性"太低. 高级一点的就是代数. 用常数和未知数取代具体数字,
    在不知道其具体内容的情况下进行运算. 写程序也是这样. 初学者总是习惯于调用
    具体的函数, 比如为 file:// 写一个 HTNet_newFileClient, 为 http:// 写一个
    HTNet_newHttpClient, 或者把协议类型作为参数传进来, 然后用 switch 分别
    调用 HTLoadFile, HTLoadHttp. 如果有一天要增加一个 newprot:// , 那么这些
    代码就都要作相应改动. 这就相当于"算术"的水平. 高级一点的就使用函数指针.
    不管这个函数的具体内容, HTNet_newClient 只管在适当的环境下以适当的方式
    进行调用就行了. 就像对未知数进行"代数"运算一样. 这样的程序抽象层次更高,
    适用性更广. 增改协议的时候, 不需要改动 HTNet_newClient. 类似的技术今天
    已经应用得相当普遍. 比如 C++ 的抽象类, Java 和 Gtk+ 的 Interface 等等.

    ===================================================================
    ??? 我如何建立一个服务呢?
    >>> 有一个 Library/Examples/listen.c 改写如下:
    <file "listen.c">
    #include <WWWLib.h>
    #include <WWWInit.h>

    int global_after(HTRequest *req, HTResponse *resp, void *param, int status)
    {
            return (HTEventList_stopLoop(), HT_OK);
    }

    int main(int argc, char *argv[])
    {
            HTRequest *req = HTRequest_new();
            HTStream *out;

            HTLibInit("Simple Listener", "0.0.1");
            HTEventInit();
            HTNet_addAfter(global_after, NULL, NULL, HT_ALL, HT_FILTER_LAST);
            HTTransport_add("tcp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
            /* 注意端口号是 2004 (任意), 服务回调函数是 HTLoadSocket */
            HTProtocol_add("noop", "tcp", 2004, NO, NULL, HTLoadSocket);

            HTRequest_setOutputFormat(req, WWW_RAW);
            out = HTFWriter_new(req, stdout, YES);
            HTRequest_setOutputStream(req, out);

            HTServeAbsolute("noop://localhost", req);
            HTEventList_newLoop();

            HTRequest_delete(req);
            HTEventTerminate();
            HTLibTerminate();
            return 0;
    }
    </file>
    执行 listen.exe , 然后从浏览器上访问 http://localhost:2004 , 就可以从
    控制台看到浏览器发给服务器的请求的全部内容. 用 telnet://localhost:2004
    连接, 则可以看到从 telnet 终端输入的内容都显示在控制台上.

    ===================================================================
    ??? 看起来关键是服务回调函数 HTLoadSocket , 它是干什么的?
    >>> 参见 Library/src/HTSocket.c . 先做一个 HTHost_listen, 然后进入
    SocketEvent, 若是 RAW_BEGIN, 则 HTHost_accept, 进入 RAW_NEED_STREAM.
    如是 RAW_NEED_STREAM, 则把 request 的 outputStream 设置为 net 的
    readStream , 并设置已连接, 进入 RAW_READ. 若是 RAW_READ, 则进行
    HTHost_read.

    ===================================================================
    ??? 我能做一个 echo 吗?
    >>>
    <file "secho.c">
    #include <WWWLib.h>
    #include <WWWInit.h>

    int global_after(HTRequest *req, HTResponse *resp, void *param, int status)
    {
            HTEventList_stopLoop();
            return HT_OK;
    }

    typedef enum _EchoState {
            ECHO_ERROR = -2,
            ECHO_OK = -1,
            ECHO_BEGIN = 0,
            ECHO_NEED_STREAM,
            ECHO_READ
    } EchoState;

    typedef struct _echo_info {
            EchoState state;
            HTNet *net;
            HTRequest *request;
    } echo_info;

    static int EchoCleanup(HTRequest *request, int status)
    {
            HTNet *net = HTRequest_net(request);
            echo_info *echo = (echo_info *)HTNet_context(net);

            if (status == HT_INTERRUPTED) {
                    (void)0;
            } else if (status == HT_TIMEOUT) {
                    (void)0;
            }
            HTNet_delete(net, HT_ERROR);
            HT_FREE(echo);
            return YES;
    }

    static int EchoEvent(SOCKET soc, void *pVoid, HTEventType type);

    int HTServEcho(SOCKET soc, HTRequest *request)
    {
            echo_info *echo;
            HTNet *net = HTRequest_net(request);
            char *url;

            if (NULL == (echo = (echo_info *)HT_CALLOC(1, sizeof(echo_info))))
                    HT_OUTOFMEM("HTServEcho");
            echo->state = ECHO_BEGIN;
            echo->net = net;
            echo->request = request;
            HTNet_setContext(net, echo);
            HTNet_setEventCallback(net, EchoEvent);
            HTNet_setEventParam(net, echo);

            url = HTAnchor_physical(HTRequest_anchor(request));
            if (HTHost_listen(NULL, net, url) == HT_ERROR)
                    /* HTNet_execute(net, HTEvent_CLOSE) */
                    return EchoEvent(soc, echo, HTEvent_CLOSE);
            /* HTNet_start(net) */
            return EchoEvent(soc, echo, HTEvent_BEGIN);
    }

    static int EchoEvent(SOCKET soc, void *pVoid, HTEventType type)
    {
            echo_info *echo = (echo_info *)pVoid;
            int status = HT_ERROR;
            HTNet *net = echo->net;
            HTRequest *request = echo->request;
            HTHost *host = HTNet_host(net);

            if (type == HTEvent_BEGIN) {
                    echo->state = ECHO_BEGIN;
            } else if (type == HTEvent_CLOSE) {
                    EchoCleanup(request, HT_INTERRUPTED);
                    return HT_OK;
            } else if (type == HTEvent_TIMEOUT) {
                    HTRequest_addError(request, ERR_FATAL, NO, HTERR_TIME_OUT,
                                       NULL, 0, "HTServEcho");
                    EchoCleanup(request, HT_TIMEOUT);
                    return HT_OK;
            } else if (type == HTEvent_END) {
                    EchoCleanup(request, HT_OK);
                    return HT_OK;
            }

            while (1) {
                    switch (echo->state) {
                    case ECHO_BEGIN:
                            status = HTHost_accept(host, net, NULL);
                            host = HTNet_host(net);
                            if (status == HT_OK) {
                                    echo->state = ECHO_NEED_STREAM;
                            } else if (status == HT_WOULD_BLOCK
                                       || status == HT_PENDING) {
                                    return HT_OK;
                            } else {
                                    echo->state = ECHO_ERROR;
                            }
                            break;
                    case ECHO_NEED_STREAM:
                            HTNet_setReadStream(net,
                                (HTStream *)HTNet_getOutput(net, NULL, 0));
                            HTRequest_setOutputConnected(request, YES);
                            echo->state = ECHO_READ;
                            break;
                    case ECHO_READ:
                            status = HTHost_read(host, net);
                            if (status == HT_WOULD_BLOCK)
                                    return HT_OK;
                            else if (status == HT_CLOSED)
                                    echo->state = ECHO_OK;
                            else
                                    echo->state = ECHO_ERROR;
                            break;
                    case ECHO_OK:
                            EchoCleanup(request, HT_OK);
                            return HT_OK;
                            break;
                    case ECHO_ERROR:
                            EchoCleanup(request, HT_ERROR);
                            return HT_OK;
                            break;
                    default:
                            HTDEBUGBREAK("Bad echo state %d\n" _ echo->state);
                    }
            }
            return HT_OK;
    }

    int main(int argc, char *argv[])
    {
            HTRequest *req;

            HTLibInit("Simply Echo", "0.0.1");
            HTEventInit();
            HTNet_addAfter(global_after, NULL, NULL, HT_ALL, HT_FILTER_LAST);
            HTTransport_add("tcp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
            HTProtocol_add("noop", "tcp", 2004, NO, NULL, HTServEcho);

            req = HTRequest_new();
            HTRequest_setOutputFormat(req, WWW_RAW);

            HTServeAbsolute("noop://localhost", req);
            HTEventList_newLoop();

            HTRequest_delete(req);
            HTEventTerminate();
            HTLibTerminate();
            return 0;
    }
    </file>
    这个程序在 Win32/Cygwin 上编译后, 运行. 用 telnet 连接本机端口 2004,
    就可以看到 echo 的效果了. 但是在 linux 上编译后, 只能 echo 一下,
    包括其本身的源码 Library/Example/listen.c 也是这样. 原因不详.

    ===================================================================
    ??? 事件究竟是在什么地方被执行的?
    >>>
    <code "HTEvtLst.c!AsyncWindowProc(hwnd, wMsg, wParam, lParam)">
        if (uMsg == WM_TIMER) {
            HTTimer_dispatch((HTTimer *)wParam);
            return (0);
        }
        /* dispatch() means (*event->cbf)(s, event->param, type); */
        if (HTEventList_dispatch((int)sock, type, now) != HT_OK)
            HTEndLoop = -1;
    </code>
    另一方面:
    <code "HTEvtLst.c!HTEventList_loop(NULL)">
        /* Don't leave this loop until we leave the application */
        while (!HTEndLoop) {
            if (active_sockets == 0)
                continue;

            /* There were active sockets. Determine which fd sets they were in */
            for (s = 0 ; s <= maxfds ; s++) {
                if (FD_ISSET(s, &texceptset))
                    if ((status = EventOrder_add(s, HTEvent_OOB, now)) != HT_OK)
                        goto stop_loop;
                if (FD_ISSET(s, &twriteset))
                    if ((status = EventOrder_add(s, HTEvent_WRITE, now)) != HT_OK)
                        goto stop_loop;
                if (FD_ISSET(s, &treadset))
                    if ((status = EventOrder_add(s, HTEvent_READ, now)) != HT_OK)
                        goto stop_loop;
            }
            /* execute() means (*event->cbf)(s, event->param, type); */
            if ((status = EventOrder_executeAndDelete()) != HT_OK) break;
        };
    </code>

    ===================================================================
    ??? secho 只能接受一个连接, 我如何做一个真正的服务器呢?
    >>> 从 4.0 版到 5.0 版, w3c-libwww 曾经有个短命的 MiniServer 子项目.
    到 5.1 版就被放弃了. 由于现在的 5.4 版与 5.0 版在 API 上已经发生了巨大的
    变化, 因此这个 MiniServer 的源代码已经没有什么参考价值了. w3c-libwww
    可能是出于 "只做一件事情, 力图做到最好" 的考虑, 放弃了对服务器框架的支持.

    ===================================================================
    ??? 用 w3c-libwww 来开发客户端, 有哪些典型的应用呢?
    >>> 你自己去看文档吧, 有很多好的例子. 我能说的就到此为止了.
    本文附录有一些随手摘录的笔记, 既不正式, 也不完全, 聊作参考吧.

    ===================================================================
    附: 本文档例子程序在 Win32/Cygwin 平台上的 Makefile,
    <file "Makefile">
    all: libinit.exe req.exe filter.exe timer.exe out_strm.exe in_strm.exe \
            sload.exe listen.exe secho.exe

    .SUFFIXES:
    .SUFFIXES: .exe .o .c

    .o.exe:
            gcc -g -mno-cygwin -o $@ $< `pkg-config --libs w3c-libwww`

    .c.o:
            gcc -c -g -Wall -mno-cygwin -D_CONSOLE -o $@ \
                    `pkg-config --cflags w3c-libwww` $<

    clean:
            @rm -f *.exe *.o

    .PHONY: all
    </file>

    ===================================================================
    ??? WWWLib.h 包括哪些内容?
    >>>
    <code "WWWLib.h">
    #include "wwwsys.h"
    #include "WWWUtil.h"
    #include "WWWCore.h"
    </code>

    ===================================================================
    ??? WWWUtil.h 包括哪些内容?
    >>>
    <code "WWWUtil.h">
    #include "wwwsys.h"
    #include "HTUtils.h"
    #include "HTArray.h"
    #include "HTAssoc.h"
    #include "HTAtom.h"
    #include "HTChunk.h"
    #include "HTList.h"
    #include "HTMemory.h"
    #include "HTString.h"
    #include "HTUU.h"
    </code>

    ===================================================================
    ??? libwww 是如何处理 HTPrint 的?
    >>> 用户可以设置自己的方式。对 Win32 平台,则是“必须”设置自己的方式。
    <code "HTUtils.h">
    typedef int HTPrintCallback(const char * fmt, va_list pArgs);

    extern void HTPrint_setCallback(HTPrintCallback * pCall);
    extern HTPrintCallback * HTPrint_getCallback(void);

    extern int HTPrint(const char * fmt, ...);
    </code>

    <code "HTTrace.c">
    PRIVATE HTPrintCallback * PHTPrintCallback = NULL;

    PUBLIC void HTPrint_setCallback (HTPrintCallback * pCall)
    {
        PHTPrintCallback = pCall;
    }

    PUBLIC HTPrintCallback * HTPrint_getCallback (void)
    {
        return PHTPrintCallback;
    }

    PUBLIC int HTPrint (const char * fmt, ...)
    {
        va_list pArgs;
        va_start(pArgs, fmt);
        if (PHTPrintCallback)
            return (*PHTPrintCallback)(fmt, pArgs);
    #ifdef WWW_WIN_WINDOW
        return (0);  /* 对于 Win32 平台,必须由用户设置回调函数。*/
    #else
        return (vfprintf(stdout, fmt, pArgs));
    #endif
    }
    </code>

    ===================================================================
    ??? libwww 是如何处理 HTTRACE 的?
    >>> 注意 HTTRACE 与 HTPrint 有本质的不同。HTTRACE 是一个宏,可以被定义为
    像 HTPrint 一样的函数 HTTrace,也可以被定义为根本什么也没有。而且 HTTRACE
    在 Win32 平台上实际没有实现,虽然理论上并没有障碍。
    在其他平台上,函数 HTTrace 的实现与 HTPrint 类似,只是增加了一个
    WWW_TraceFlag。以选择不同的 trace 范畴。
    <code "HTUtils.h">
    #ifdef HTDEBUG
    #ifdef WWW_WIN_DLL
    extern int *            WWW_TraceFlag;   /* In DLLs, we need the indirection*/
    #define WWWTRACE        (*WWW_TraceFlag) /* 但实际上,这个变量既没有被实例化*/
                                             /* 也没有被 export,因此根本没用。*/
    #else
    extern unsigned int     WWW_TraceFlag;       /* Global flag for all W3 trace*/
    #define WWWTRACE        (WWW_TraceFlag)  /* 在 HTTrace.c 中被实例化。*/
    #endif /* WWW_WIN_DLL */
    #else
    #define WWWTRACE        0                /* 非调试版屏蔽一切 trace。*/
    #endif /* HTDEBUG */

    typedef enum _HTTraceFlags {
        SHOW_UTIL_TRACE     = 0x1,
        SHOW_APP_TRACE      = 0x2,
        ....
        SHOW_ALL_TRACE      = (int) 0xFFFFFFFF
    } HTTraceFlags;

    #define UTIL_TRACE      (WWWTRACE & SHOW_UTIL_TRACE)
    #define APP_TRACE       (WWWTRACE & SHOW_APP_TRACE)
    ....
    #define ALL_TRACE       (WWWTRACE & SHOW_ALL_TRACE)

    typedef int HTTraceCallback(const char * fmt, va_list pArgs);
    extern void HTTrace_setCallback(HTTraceCallback * pCall);
    extern HTTraceCallback * HTTrace_getCallback(void);

    #ifdef HTDEBUG
    #undef _
    #define _ ,
    /* 这里的 TYPE 就是 APP_TRACE 之类,即 (WWWTRACE & SHOW_APP_TRACE) 之类。*/
    #define HTTRACE(TYPE, FMT) \
            do { if (TYPE) HTTrace(FMT); } while (0);
    extern int HTTrace(const char * fmt, ...);
    #else
    #define HTTRACE(TYPE, FMT)              /* empty */
    #endif /* HTDEBUG */

    typedef int HTTraceDataCallback(char * data, size_t len, char * fmt,
                                    va_list pArgs);
    extern void HTTraceData_setCallback(HTTraceDataCallback * pCall);
    extern HTTraceDataCallback * HTTraceData_getCallback(void);

    #ifdef HTDEBUG
    #define HTTRACEDATA(DATA, LEN, FMT) HTTraceData((DATA), (LEN), FMT)
    extern int HTTraceData(char * data, size_t len, char * fmt, ...);
    #else
    #define HTTRACEDATA(DATA, LEN, FMT)     /* empty */
    #endif /* HTDEBUG */
    </code>

    <code "HTTrace.c">
    #if WWWTRACE_MODE == WWWTRACE_FILE
    PUBLIC FILE *WWWTrace = NULL;
    #endif

    #ifndef WWW_WIN_DLL
    PUBLIC unsigned int WWW_TraceFlag = 0; /* Global trace flag for ALL W3 code */
    #endif

    PRIVATE HTTraceCallback * PHTTraceCallback = NULL;
    PRIVATE HTTraceDataCallback * PHTTraceDataCallback = NULL;

    /* ---------------------------------------------------------------------- */

    PUBLIC void HTTrace_setCallback (HTTraceCallback * pCall)
    {
        PHTTraceCallback = pCall;
    }

    PUBLIC HTTraceCallback * HTTrace_getCallback (void)
    {
        return PHTTraceCallback;
    }

    PUBLIC int HTTrace (const char * fmt, ...)
    {
        va_list pArgs;
        va_start(pArgs, fmt);
        if (PHTTraceCallback)
            return (*PHTTraceCallback)(fmt, pArgs);
    #ifdef WWW_WIN_WINDOW
        return (0);
    #else
        return (vfprintf(stderr, fmt, pArgs));
    #endif
    }

    PUBLIC void HTTraceData_setCallback (HTTraceDataCallback * pCall)
    {
        PHTTraceDataCallback = pCall;
    }

    PUBLIC HTTraceDataCallback * HTTraceData_getCallback (void)
    {
        return PHTTraceDataCallback;
    }

    PUBLIC int HTTraceData (char * data, size_t len, char * fmt, ...)
    {
        va_list pArgs;
        va_start(pArgs, fmt);
        if (PHTTraceDataCallback)
            return (*PHTTraceDataCallback)(data, len, fmt, pArgs);
        return (0); /* 必须由用户定义回调函数 */
    }
    </code>

    ===================================================================
    ??? libwww 是如何处理 HTArray 的?
    >>> HTArray 是个存储 (void *) 的数组,可以自动扩展。
    <code "HTArray.h" impl="HTArray.c">
    /* 这个结构也可以放在 HTArray.c 当中,做成 private。
       相应的几个宏改成函数。参见 HTChunk。
    */
    typedef struct {
        int         size;           /* In numbers of elements       */
        int         growby;         /* Allocation unit in elements  */
        int         allocated;      /* Current size of *data        */
        void **     data;           /* Pointer to malloced area or 0 */
    } HTArray;

    extern HTArray * HTArray_new (int grow);
    extern BOOL HTArray_delete (HTArray * array);
    extern BOOL HTArray_clear (HTArray * array);
    extern BOOL HTArray_addObject (HTArray * array, void * object);

    /* 如果有数据,则返回该数据,并准备访问下一个数据 */
    #define HTArray_firstObject(me, dp) \
            ((me) && ((dp)=(me)->data) ? *(dp)++ : NULL)
    #define HTArray_nextObject(me, dp) \
            ((me) && (dp) ? *(dp)++ : NULL)

    typedef int HTComparer (const void * a, const void * b);
    extern BOOL HTArray_sort (HTArray * array, HTComparer * comp);
    #define HTArray_data(me)        ((me) ? (me)->data : NULL)
    #define HTArray_size(me)        ((me) ? (me)->size : -1)
    </code>

    ===================================================================
    ??? libwww 如何处理 HTList?
    >>> HTList 是个单向链表,其数据域为 void * object。
    <code "HTList.h" impl="HTList.c">
    typedef struct _HTList HTList;

    struct _HTList {
      void * object;
      HTList * next;
    };

    extern HTList * HTList_new      (void);
    extern BOOL     HTList_delete   (HTList *me);
    extern BOOL HTList_addObject (HTList *me, void *newObject);
    extern BOOL HTList_appendObject (HTList * me, void * newObject);
    extern HTList * HTList_addList (HTList * me, void * newObject);
    extern HTList * HTList_appendList (HTList * me, void * newObject);
    extern BOOL     HTList_removeObject             (HTList * me, void * oldObject);
    extern BOOL     HTList_quickRemoveElement       (HTList * me, HTList * last);
    extern BOOL     HTList_removeObjectAll          (HTList * me, void * oldObject);
    extern void *   HTList_removeLastObject         (HTList * me);
    extern void *   HTList_removeFirstObject        (HTList * me);
    /* NULL 和 me->next==NULL 都被视为空链表 */
    #define         HTList_isEmpty(me)              (me ? me->next == NULL : YES)
    extern int      HTList_count                    (HTList *me);
    extern int      HTList_indexOf   (HTList * me, void * object);
    extern int      HTList_indexOfElement (HTList * me, HTList * element);
    extern void *   HTList_objectAt  (HTList * me, int position);
    extern HTList * HTList_elementOf (HTList * me, void * object, HTList ** pLast);
    #define         HTList_objectOf(me)             (me ? me->object: NULL)
    #define         HTList_lastObject(me) \
                    ((me) && (me)->next ? (me)->next->object : NULL)
    extern void * HTList_firstObject  (HTList * me);
    #define         HTList_nextObject(me) \
                    ((me) && ((me) = (me)->next) ? (me)->object : NULL)
    extern BOOL HTList_insertionSort(HTList * list, HTComparer * comp);
    #define HTList_free(x)  HT_FREE(x)
    </code>

    ===================================================================
    ??? libwww 如何处理 HTAssocList ?
    >>> HTAssocList 就是 HTList,只是其数据域为 HTAssoc。
    <code "HTAssoc.h" impl="HTAssoc.c">
    typedef HTList HTAssocList;

    typedef struct {
        char * name;
        char * value;
    } HTAssoc;

    extern HTAssocList * HTAssocList_new (void);
    extern BOOL          HTAssocList_delete (HTAssocList * alist);
    extern BOOL HTAssocList_addObject (
            HTAssocList *   alist,
            const char *    name,
            const char *    value);
    extern BOOL HTAssocList_replaceObject (
            HTAssocList *   list,
            const char *    name,
            const char *    value);
    extern BOOL HTAssocList_removeObject (
            HTAssocList *   list,
            const char *    name);
    extern char * HTAssocList_findObject (
            HTAssocList *   alist,
            const char *    name);
    extern char * HTAssocList_findObjectExact (
            HTAssocList *   alist,
            const char *    name);
    extern char * HTAssocList_findObjectCaseSensitive (
            HTAssocList *   list,
            const char *    name);
    extern char * HTAssocList_findObjectCaseSensitiveExact (
            HTAssocList *   list,
            const char *    name);
    #define HTAssoc_name(me)        ((me) ? (me)->name : NULL)
    #define HTAssoc_value(me)       ((me) ? (me)->value : NULL)
    #define HTAssocList_nextObject(me) \
            ((me) && ((me) = (me)->next) ? (me)->object : NULL)
    </code>

    ===================================================================
    ??? libwww 如何处理 HTString?
    >>>
    <code "HTString.h" impl="HTString.c">
    #define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
    #define StrAllocCat(dest, src)  HTSACat  (&(dest), src)
    extern char * HTSACopy (char **dest, const char *src);
    extern char * HTSACat  (char **dest, const char *src);
    extern char * StrAllocMCopy (char ** dest, ...);
    extern char * StrAllocMCat (char ** dest, ...);
    extern int strcasecomp  (const char *a, const char *b);
    extern int strncasecomp (const char *a, const char *b, int n);
    extern int tailcomp(const char * s1, const char * s2);
    extern int tailcasecomp(const char * s1, const char * s2);
    extern char * HTStrMatch        (const char * tmpl, const char * name);
    extern char * HTStrCaseMatch    (const char * tmpl, const char * name);
    extern char * HTStrCaseStr (char * s1, char * s2);
    extern char * HTStrip (char * s);
    </code>

    ===================================================================
    ??? libwww 如何处理 HTAtom?
    >>> HTAtom 是个散列表,所谓的 atom 在这里就是单元的内存地址。
    <code "HTAtom.h" impl="HTAtom.c">
    typedef struct _HTAtom HTAtom;
    struct _HTAtom {
            HTAtom *        next;
            char *          name;
    }; /* struct _HTAtom */

    extern HTAtom * HTAtom_for      (const char * string);
    extern HTAtom * HTAtom_caseFor  (const char * string);
    #define HTAtom_name(a) ((a) ? (a)->name : NULL)
    extern HTList * HTAtom_templateMatches (const char * templ);
    extern void HTAtom_deleteAll (void);
    </code>

    <code "HTAtom.c">
    PRIVATE HTAtom * hash_table[HT_XL_HASH_SIZE];
    PRIVATE BOOL initialised = NO;
    /* 在 HTAtom_for 和 HTAtom_casefor 中, 先计算 hash,然后在
    hash_table 中 strcmp,strcasecomp,若有就返回该单元的内存地址,
    若没有就加入一个新单元,并返回其地址。
    */
    </code>

    ===================================================================
    ??? libwww 如何处理 HTChunk?
    >>>
    <code "HTChunk.h" impl="HTChunk.c">
    typedef struct _HTChunk HTChunk;

    extern HTChunk * HTChunk_new (int growby);
    extern void HTChunk_delete (HTChunk * ch);
    extern void HTChunk_clear (HTChunk * ch);
    extern void HTChunk_ensure (HTChunk * ch, int extra_size);
    extern void HTChunk_putc (HTChunk * ch, char c);
    extern void HTChunk_puts (HTChunk * ch, const char *str);
    extern void HTChunk_putb (HTChunk * ch, const char *block, int len);
    extern char * HTChunk_data (HTChunk * ch);
    extern int HTChunk_size (HTChunk * ch);
    extern BOOL HTChunk_truncate (HTChunk * ch, int size);
    extern BOOL HTChunk_setSize (HTChunk * ch, int size);
    extern void HTChunk_terminate (HTChunk * ch);
    extern HTChunk * HTChunk_fromCString    (char * str, int grow);
    extern char * HTChunk_toCString         (HTChunk * ch);
    extern HTChunk * HTChunk_fromBuffer (char * buf, int buflen, int size, int gr
    ow);
    </code>

    <code "HTChunk.c">
    struct _HTChunk {
        int         size;           /* In bytes                     */
        int         growby;         /* Allocation unit in bytes     */
        int         allocated;      /* Current size of *data        */
        char *      data;           /* Pointer to malloced area or 0 */
    };     
    /*      Free a chunk but keep the data
    */
    PUBLIC char * HTChunk_toCString (HTChunk * ch)
    {
        char * ret = 0;
        if (ch) {
            ret = ch->data;
            HT_FREE(ch);
        }
        return ret;
    }
    </code>

    ===================================================================
    ??? libwww 如何处理 HTMemory?
    >>>
    <code "HTMemory.h" impl="HTMemory.c">
    extern void* HTMemory_malloc(size_t size);
    extern void* HTMemory_calloc(size_t count, size_t size);
    extern void* HTMemory_realloc(void * ptr, size_t size);
    extern void HTMemory_free(void* ptr);

    #define HT_MALLOC(size)         HTMemory_malloc((size))
    #define HT_CALLOC(count, size)  HTMemory_calloc((count), (size))
    #define HT_REALLOC(ptr, size)   HTMemory_realloc((ptr), (size))
    #define HT_FREE(pointer)        {HTMemory_free((pointer));((pointer))=NULL;}

    /* 可以注册多个内存清理函数,出现短缺就进行清理 */
    typedef void HTMemoryCallback(size_t size);
    extern BOOL HTMemoryCall_add (HTMemoryCallback * cbf);
    extern BOOL HTMemoryCall_delete (HTMemoryCallback * cbf);
    extern BOOL HTMemoryCall_deleteAll (void);

    /* 可以设置一个内存不足无法继续时的必要清理函数,清理之后就 abort() */
    typedef void HTMemory_exitCallback(char *name, char *file, unsigned long line);
    extern void HTMemory_setExit(HTMemory_exitCallback * pExit);
    extern HTMemory_exitCallback * HTMemory_exit(void);

    #define outofmem(file, name)    HT_OUTOFMEM(name)
    #define HT_OUTOFMEM(name)       HTMemory_outofmem((name), __FILE__, __LINE__)
    extern void HTMemory_outofmem(char * name, char * file, unsigned long line);
    </code>

    <code "HTMemory.c">
    PRIVATE HTList * HTMemCall = NULL;                  /* List of memory freers */
    PRIVATE HTMemory_exitCallback * PExit = NULL;     /* panic and exit function */
    PRIVATE size_t LastAllocSize = 0;                 /* size of last allocation */

    PUBLIC void * HTMemory_malloc (size_t size)
    {
        void * ptr;
        ptr = malloc(LastAllocSize = size);
        if (ptr != NULL) return ptr;
        /* 出现短缺 */
        if (HTMemCall) {
            HTMemoryCallback * pres;
            while ((pres = (HTMemoryCallback *) HTList_nextObject(HTMemCall))) {
                HTTRACE(MEM_TRACE, "Mem Calling. %p (size %d)\n" _ (void*)pres _
    size);
                (*pres)(size);  /* 清理 */
                if ((ptr = malloc(size)) != NULL) return ptr;  /* 重试 */
            }
        }
        HTTRACE(MEM_TRACE, "Memory.... Couldn't allocate %d bytes\n" _ size);
        return NULL;
    }

    PUBLIC void HTMemory_outofmem (char * name, char * file, unsigned long line)
    {
        if (PExit)  /* 如果注册了必要清理函数,就进行必要清理 */
            (*PExit)(name, file, line);
        HTTRACE(ALL_TRACE, "%s:%ld failed allocation for \"%s\" (%ld bytes).\nPro
    gram aborted.\n" _
                file _ line _ name _ LastAllocSize);
        abort();
    }
    </code>

    ===================================================================
    ??? libwww 如何处理 HTUU?
    >>>
    <code "HTUU.h" impl="HTUU.c">
    extern int HTUU_encode (unsigned char * bufin, unsigned int nbytes,
                            char * bufcoded);
    extern int HTUU_decode (char * bufcoded, unsigned char * bufplain,
                            int outbufsize);
    </code>

    ===================================================================
    ??? WWWCore.h 包括哪些内容?
    >>>
    <code "WWWCore.h">
    #include "wwwsys.h"
    #include "HTLib.h"
    #include "HTReq.h"
    #include "HTMethod.h"
    #include "HTAnchor.h"
    #include "HTLink.h"
    #include "HTParse.h"
    #include "HTEscape.h"
    #include "HTUTree.h"
    #include "HTWWWStr.h"
    #include "HTUser.h"
    #include "HTEvent.h"
    #include "HTMemLog.h"
    #include "HTError.h"
    #include "HTAlert.h"
    #include "HTFormat.h"
    #include "HTStream.h"
    #include "HTStruct.h"
    #include "HTNoFree.h"
    #include "HTIOStream.h"
    #include "HTDNS.h"
    #include "HTHost.h"
    #include "HTNet.h"
    #include "HTInet.h"
    #include "HTTrans.h"
    #include "HTProt.h"
    </code>

    ===================================================================
    ??? libwww 如何处理 HTInet?
    >>> 获取网络配置的一些函数,在 HTUserProfile 中有运用。
    <code "HTInet.h" impl="HTInet.c">
    extern char * HTErrnoString     (int errnum);
    extern int HTInetStatus         (int errnum, char * where);
    extern unsigned int HTCardinal (int *           pstatus,
                                    char **         pp,
                                    unsigned int    max_value);
    extern const char * HTInetString (struct sockaddr_in * sin);
    extern int HTParseInet (HTHost * host, char * hostname, HTRequest * request);
    extern time_t HTGetTimeZoneOffset (void);
    extern ms_t HTGetTimeInMillis (void);
    extern char * HTGetHostName (void);
    extern char * HTGetMailAddress (void);
    extern char * HTGetNewsServer (void);
    extern char * HTGetTmpFileName (const char * dir);
    #ifdef WWWLIB_SIG
    extern void HTSetSignal (void);
    #endif
    </code>

    ===================================================================
    ??? libwww 如何处理 HTUserProfile?
    >>>
    <code "HTUser.h" impl="HTUser.c">
    typedef struct _HTUserProfile HTUserProfile;

    extern HTUserProfile * HTUserProfile_new (const char * name, void * context);
    extern BOOL HTUserProfile_localize (HTUserProfile * up); /* 填缺省值 */
    extern BOOL HTUserProfile_delete (HTUserProfile * up);
    </code>

    <code "HTUser.c">
    struct _HTUserProfile {
        char *      user; /* name, set in HTUserProfile_new(name, context) */
        /* 但是这个字段实际上没有任何用处! */

        char *      fqdn;                         /* Fully qualified domain name */
    extern char * HTUserProfile_fqdn (HTUserProfile * up);
    extern BOOL HTUserProfile_setFqdn (HTUserProfile * up, const char * fqdn);

        char *      email;                      /* Email address of current user */
    extern char * HTUserProfile_email (HTUserProfile * up);
    extern BOOL HTUserProfile_setEmail (HTUserProfile * up, const char * email);

        char *      news;                              /* The news server to use */
    extern char * HTUserProfile_news (HTUserProfile * host);
    extern BOOL HTUserProfile_setNews (HTUserProfile * host, const char * news);

        char *      tmp;                         /* Location for temporary files */
    extern char * HTUserProfile_tmp (HTUserProfile * host);
    extern BOOL HTUserProfile_setTmp (HTUserProfile * host, const char * tmp);

        time_t      timezone;                            /* Time zone in seconds */
    extern time_t HTUserProfile_timezone (HTUserProfile * up);
    extern BOOL HTUserProfile_setTimezone (HTUserProfile * up, time_t timezone);

        void *      context;                             /* Application specific */
    extern void * HTUserProfile_context (HTUserProfile * up);
    extern BOOL HTUserProfile_setContext (HTUserProfile * up, void * context);
    };
    </code>

    ===================================================================
    ??? libwww 如何处理 HTLib?
    >>> 注意,实际上 HTLibInit 干的事儿很少,HTLibTerminate 倒是干的很多。
    <code "HTLib.h" impl="HTLib.c">
    extern BOOL HTLibInit (const char * AppName, const char * AppVersion);
    extern BOOL HTLibTerminate (void);
    </code>

    <code "HTLib.c">
    PRIVATE char * HTAppName = NULL;          /* Application name: please supply */
    extern const char * HTLib_appName (void);
    extern BOOL HTLib_setAppName (const char * name);

    PRIVATE char * HTAppVersion = NULL;    /* Application version: please supply */
    extern const char * HTLib_appVersion (void);
    extern BOOL HTLib_setAppVersion (const char * version);

    PRIVATE char * HTLibName = "libwww";
    extern const char * HTLib_name (void);

    PRIVATE char * HTLibVersion = W3C_VERSION;
    extern const char * HTLib_version (void);

    PRIVATE BOOL   HTSecure = NO;            /* Can we access local file system? */
    extern BOOL HTLib_secure (void);
    extern void HTLib_setSecure (BOOL mode);

    PRIVATE BOOL   initialized = NO;
    extern BOOL HTLib_isInitialized (void);

    PRIVATE HTUserProfile * UserProfile = NULL;          /* Default user profile */
    extern HTUserProfile * HTLib_userProfile (void);
    extern BOOL HTLib_setUserProfile (HTUserProfile * up);

    PUBLIC BOOL HTLibInit (const char * AppName, const char * AppVersion)
    {
        HTTRACE(CORE_TRACE, "WWWLibInit.. INITIALIZING LIBRARY OF COMMON CODE\n");

        /* Set the application name and version */
        HTLib_setAppName(AppName);
        HTLib_setAppVersion(AppVersion);

        /* Initialize the timezone */
    #ifdef HAVE_TZSET
        tzset();
    #endif

        /* Create a default user profile and initialize it */
        UserProfile = HTUserProfile_new(HT_DEFAULT_USER, NULL);
        HTUserProfile_localize(UserProfile);

    #ifdef WWWLIB_SIG
        /* On Solaris (and others?) we get a BROKEN PIPE signal when connecting
        ** to a port where we should get `connection refused'. We ignore this
        ** using the following function call
        */
        HTSetSignal();                                 /* Set signals in library */
    #endif

        initialized = YES;
        return YES;
    }

    PUBLIC BOOL HTLibTerminate (void)
    {
        HTTRACE(CORE_TRACE, "WWWLibTerm.. Cleaning up LIBRARY OF COMMON CODE\n");

        /* 这八个单元是有全局结构的,应该对它们的初始化予以关注 */
        HTNet_killAll();
        HTHost_deleteAll();         /* Delete remaining hosts */
        HTChannel_deleteAll();                      /* Delete remaining channels */
        HTAtom_deleteAll();                                  /* Remove the atoms */
        HTDNS_deleteAll();                          /* Remove the DNS host cache */
        HTAnchor_deleteAll(NULL);           /* Delete anchors and drop hyperdocs */
        HTProtocol_deleteAll();  /* Remove bindings between access and protocols */
        HTUTree_deleteAll();                             /* Delete all URL Trees */

        HT_FREE(HTAppName);         /* Freed thanks to Wade Ogden <wade@ebt.com> */
        HT_FREE(HTAppVersion);
        HTUserProfile_delete(UserProfile);      /* Free our default User profile */

        initialized = NO;
        return YES;
    }
    </code>

    ===================================================================
    ??? libwww 如何处理 HTRequest?
    >>> HTRequest 涉及三个文件,HTReq.h HTReqMan.h HTReqMan.c。
    <code "HTReqMan.h" impl="HTReqMan.c">
    struct _HTRequest {
        BOOL                internal;      /* Does the app knows about this one? */
    extern BOOL HTRequest_setInternal (HTRequest * request, BOOL mode);
    extern BOOL HTRequest_internal (HTRequest * request);

        time_t              date;      /* Time stamp when the request was issued */
    extern time_t HTRequest_date  (HTRequest * request);
    extern BOOL HTRequest_setDate (HTRequest * request, time_t date);

        HTMethod            method;
    extern void HTRequest_setMethod (HTRequest *request, HTMethod method);
    extern HTMethod HTRequest_method (HTRequest *request);

        BOOL                flush;                /* Should we flush immediately */
    extern BOOL HTRequest_setFlush (HTRequest * me, BOOL mode);
    extern BOOL HTRequest_flush (HTRequest * me);

        HTPriority          priority;               /* Priority for this request */
    extern HTPriority HTRequest_priority (HTRequest * request);
    extern BOOL HTRequest_setPriority (HTRequest * request, HTPriority priority);

    #ifdef HT_EXT
        char *             messageBody;
    extern BOOL HTRequest_setMessageBody (HTRequest * request, const char * body);
    extern BOOL HTRequest_deleteMessageBody (HTRequest * request);
    extern char * HTRequest_messageBody (HTRequest * request);

        long int           messageBodyLength;
    extern BOOL HTRequest_setMessageBodyLength (HTRequest * request, long int len
    gth);
    extern long int HTRequest_messageBodyLength (HTRequest * request);

        HTFormat           messageBodyFormat;
    extern BOOL HTRequest_setMessageBodyFormat (HTRequest * request, HTFormat for
    mat);
    extern HTFormat HTRequest_messageBodyFormat (HTRequest * request);
    #endif
        HTUserProfile *     userprofile;
    extern BOOL HTRequest_setUserProfile (HTRequest * request, HTUserProfile * up);
    extern HTUserProfile * HTRequest_userProfile (HTRequest * request);

        HTNet *             net;                /* Information about socket etc. */
    extern HTNet * HTRequest_net (HTRequest * request);
    extern BOOL HTRequest_setNet (HTRequest * request, HTNet * net);

        HTResponse *        response;
    extern HTResponse * HTRequest_response (HTRequest * request);
    extern BOOL HTRequest_setResponse (HTRequest * request, HTResponse * response);

        HTList *            error_stack;                       /* List of errors */
    extern HTList * HTRequest_error (HTRequest * request);
    extern void HTRequest_setError  (HTRequest * request, HTList * list);
    extern void HTRequest_deleteAllErrors (HTRequest * request);
    extern BOOL HTRequest_addError (HTRequest *     request,
                                    HTSeverity      severity,
                                    BOOL            ignore,
                                    int             element,
                                    void *          par,
                                    unsigned int    length,
                                    char *          where);
    extern BOOL HTRequest_addSystemError (HTRequest *       request,
                                          HTSeverity        severity,
                                          int               errornumber,
                                          BOOL              ignore,
                                          char *            syscall);

        int                 retrys;   &n