2012年4月1日星期日

[转]PHP中路径问题的一个终极解决方案(require include路径方案)

引言:
关于PERL与PHP中的包含路径一直是一个比较难解的问题,主要是与操作系统和WEB服务器有关,不可能非常智能化的解决这个路径问题。相对于 PERL,PHP的路径好得多,解决起来也容易得多,因为PHP的相对路径在PHP程序中的任何场合都可以使用,而不象PERL在某些语句中必须用绝对路 径而导致移植的极其复杂。

基于此,在PHP中,我设计了一个绝对稳妥的解决方案,如下所述。

原则:
使用相对路径,但在相对路径中用绝对路径(有点绕,待会儿详解),一是可以保证可移植性,二是可以方便地修改,三是公式化且结构清晰明了,易于扩展。

步骤详解:
1、先确定好一个程序的根目录,注意是文件系统下的,不是WEB服务器下的虚拟目录,不过一般情况下该目录下的子目录的相对路径与URL下该目录的虚拟子目录是相同的。
2、在定义好的程序根目录下的每个子目录下(其实不一定是每个,根据需要)建立一个settings.php,里面定义一个变量或是常数(常数比较好,因 为作用域比较大),如APPROOT,但这个APPROOT却不是绝对路径,而是该目录相对于你指定的程序根目录的相对路径。
3、在此目录下的所有程序入口文件(也即第一个包含其它文件的文件,或是允许直接在浏览器中浏览的文件)中第一句写上 require_once('settings.php');,但要注意,所有被包含文件最好不要加此句――其实加上也可以,因为你可以在 settings.php中写上 if(!defined(APPROOT)) define(APPROOT, '../..');这类的语句以防重定义。
4、如果你要包含其它文件,无论是直接还是间接地包含,都可以写成 include(APPROOT.$path);,这里$path为被包含文件相对于你所指定的程序根目录的绝对路径。

原理:
定下的程序根目录是相对路径,但具体的目录位置是相对于那个根目录的绝对路径,两者组合起来就是具体文件相对于程序根目录的相对路径了。例如目录 c:\wwwroot\app为你指定的程序根目录,然后有这么两个文件c:\wwwroot\app\a\index.php和c:\wwwroot \app\b\inc.php。对子目录a来说,APPROOT是'..',而对程序根目录来说,inc.php的绝对路径是$path='/b /inc.php',两者组合为'../b/inc.php'。如果要在index.php中包含inc.php就要写成include('../b /inc.php');,而这个路径不就正好是刚才组合而成的APPROOT.$path吗?


结论:
经过以上处理,各个路径绝对整齐划一,唯一罗嗦一点的就是每个目录下要定义一下这个APPROOT,但每个目录下只需在本目录的settings.php 中定义一次就足够了。如果你整个程序只有一个入口文件,如index.php,而其它文件全部都是直接或是间接地被包含进这个唯一的入口文件的话,就只需 在index.php的所在目录下的settings.php中定义一次就OK了。如果有朋友做过Delphi的工程并对工程文件研究过的话,就会发现我 刚才说的一个程序只有一个主入口文件的情况与Delphi的工程十分相似,因为Delphi除了一个主程序文件(dpr文件),其余的全部是单元文件或是 资源文件,都不能独立执行。在PHP中,如果这种情况出现,只需定义一次APPROOT,并在主程序文件中第一句话写成 require_once('settings.php');,而以后所有的包含全都可以用include(APPROOT.$path);,就保证不会 有任何问题,除非你不会写这个"包含文件相对于程序根目录的绝对路径"$path。

这个方法我用了不止一次,收效很好。另外还可以参考JSP的WEB-INFO中路径的定义方式。

我这个是以不变应万变的公式化的方案,如果有朋友有更好的方案,欢迎提出讨论!如有不明白的也欢迎提出。

摘自:http://apps.hi.baidu.com/share/detail/48724853

2012年2月4日星期六

【转】SQLiteSecure �SQLite的加密扩展方法

SQLiteSecure –SQLite的加密扩展方法

这篇文章专门介绍如何扩展当前正越来越流行SQLite的数据库的加密部分, SQLite"一个无须任何配置部署的嵌入式SQL数据库." 下面是本篇文章的目录:

          背景

          本扩展模块的技术说明书

          安装和使用

          下载

          警告和版权限制

          鸣谢

          相关法律说明

背景

我为什么要写这个扩展? 为了满足我自己的一些疯狂的想法. :) 好了,不开玩笑了. 不久前我想写一个程序用来存储一些私人信息 (我自己的一个项目). 我不想用那些很大的开源数据库,例如MySQL,因为它们确实是太大了,需要占用很大的空间,而且要单独的安装部署. 后来我发现了 SQLite, 它非常的小巧而且运行起来很快,并且它的API函数十分的简单,在我的C++程序当中可以很方便的使用. 就是有一个问题,大概是由于它要保持简单所以它不支持任何的验证和加密. 这令我一点安全感都没有.

因此我开始寻找SQLite的加密解决方案. 我找到了两个,但是它们都是商业软件. SQLite的作者, D. Richard Hipp先生提供了一个可以对数据库文件进行完全加密SQLite的加强版本. 也就是那个叫做SQLcrypt(tm)的商业软件, 它实现了数据存储层的透明加密. 不幸的是它们对于我们这些普通人来说都太贵了, 尤其是对于我这种非商业目的只是想用SQLite开发一些小应用自己玩的人来说更是如此. 然而我却是非常的需要对数据库进行加密- 在数据库层对整个数据库文件进行透明的加密. 所有的开发者和用户只需要在打开数据库的时候提供密码就可以了. 然后接下来的事情就全都交给数据库去做了. 这种方式将比那种在数据和字段上的加密要容易的多,也方便的多, 不然需要加密的字段就都要设计成BLOB或者string类型了.

在我搜索了SQLite的邮件列表和在Google上搜索免费的SQLite插件或扩展之后,我发现没有能够满足我的需求的, 所以我决定自己写一个. 我从SQLite的作者预留的一些用来支持数据库加密解密的API接口获得了灵感,并且我发现实际上也有人写了一个基于SQLite的加密库(SQLcrypt). 我花费了几天的时间来研究一些加密的算法,我要选出一种使用(The AES (Rijndael) block cipher) ,另外还有就是如何生成密码, 当然最重要的就是我怎样才能把我用来完成加密解密数据库的代码嵌入SQLite的核心当中执行.

扩展模块的技术说明书

这个扩展模块的结构相当的简单. 大体上来说我编写了SQLite代码中已经提供了原型的四个函数: sqlite3_key(), sqlite3_rekey(), sqlite3CodecGetKey(), sqlite3CodecAttach(). 前两个函数是在sqlite3.h头文件中定义的公共API. 另外两个定义在 attach.c sqlite3Attach()函数中. 我阅读了它的源代码中提供的函数原形(主要是 pager.c btree.c两个文件),看一下它们是如何工作的. 我发现实现他们并不困难,因为对数据库加密解密的机制已经都实现了(感谢 Richard!).

这个扩展的其他部分就是写一个用来加密和解密的程序. 为此我使用了AES加密算法,其中代码的关键部分取自Brian Gladman (his site) 还有David Ireland的高精度加密算法库BigDigits. 因为我不想在一开始的时候就跟SQLite的数据库格式过多的纠缠, 所以我用了计数器模式(CTR)AES加密块转换成256字节的加密流. 这样加密之后的密文就可以和原来的明文有相同的长度,加密和没加密的数据库文件尺寸相同 (也就是说不需要保存而外的信息). 我还使用了Brian Gladman网站上提供的 PKCS#5签名的SHA256算法从用户提供的密码中来生成AES 算法的密钥. 如果是这样的话, 我就要写很多的代码来处理salt value 才能避免往数据库中存入额外的信息.

Pager
结构体用来存放指向编码函数的指针, 用来从数据库中加密或解密数据. 我写了这些代码用来加密或解密数据库中的记录. 因为我使用了计数器(CTR)模式,加密和解密用的是相同的算法,所以简单了许多. 但是我还是需要初始化一个进程的计数器. 我把数据库切分成了一个一个的块,每个都作为一个单独的AES加密块 (例如默认的情况下是16字节). 每个块都从0开始编号. 依据传入编码函数的page sizepage number参数, 我算出计数块内的计数值 (等于块的编号) 和偏移量 (具体实现请看代码),然后用它来初始化加密流.

安装和使用

警告: 目前的代码还在试验当中. 因此我很希望大家来帮我测试这些代码 (因为我没有那么多的时间和数据去进行测试), 使用的风险需要你自己去承担.

如果要使用这个扩展模块你需要下载修改过的SQLiteBigDigits库,然后自己编译它们. 我只是在Win32系统下用MinGW compiler编译并测试过. 但是我认为它在*nix系统下也能正常工作. 如果你要使用MinGW, 你需要下载最新版MinGW的和M-sys . 我是用gcc 3.4.2编译的.

BigDigits
高精度算法库可以从这里下载, 我自己写了一个Makefile 文件用MinGW把它编译成了一个静态的库文件. 下载包里有编译好的BigDigits库文件. (bigdigits.h libbdmpa.a) 如果你想要自己编译,请遵循以下步骤:

1.       从上面的地址下载源代码.

2.       解压到一个目录里.

3.       下载并保存'Makelib.mak'文件到刚才的目录.

4.       修改头文件中最开始的一些 #define'stypedef's使之适合你的操作系统.

5.       在命令行中输入'make -f Makelib.mak'编译源代码.

6.       把编译出来的libbdmpa.a文件和bigdigits.h头文件拷贝到SQLite的顶层目录下.

对于Linux/UnixMinGW的用户, 你只需要按照通常的步骤去做就可以

./configure

make

make install

 

SQLiteSecure编译了(包括配置和安装). 编译出来的库文件和命令行可执行文件和原始的SQLite基本相同,除了前面加上了'sec'前缀,从而避免和你之前使用的SQLite命名冲突.

注意: **** 不要问我如何用VC++或者其他编译其编译源代码. 以为我不用,所以不知道.

你可以用的命令行sqlite3sec工具来先体验一下SQLiteSecure. 打开一个普通的数据库文件, 输入

$ sqlite3sec a.db

 

打开一个加密的数据库文件, 输入

$ sqlite3sec -key "your passphrase" b.db

 

sqlite3sec中你可以使用下列三种方法来添加一个加密的数据库:

sqlite> ATTACH 'b.db' AS b;

sqlite> ATTACH 'b.db' AS b KEY 'your passphrase';

sqlite> ATTACH 'b.db' AS b KEY blob;

 

第一种方法使用和主数据库相同的密码(或者是没有密码) , 第二种方法用你输入的短语来做密码. 第三种方法假设你用BLOB的十六进制值作为密码(例如 f03d69ac3981...). 不过我还没有充分的测试这个使用BLOB作为密码的版本. 请注意:如果你的主数据库是加密的,然后你想添加一个不加密的数据库,这种情况下你需要用第二种方法然后传递一个空字符串('')作为密码.

sqlite3sec中你还可以用.rekey命令来改变数据库的密码,但是这个我目前还没有写完,如果调用的话会返回一个错误. ;)

API
方法, 如果你需要加密一个数据库,你仅仅需要从用户或者其他的地方得到设置的密码, 然后在sqlite3_open() 或者 sqlite3_open16()函数之后,在调用其他的sqlite3函数之前调用sqlite3_key()函数。

 

函数举例:

sqlite3 *db;

...

sqlite3_open(DbFilename, &db);

sqlite3_key(db, zKey, strlen(zKey));

...

之前提到过re-key函数目前还不能工作,所以如果你调用了sqlite3_rekey()将会直接返回一个错误.

下载

下载包中包含了SQLiteSecure的源代码和编译好的BigDigits库文件. 最省事的方法就是不改任何东西,直接解压缩以后先运行./configure, 然后运行make. 如果Makefile文件有问题请告诉我一下. 但是除了GNU编译器(MinGW 或者 Linux)之外我无法提供其他系统的技术支持.

SQLiteSecure source in .tar.gz format
SQLiteSecure source in ZIP format

警告和限制

          我没有用tcl测试过这个库.

          我没有在实际的数据或者程序中测试这个库,也没有用一些特殊用例来测试.

          因为这个版本还没有实现更改密码. 所以如果你想要更改密码,你需要导出加密的数据库到一个新的数据库当中(没有密码或者设置新的密码):

$ sqlite3sec -key "cur pwd" a.db .dump | sqlite3sec b.db

或者

$ sqlite3sec -key "cur pwd" a.db .dump | sqlite3sec -key "new pwd" b.db

          我要声明我并不是一个加密学的专家, 所以在设计和实现加密层的时候难免会存在安全漏洞. 因此大家一定要明白我设计的这个扩展模块只是打算用在握自己的个人工程当中,对于安全性的要求远低于一般的商业产品的标准.

          最后你一定要记住它还不是一个完整的版本,它还在测试当中。

具体的实现请阅读src/sec_ext目录下的代码. 你可以用我名字的大写缩写(LWL)来搜索我对于原始代码的改动. 如果你对代码的实现有任何的意见或建议欢迎你反馈给我. 当然它确实还有很大的提升空间.

鸣谢

向以下人致谢:

          D Richard Hipp for his SQLite library

          Brian Gladman for his AES and key derivation code

          David Ireland for his BigDigits multiple-precision arithmetic library

          Everyone who helped test and gave feedback, comments, and suggestions

法律声明

为了避免其他和我有同样需求的人重新发明轮子,我最终把代码开源了. 但是我也说过了,这个扩展模块目前还是试验阶段,你可以使用或者修改代码来适合你的程序, 但是请在源代码中保留我的名字,另外在你的程序或者文档中也请注明我的付出.

但是如果你非不遵守这些约定,我也不会以法律的形式去起诉你,不会做任何对你本人,你的硬件产品,客户端程序,公司,个人名誉或者其他任何和使用这个软件相关的东西做有害的事情. 你也不是非要使用它不可, 你用了这个试验性的扩展模块就要考虑到它可能有bug,我对于这些bug造成的损失不负任何的责任. 源代码已经提供给你浏览和审查了,我只能是担保我没有故意写任何破坏你的系统的代码. 所以如果你编译运行这个代码,一切风险自负.

欢迎大家提出建议和意见来帮助我改进代码,同时也可以发送给我代码的修正或者片段. 我把这当作是一个学习的机会,所以我很期待好的建议或反馈.

我认为我的代码没有任何地方违反了别人的版权或协议. 如果你发现有请通知我,我将去掉那些违反的地方.

摘自:http://www.sqlite.com.cn/MySqlite/3/348.Html

【转】给Sqlite数据库设置密码

摘自:http://blog.knowsky.com/193467.htm

免费版Sqlie是不提供设置密码功能的,经过查阅资料最终找到了解决方案

方案一,从sqlite源码入手,据说sqlite源码已经提供了加密的接口,只是免费版没有实现,可以参考这位仁兄的博客以了解详情:http://www.cnblogs.com/hiloves/archive/2010/04/25/1719749.html

方案二,使用System.Data.SQLite这个ADO.Net驱动写好的方法来修改密码,详情请参见这位仁兄的博客:http://www.watch-life.net/net-tip/sqlite-encrypted.html

System.Data.SQLite中的SQLiteConnection类提供了一个ChangePassword的方法,这个方法并非来自其父类DbConnection,而是在SQLiteConnection类中定义的。不禁让人感叹,这个开源驱动做的真不错。

我写了个Winform程序以方便修改密码:

clip_image002

主要功能类代码如下:

/* * author:Joey Zhao * date:2010-12-28 22:04:46 */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SQLite; namespace SetSqlitePassword { class DbOperator { private SQLiteConnection _con; /// <summary> /// 文件路径 /// </summary> public string DbFilePath { get; set; } /// <summary> /// 旧密码 /// </summary> public string OriginalPassword { get; set; } /// <summary> /// 修改密码 /// </summary> /// <param name="newPassword">新密码</param> public void ChangePassword(string newPassword) { _con = new SQLiteConnection(); _con.ConnectionString = "Data Source=" + this.DbFilePath; if (this.OriginalPassword.Length > 0) { _con.ConnectionString += ";Password=" + this.OriginalPassword; } try { _con.Open(); } catch (Exception ex) { throw new Exception("无法连接到数据库!"+ ex.Message); } _con.ChangePassword(newPassword); _con.Close(); } } }

点击下载程序

作者: 会长 发表于 2010-12-30 21:05 原文链接

【转】Sqlite数据库的加密

最近在做一个winform的程序,考虑用Sqlite的数据库,小巧而实用,比Access强多了,不过需要加密,不过free版本没有实现加密,有一些c++的实现:比如:http://www.sqlite.com.cn/MySqlite/3/253.Htmlhttp://www.cppblog.com/niewenlong/archive/2007/06/01/25261.html 。不过,鉴于我对c++不是很精通,于是就采用了ADO.NET 2.0 SQLite Data Provider 这样可以直接利用它来创建一个加密的sqlite数据库
有关c#代码如下:
1、创建空的sqlite数据库。

//数据库名的后缀你可以直接指定,甚至没有后缀都可以
//方法一:创建一个空sqlite数据库,用IO的方式
FileStream fs = File.Create(c:\\test.db);
//方法二:用SQLiteConnection
SQLiteConnection.CreateFile(c:\\test.db);

创建的数据库是个0字节的文件。

2、创建加密的空sqlite数据库

//创建一个密码为password的空的sqlite数据库
SQLiteConnection.CreateFile(c:\\test2.db);                
SQLiteConnection cnn 
= new SQLiteConnection(Data Source=c:\\test2.db);
 SQLiteConnection cnn 
= new SQLiteConnection(Data Source=D:\\test2.db);
cnn.Open();
cnn.ChangePassword(
password);

3、给未加密的数据库加密

SQLiteConnection cnn = new SQLiteConnection(Data Source=c:\\test.db);
cnn.Open();
cnn.ChangePassword(
password);

4、打开加密sqlite数据库

//方法一
SQLiteConnection cnn = new SQLiteConnection(Data Source=c:\\test2.db);
cnn.SetPassword(
password);
cnn.Open();
//方法二
SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
 builder.DataSource 
= @”c:\test.db;
builder.Password 
= @”password;
SQLiteConnection cnn 
= new SQLiteConnection(builder.ConnectionString);
cnn .Open();

注:
A、因为加密的函数是利用windows api,故加密后的数据库只能适用在windows平台,加密的方式是整体文件加密。
B、加密的算法是RC4,如果你想采用别的加密算法来加密,请参考ADO.NET 2.0 SQLite Data Provider 的源码来修改。
c、相关sqlite数据库操作类似ADO.NET 2.0。详见ADO.NET 2.0 SQLite Data Provider的帮助文档。
c、ADO.NET 2.0 SQLite Data Provider 版本为:1.0.53.0 ,SQLite版本 : 3.6.0。
d、开发环境为vs2008。

ADO.NET 2.0 SQLite Data Provider的下载地址:http://sourceforge.net/project/showfiles.php?group_id=132486&package_id=145568

有关sqlite的中文介绍,你可以看看这里:http://www.cnblogs.com/shanyou/archive/2007/01/08/615245.html

摘自:http://www.watch-life.net/net-tip/sqlite-encrypted.html

2012年1月17日星期二

【转】sqlite fts3自定义分词器

     sqlite3通过使用fts3虚表支持全文搜索,默认支持simple和porter两种分词器,并提供了接口来自定义分词器。这里我们利用mmseg来构造自定义的中文分词器。
      虽然sqlite在fts3_tokenizer.h中提供了各种接口供用户自定义分词器,但其并未提供c函数供用户来注册自定义的分词器,分词器的注册必须使用sql语句来完成。
    SELECT fts3_tokenizer(<tokenizer-name>, <sqlite3_tokenizer_module ptr>);
    其中tokenizer-name是分词器的名称,sqlite3_tokenizer_module ptr只一个指向sqlite3_tokenizer_module结构的指针并且编码为SQL blob。下面是官方给出的注册函数:
int registerTokenizer(
        sqlite3 *db,
        char *zName,
        const sqlite3_tokenizer_module *p
        ){
    int rc;
    sqlite3_stmt *pStmt;
    const char *zSql = "SELECT fts3_tokenizer(?, ?)";
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
    if( rc!=SQLITE_OK ){
        return rc;
    }
    sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
    sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
    sqlite3_step(pStmt);
    return sqlite3_finalize(pStmt);
}

    要想实现自定义的分词器,最关键的时是得到指向sqlite3_tokenizer_module结构的一个指针,sqlite3_tokenizer_module结构体定义如下:
struct sqlite3_tokenizer_module {
int iVersion; //版本号,必须设置为0
int (*xCreate)( //创建虚表时自动调用并创建分词器
    int argc,                          
    const char *const*argv,           
    sqlite3_tokenizer **ppTokenizer   
);
int (*xDestroy)(sqlite3_tokenizer *pTokenizer); //数据库连接关闭时自动调用,用于销毁资源
int (*xOpen)( //插入数据或检索时自动调用以进行分词
    sqlite3_tok enizer *pTokenizer,     
    const char *pInput, int nBytes,    
    sqlite3_tokenizer_cursor **ppCursor
);

int (*xClose)(sqlite3_tokenizer_cursor *pCursor); //分词结果提取完毕后自动调用

int (*xNext)( //逐个提取分词结果
    sqlite3_tokenizer_cursor *pCursor,  
    const char **ppToken, int *pnBytes,
    int *piStartOffset,
    int *piEndOffset,
    int *piPosition
);
};
    有几点需要注意的是:
    1 分词引擎使用sql语句注册意味着每建立一个sqlite连接都必须注册一次分词器,对于需要使用词库的中文分词器来说也意味着巨大的内存消耗。
    2 在检索时分词结果的提取和语义的解析式交替进行的。例如我们搜索"kanif OR sqlite"的时候,引擎先将全部传入到分词器,在调用一次next获取到词 kanif后,在将词sqlite传入到分词器,直到全部解析完毕。
    3 由于中文分词本身的特殊性,例如"北京市"很有可能视为一个完整的词,这样在搜索"北京"的时候就无法获取到结果。如果分词器支持将"北京市"切分为"北 京市"和"北京"或者将十一月切分为"11月"和"十一",那么需注意(*xNext)函数中的piStartOffset和piEndOffset参 数。经测试在插入数据的时候这两个参数无实际用途,但在查询的时候这两个参数决定了下一次的输入串。

附:
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>

#include "fts3_tokenizer.h"
#include "mmseg/mmseg.cpp"

static bool loadDic = true;

typedef struct cus_tokenizer {
sqlite3_tokenizer base;
} cus_tokenizer;

typedef struct cus_tokenizer_cursor {
sqlite3_tokenizer_cursor base;
char *pInput;
int nBytes;
int iToken;
char *pToken;
rmmseg::Algorithm *pAlgor;
} cus_tokenizer_cursor;

void initmmseg(void){
    if(!loadDic)
        return;
    mmseg_load_words("chars.dic");
    mmseg_load_words("words.dic");
    loadDic = False;
}

static int cusCreate(
int argc, const char * const *argv,
sqlite3_tokenizer **ppTokenizer
){
cus_tokenizer *t;
t = (cus_tokenizer *) sqlite3_malloc(sizeof(*t));
if( t==NULL ) return SQLITE_NOMEM;
memset(t, 0, sizeof(*t));
initmmseg();
*ppTokenizer = &t->base;
return SQLITE_OK;
}

static int cusDestroy(sqlite3_tokenizer *pTokenizer){
sqlite3_free(pTokenizer);
return SQLITE_OK;
}

static int cusOpen(
sqlite3_tokenizer *pTokenizer,         /* The tokenizer */
const char *pInput, int nBytes,        /* String to be tokenized */
sqlite3_tokenizer_cursor **ppCursor    /* OUT: Tokenization cursor */
){
cus_tokenizer_cursor *c;
if(pInput == 0){
    nBytes = 0;
}else if(nBytes < 0)
     nBytes = (int)strlen(pInput);

c = (cus_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
if(c == NULL)
      return SQLITE_NOMEM;

c->iToken = c->nBytes = 0;
c->pInput = c->pToken = NULL;
c->pAlgor = mmseg_algor_create(pInput, nBytes);
c->nBytes = nBytes;
*ppCursor = &c->base;
return SQLITE_OK;
}

static int cusClose(sqlite3_tokenizer_cursor *pCursor){
cus_tokenizer_cursor *c = (cus_tokenizer_cursor *) pCursor;
if(c->pInput != NULL){
    sqlite3_free(c->pInput);
}
if(c->pToken != NULL){
    sqlite3_free(c->pToken);
}
if(c->pAlgor != NULL){
    mmseg_algor_destroy(c->pAlgor);
}
c->pInput = c->pToken = NULL;
c->pAlgor = NULL;
sqlite3_free(c);
return SQLITE_OK;
}

/*
1 sqlite只有在插入数据的时候才使用cursor遍历
2 在进行数据查询时,只会进入一次,然后使用piStartOffset与piEndOffset根据原始串重新生成查询串
*/
static int cusNext(
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by cusOpen */
const char **ppToken,               /* OUT: *ppToken is the token text */
int *pnBytes,                       /* OUT: Number of bytes in token */
int *piStartOffset,                 /* OUT: Starting offset of token */
int *piEndOffset,                   /* OUT: Ending offset of token */
int *piPosition                     /* OUT: Position integer of token */
){
cus_tokenizer_cursor *c = (cus_tokenizer_cursor *) pCursor;
cus_tokenizer *t = (cus_tokenizer *) pCursor->pTokenizer;
if(c->pToken != NULL){
    sqlite3_free(c->pToken);
    c->pToken = NULL;
}
struct Token token = mmseg_next_token(c->pAlgor);
if(token.length != 0 ){
    int l = token.length;
    c->pToken = (char *)sqlite3_malloc(l+1);
    if(c->pToken == NULL)
        return SQLITE_NOMEM;
    c->pToken[l] = 0;
    memcpy(c->pToken, token.text, l);
    *ppToken = c->pToken;
    *pnBytes = l;
    *piStartOffset = token.offset;
    *piEndOffset = token.offset + token.length;
    *piPosition = c->iToken++;
    return SQLITE_OK;
}
//一般来说只有插入数据时才会进入到这里
return SQLITE_DONE;
}
static const sqlite3_tokenizer_module cusTokenizerModule = {
0,
cusCreate,
cusDestroy,
cusOpen,
cusClose,
cusNext,
};

int registerTokenizer(
        sqlite3 *db,
        char *zName,
        const sqlite3_tokenizer_module *p
        ){
    int rc;
    sqlite3_stmt *pStmt;
    const char *zSql = "SELECT fts3_tokenizer(?, ?)";
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
    if( rc!=SQLITE_OK ){
        return rc;
    }
    sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
    sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
    sqlite3_step(pStmt);
    return sqlite3_finalize(pStmt);
}

int main(){
    const sqlite3_tokenizer_module *ptr = &cusTokenizerModule;
    sqlite3 *pDB;
    sqlite3_stmt * stmt;
    char * errMsg = NULL;
    const char *zTail;

    int rc = sqlite3_open("test.sqlite3", &pDB);
    if(rc){
        printf("create error. %s\n",sqlite3_errmsg(pDB));
        return rc;
    }
    char token_name[] = "custoken";
    registerTokenizer(pDB, token_name, ptr);

    rc = sqlite3_exec(pDB, "CREATE VIRTUAL TABLE foo USING fts3(tokenize=custoken)", 0, 0, &errMsg);
    if(rc != SQLITE_OK){
        printf("create virtual error, %s\n", errMsg);
    if(rc != SQLITE_OK){
        printf("create virtual error, %s\n", errMsg);
        return rc;
    }
    rc = sqlite3_exec(pDB, "INSERT INTO foo VALUES('\xe5\x8c\x97\xe4\xba\xac\xe5\xb8\x82')", 0, 0, &errMsg);
    if(rc != SQLITE_OK){
        printf("insert value error, %s\n", errMsg);
        return rc;
    }
    int nrow = 0, ncolumn = 0;
    char **azResult; //二维数组存放结果
    sqlite3_get_table(pDB , "SELECT * FROM foo WHERE content MATCH '\xe5\x8c\x97\xe4\xba\xac\xe5\xb8\x82'" , &azResult , &nrow , &ncolumn , &errMsg );
    int i = 0 ;
    printf( "row:%d column=%d \n" , nrow , ncolumn );
    printf( "\nThe result of querying is : \n" );
    for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
          printf( "azResult[%d] = %s\n", i , azResult[i] );
    sqlite3_free_table( azResult );
    sqlite3_close(pDB);
    return 0;
}

摘自:http://hi.baidu.com/xjtukanif/blog/item/8e7a4ea5362abf99d14358e2.html

2012年1月12日星期四

[转载]delphi中register, pascal, cdecl, stdcall, safecall

注: 使用错误,或者在该加的地方没有加,可能会出现"privileged instruction"错误,或者地址访问错误。

常见的调用惯例有register, pascal, cdecl, stdcall, safecall。函数的调用管理决定了参数如何传递给子过程,并从堆栈中退出,以及寄存器在参数传递中的使用,错误和异常的处理。Delphi中默认的调用惯例是register。
1) register和pascal:参数从左向右传递,也就是说最左边的参数最先求值并传入,最右边的参数最后求值和传入。cdecl,stdcall和safecall则按从右向左方向。
2) 对于除cdecl之外的所有调用惯例,函数/过程在返回的时候要把堆栈中的参数退栈。对cdecl惯例,调用者在被调用的过程返回后执行参数退栈操作
3) register调用惯例最多能用3个CPU寄存器来传递参数,而其它调用惯例只能通过堆栈来传递参数
4) safecall调用惯例实现了异常的防火墙。在Windows上实现了跨进程的COM错误通知机制。
5) register调用效率最高,因为它避免了堆栈的创建。Delphi中published属性必须是register。
6) cdecl常用于调用C/C++编写的共享库中的函数;但是,如果要调用外部代码,那么一般要用stdcall和safecall
7) 在Windows上,系统的API都是stdcall和safecall;在其它操作系统上通常用cdecl(注意:stdcall比cdecl效率要高)
8) 在dual-interface(双接口)方法中必须用safecall惯例。
9) pascal惯例是为了向后兼容;near/far/export用于16位Window编程中的函数调用,在32位的应用程序中不发挥作用,仅仅是为了向后兼容。
下表进行了总结:

Calling conventions Parameter order Clean-up Passes parameters in registers?
register Left-to-right Routine Yes
pascal Left-to-right Routine No
cdecl Right-to-left Caller No
stdcall Right-to-left Routine No
safecall Right-to-left Routine No

参考资料:Delphi帮助文档

摘自:http://blog.sina.com.cn/s/blog_9048a0c40100uin9.html

2011年12月16日星期五

【转】jquery的checked以及disabled

下面只提到checked,其实disabled在jquery里的用法和checked是一模一样的

下边两种写法没有任何区别 只是少了些代码而已...

-----------------------------------------------------------
<input id="cb1" type="checkbox" checked />
<input id="cb2" type="checkbox" checked="checked" />

--------------------------------------------------------------

jquery判断checked的三种方法:

.attr('checked'):   //看版本1.6+返回:"checked"或"undefined" ;1.5-返回:true或false
.prop('checked'): //16+:true/false
.is(':checked'):    //所有版本:true/false//别忘记冒号哦

jquery赋值checked的几种写法:

所有的jquery版本都可以这样赋值:

// $("#cb1").attr("checked","checked");
// $("#cb1").attr("checked",true);

jquery1.6+:prop的4种赋值:

// $("#cb1").prop("checked",true);//很简单就不说了哦
// $("#cb1").prop({checked:true}); //map键值对
// $("#cb1").prop("checked",function(){
return true;//函数返回true或false
});

//记得还有这种哦:$("#cb1").prop("checked","checked");

更多参考:http://api.jquery.com/prop/

上代码 大家可以随便测试:(你是懒人么-_-)

jquery1.6以后才支持prop的哦

新建一个text复制内容进去  后缀名改成html

<html>
<head>
<title>测试</title>
<style type="text/css">

</style>
<!--1.62可以修改1.42 1.52 1.7来测试-->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
$(
function(){
//判断checked
       // var a=$("#cb1").attr('checked'); //看版本1.6+返回:"checked"或"undefined" ;1.5-返回:true或false
       // var b=$("#cb1").prop('checked'); //1.6+:true/false
       var c=$("#cb1").is(':checked'); //所有版本:true/false
       // alert(a);
       // alert(b);
alert(c);

//赋值 前两个所有的jquery版本都支持 prop只有jquery1.6+支持
       // $("#cb1").attr("checked","checked");//1.5-
       // $("#cb1").attr("checked",true);//1.5-
       //   $("#cb1").prop("checked","checked");//1.6+(整理的时候把这个忘记啦)
       //    $("#cb1").prop("checked",true);//1.6+
       // $("#cb1").prop({checked:true});//1.6+
      // $("#cb1").prop("checked",function(){
       // return true;//1.6+
       // });
})();

</script>
</head>
<body>
<!--赋值的时候记得去掉checked-->
<input id="cb1" type="checkbox" checked />
<input id="cb2" type="checkbox" checked="checked"/>
</body>
</html>

摘自:http://www.cnblogs.com/0banana0/archive/2011/11/16/2251855.html