0 LDAP_SUCCESS Success
1 LDAP_OPERATIONS_ERROR Operations error
2 LDAP_PROTOCOL_ERROR Protocol error
3 LDAP_TIMELIMIT_EXCEEDED Timelimit exceeded
4 LDAP_SIZELIMIT_EXCEEDED Sizelimit exceeded
5 LDAP_COMPARE_FALSE Compare false
6 LDAP_COMPARE_TRUE Compare true
7 LDAP_STRONG_AUTH_NOT_SUPPORTED Strong authentication not supported
8 LDAP_STRONG_AUTH_REQUIRED Strong authentication required
9 LDAP_PARTIAL_RESULTS Partial results
16 LDAP_NO_SUCH_ATTRIBUTE No such attribute
17 LDAP_UNDEFINED_TYPE Undefined attribute type
18 LDAP_INAPPROPRIATE_MATCHING Inappropriate matching
19 LDAP_CONSTRAINT_VIOLATION Constraint violation
20 LDAP_TYPE_OR_VALUE_EXISTS Type or value exists
21 LDAP_INVALID_SYNTAX Invalid syntax
32 LDAP_NO_SUCH_OBJECT No such object
33 LDAP_ALIAS_PROBLEM Alias problem
34 LDAP_INVALID_DN_SYNTAX Invalid DN syntax
35 LDAP_IS_LEAF Object is a leaf
36 LDAP_ALIAS_DEREF_PROBLEM Alias dereferencing problem
48 LDAP_INAPPROPRIATE_AUTH Inappropriate authentication
49 LDAP_INVALID_CREDENTIALS Invalid credentials
50 LDAP_INSUFFICIENT_ACCESS Insufficient access
51 LDAP_BUSY DSA is busy
52 LDAP_UNAVAILABLE DSA is unavailable
53 LDAP_UNWILLING_TO_PERFORM DSA is unwilling to perform
54 LDAP_LOOP_DETECT Loop detected
64 LDAP_NAMING_VIOLATION Naming violation
65 LDAP_OBJECT_CLASS_VIOLATION Object class violation
66 LDAP_NOT_ALLOWED_ON_NONLEAF Operation not allowed on nonleaf
67 LDAP_NOT_ALLOWED_ON_RDN Operation not allowed on RDN
68 LDAP_ALREADY_EXISTS Already exists
69 LDAP_NO_OBJECT_CLASS_MODS Cannot modify object class
70 LDAP_RESULTS_TOO_LARGE Results too large
80 LDAP_OTHER Unknown error
81 LDAP_SERVER_DOWN Can't contact LDAP server
82 LDAP_LOCAL_ERROR Local error
83 LDAP_ENCODING_ERROR Encoding error
84 LDAP_DECODING_ERROR Decoding error
85 LDAP_TIMEOUT Timed out
86 LDAP_AUTH_UNKNOWN Unknown authentication method
87 LDAP_FILTER_ERROR Bad search filter
88 LDAP_USER_CANCELLED User cancelled operation
89 LDAP_PARAM_ERROR Bad parameter to an ldap routine
90 LDAP_NO_MEMORY Out of memory
面向对象编程(OOP)是我们编程的一项基本技能,PHP4对OOP提供了良好的支持。如何使用OOP的思想来进行PHP的高级编程,对于提高PHP编程能力和规划好Web开发构架都是非常有意义的。下面我们就通过实例来说明使用PHP的OOP进行编程的实际意义和应用方法。

  我们通常在做一个有数据库后台的网站的时候,都会考虑到程序需要适用于不同的应用环境。和其他编程语言有所不同的是,在PHP中,操作数据库的是一系列的具体功能函数(如果你不使用ODBC接口的话)。这样做虽然效率很高,但是封装却不够。如果有一个统一的数据库接口,那么我们就可以不对程序做任何修改而适用于多种数据库,从而使程序的移植性和跨平台能力都大大提高。

  在PHP中要完成OOP,需要进行对象封装,也就是编写类。我们可以通过生成一个新的SQL类实现对数据库的简单封装。例如:

< ?
class SQL
{
var $Driver; //实际操作的数据库驱动子类
var $connection; //共用的数据库连接变量
function DriverRegister($d)
{
if($d!="")
{
$include_path = ini_get("include_path");
$DriverFile = $include_path."/".$d.".php";
//驱动的存放路径必须在PHP.ini文件中设定的INCLUDE_PATH下
if( file_exists( $DriverFile)) //查找驱动是否存在
{
include($DriverFile);
$this->Driver = new $d();
// 根据驱动名称生成相应的数据库驱动类
return true;
}
}
return false; //注册驱动失败
}
function Connect($host,$user,$passwd,$database)//连接数据库的函数
{
$this->Driver->host=$host;
$this->Driver->user=$user;
$this->Driver->passwd=$pas
swd;
$this->Driver->database=$d
atabase;
$this->connection = $this->Driver->Connect();
}
function Close()//关闭数据库函数
{
$this->Driver->close($this->connection);
}
function Query($queryStr)//数据库字符串查询函数
{
return $this->Driver->query($queryStr,$this->connection);
}
function getRows($res)//查找行
{
return $this->Driver->getRows($res);
}
function getRowsNum($res)//取得行号
{
return $this->Driver-> getRowsNum ($res);
}
}
? >

  我们以操作MySQL数据库为例。我们写一个数据库驱动类MySQL,在该类中,我们把有关MySQL数据库操作的函数都做进一步的封装。把包含该类,文件名为MySQL.php的文件放在PHP的系统 include_path下,就可以正常地使用了。注意编写数据库驱动文件时,文件名应和类名保持一致。

< ?
Class MySQL
{
var $host;
var $user;
var $passwd;
var $database;
function MySQL() //利用构造函数实现变量初始化
{
$host = "";
$user = "";
$passwd = "";
$database = "";
}
function Connect()
{
$conn = MySQL_connect($this->host, $this->user,$this->passwd) or
die("Could not connect to $this->host");
MySQL_select_db($this->database,$conn) or
die("Could not switch to database $this->database;");
return $conn;
}
function Close($conn)
{
MySQL_close($conn);
}

function Query($queryStr, $conn)
{
$res =MySQL_query($queryStr, $conn) or
die("Could not query database");
return $res;
}
function getRows($res)
{
$rowno = 0;
$rowno = MySQL_num_rows($res);
if($rowno>0)
{
for($row=0;$row<$rowno;$row++)
{
$rows[$row]=MySQL_fetch_row($res);
}
return $rows;
}
}
function getRowsNum($res)
{
$rowno = 0;
$rowno = mysql_num_rows($res);
return $rowno;
}
}
? >
  同样我们要封装其他的“数据库驱动”到我们的SQL类中,只需要建立相应的类,并以同名命名驱动文件,放到PHP的include目录就可以了。

  完成封装以后,就可以在PHP中按照OOP的思想来实现对数据库的编程了。

< ?
Include(“SQL.php”);
$sql = new SQL; //生成新的Sql对象
if($sql-> DriverRegister(“MySQL”)) //注册数据库驱动
{
$sql->Connect(“localhost”,”root”,””,”test”);
$res=$sql->query(“select * from test”); //返回查询记录集
$rowsnum = $sql->getRowsNum($res);
if($rowsnum > 0)
{
$rows = $sql->getRows($res);
foreach($rows as $row) //循环取出记录集内容
{
foreach($row as $field){
print $field;}
}
}
$sql->Close();
}
? >

  在实际应用中,我们还可以根据实际需求对各种对象类做进一步扩展。在PHP中,还提供了一系列复杂的OOP方法,例如继承,重载,引用,串行化等等。充分调动各种方法并灵活运用,就能够使你的网站更合理和结构化,开发和维护也更容易。
<script language="JavaScript" type="text/JavaScript">
var autoclick_ok=false;
var cncodenumber = 1
function mClk()
{
if(!autoclick_ok && cncodenumber==1)
{
var source=event.srcElement;source.click();
cncodenumber+=1;
}
}
</script>
<font onmouseover=mClk()>
<a href="http://www.cncode.com"
target="_blank"><font color=darkorchid><b>移到这里,就会自动点击!</b></font></a></font>
如果是图片,就用下面的

<script language="JavaScript" type="text/JavaScript">
var autoclick_ok=false;
var cncodenumber = 1
function mClk()
{
if(!autoclick_ok && cncodenumber==1)
{
var source=event.srcElement;source.click();
cncodenumber+=1;
}
}
</script>
<a href="http://www.cncode.com/" onmouseover=mClk()><img src="http://www.cncode.com/logo/cncodelogo.gif"></a>
1. CVS初始化
-------------
(1) 创建CVSROOT根目录
编辑有关的环境变量,加入CVSROOT的定义(比如在 /etc/bashrc 文件中加入下面两行):
CVSROOT=/usr/local/cvsroot
eXPort CVSROOT

然后在相应位置开始创建CVSROOT
$cd /usr/local/
$mkdir cvsroot
$cvs –d /usr/local/cvsroot init

这时就会产生/usr/local/cvsroot/CVSROOT 目录,这下面放着有关CVS的配置文件。同时/usr/local/cvsroot/也作为文件仓库存放所有的文件。
(2) 创建开发项目
如果从头开始一个新的项目,就需要创建一个单独的目录,并把所有要使用的文件做一个有效的组织。而如果在开始使用源文件的目录之前就有了,则只需进入该目录就行了。
$cd /work/tang
$ls cvstest
. .. c/
$cd cvstest
然后,就可以输入源文件目录:
$cvs import –m “Create Source Dir” cvstest/c tang cvstest

这样会生成 $CVSROOT/cvstest/c 目录。 其中 -m 用来指定注释信息,如果后面在命令行不指定注释信息,则会启动缺省编辑器(vi)要求输入注释信息。 tang, cvstest分别标识了厂商和发行标识。

注意,使用import命令会把当前目录下的所有文件和目录(包括子目录)引入到文件仓库中指定模块(目录)下。



Cvs命令行小结
一个项目的首次导入
cvs import -m "write some comments here" project_name vendor_tag release_tag
执行后:会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下
vender_tag: 开发商标记
release_tag: 版本发布标记
项目导出:将代码从CVS库里导出
cvs checkout project_name
cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念,相对于Virvual SourceSafe的check out是cvs update, check in是cvs commit。
CVS的日常使用
=============
注意:第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。
将文件同步到最新的版本
cvs update
不制定文件名,cvs将同步所有子目录下的文件,也可以制定某个文件名/目录进行同步
cvs update file_name
最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成"先同步 后修改"的习惯,和Virvual SourceSafe不同,CVS里没有文件锁定的概念,所有的冲突是在commit之前解决,如果你修改过程中,有其他人修改并commit到了CVS库中,CVS会通知你文件冲突,并自动将冲突部分用
>>>>>> content on cvs server
<<<<<<
content in your file
>>>>>> 标记出来,由你确认冲突内容的取舍。
版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。
确认修改写入到CVS库里
cvs commit -m "write some comments here" file_name
注意:CVS的很多动作都是通过cvs commit进行最后确认并修改的,最好每次只修改一个文件。在确认的前,还需要用户填写修改注释,以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话,cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。
注释的质量很重要:所以不仅必须要写,而且必须写一些比较有意义的内容:以方便其他开发人员能够很好的理解
不好的注释,很难让其他的开发人员快速的理解:比如: -m "bug fixed" 甚至 -m ""
好的注释,甚至可以用中文: -m "在用户注册过程中加入了Email地址校验"
修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个文件某个版本的注释:
cvs admin -m 1.3:"write some comments here" file_name
添加文件
创建好新文件后,比如:touch new_file
cvs add new_file
注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -b选项按2进制文件方式导入,否则有可能出现文件被破坏的情况
比如:
cvs add -b new_file.gif
cvs add -b readme.doc
然后确认修改并注释
cvs ci -m "write some comments here"
删除文件
将某个源文件物理删除后,比如:rm file_name
cvs rm file_name
然后确认修改并注释
cvs ci -m "write some comments here"
以上面前2步合并的方法为:
cvs rm -f file_name
cvs ci -m "why delete file"
注意:很多cvs命令都有缩写形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;
添加目录
cvs add dir_name
查看修改历史
cvs log file_name
cvs history file_name
查看当前文件不同版本的区别
cvs diff -r1.3 -r1.5 file_name
查看当前文件(可能已经修改了)和库中相应文件的区别
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法,具体安装设置请看后面的cvsweb使用
正确的通过CVS恢复旧版本的方法:
如果用cvs update -r1.2 file.name
这个命令是给file.name加一个STICK TAG: "1.2" ,虽然你的本意只是想将它恢复到1.2版本
正确的恢复版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已经加成STICK TAG的话:用cvs update -A 解决
移动文件/文件重命名
cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。
删除/移动目录
最方便的方法是让管理员直接移动,删除CVSROOT里相应目录(因为CVS一个项目下的子目录都是独立的,移动到$CVSROOT目录下都可以作为新的独立项目:好比一颗树,其实砍下任意一枝都能独立存活),对目录进行了修改后,要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。
项目发布导出不带CVS目录的源文件
做开发的时候你可能注意到了,每个开发目录下,CVS都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS目录吧,这个一次性的导出过程使用cvs export命令,不过export只能针对一个TAG或者日期导出,比如:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name
CVS Branch:项目多分支同步开发
=============================
确认版本里程碑:多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定一个阶段里程碑版本号,方便以后按照这个阶段里程碑版本号导出项目,同时也是项目的多个分支开发的基础。
cvs tag release_1_0
开始一个新的里程碑:
cvs commit -r 2 标记所有文件开始进入2.x的开发
注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。
版本分支的建立
在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir
一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,
cvs checkout -r release_1_0_patch
而其他人员仍旧在项目的主干分支2.x上开发
在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs tag release_1_0_patch_1
如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
cvs update -j release_1_0_patch_1
本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了!

vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令。由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方进一步了解它。Vi也是Linux中最基本的文本编辑器,学会它后,您将在Linux的世界里畅行无阻。

1、vi的基本概念
  基本上vi可以分为三种状态,分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:

1) 命令行模式command mode)

  控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode。

2) 插入模式(Insert mode)

  只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。

3) 底行模式(last line mode)

  将文件保存或退出vi,也可以设置编辑环境,如寻找字符串、列出行号……等。

不过一般我们在使用时把vi简化成两个模式,就是将底行模式(last line mode)也算入命令行模式command mode)。

2、vi的基本操作
a) 进入vi

  在系统提示符号输入vi及文件名称后,就进入vi全屏幕编辑画面:

   $ vi myfile


  不过有一点要特别注意,就是您进入vi之后,是处于「命令行模式(command mode)」,您要切换到「插入模式(Insert mode)」才能够输入文字。初次使用vi的人都会想先用上下左右键移动光标,结果电脑一直哔哔叫,把自己气个半死,所以进入vi后,先不要乱动,转换到「插入模式(Insert mode)」再说吧!

b) 切换至插入模式(Insert mode)编辑文件

  在「命令行模式(command mode)」下按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。

c) Insert 的切换

  您目前处于「插入模式(Insert mode)」,您就只能一直输入文字,如果您发现输错了字!想用光标键往回移动,将该字删除,就要先按一下「ESC」键转到「命令行模式(command mode)」再删除文字。

d) 退出vi及保存文件

  在「命令行模式(command mode)」下,按一下「:」冒号键进入「Last line mode」,例如:

: w filename (输入 「w filename」将文章以指定的文件名filename保存)

: wq (输入「wq」,存盘并退出vi)

: q! (输入q!, 不存盘强制退出vi)


3、命令行模式(command mode)功能键
1). 插入模式

  按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;

  按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;

  按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。

2). 从插入模式切换为命令行模式

  按「ESC」键。

3). 移动光标

  vi可以直接用键盘上的光标来上下左右移动,但正规的vi是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格。

  按「ctrl」+「b」:屏幕往“后”移动一页。

  按「ctrl」+「f」:屏幕往“前”移动一页。

  按「ctrl」+「u」:屏幕往“后”移动半页。

  按「ctrl」+「d」:屏幕往“前”移动半页。

  按数字「0」:移到文章的开头。

  按「G」:移动到文章的最后。

  按「$」:移动到光标所在行的“行尾”。

  按「^」:移动到光标所在行的“行首”

  按「w」:光标跳到下个字的开头

  按「e」:光标跳到下个字的字尾

  按「b」:光标回到上个字的开头

  按「#l」:光标移到该行的第#个位置,如:5l,56l。

4). 删除文字

  「x」:每按一次,删除光标所在位置的“后面”一个字符。

  「#x」:例如,「6x」表示删除光标所在位置的“后面”6个字符。

  「X」:大写的X,每按一次,删除光标所在位置的“前面”一个字符。

  「#X」:例如,「20X」表示删除光标所在位置的“前面”20个字符。

  「dd」:删除光标所在行。

  「#dd」:从光标所在行开始删除#行

5). 复制

  「yw」:将光标所在之处到字尾的字符复制到缓冲区中。

  「#yw」:复制#个字到缓冲区

  「yy」:复制光标所在行到缓冲区。

  「#yy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字。

  「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能。

6). 替换

  「r」:替换光标所在处的字符。

  「R」:替换光标所到之处的字符,直到按下「ESC」键为止。

7). 回复上一次操作

  「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回复。

8). 更改

  「cw」:更改光标所在处的字到字尾处

  「c#w」:例如,「c3w」表示更改3个字

9). 跳至指定的行

  「ctrl」+「g」列出光标所在行的行号。

  「#G」:例如,「15G」,表示移动光标至文章的第15行行首。

4、Last line mode下命令简介
  在使用「last line mode」之前,请记住先按「ESC」键确定您已经处于「command mode」下后,再按「:」冒号即可进入「last line mode」。

A) 列出行号

 「set nu」:输入「set nu」后,会在文件中的每一行前面列出行号。

B) 跳到文件中的某一行

 「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行。

C) 查找字符

 「/关键字」:先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止。

 「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往前寻找到您要的关键字为止。

D) 保存文件

 「w」:在冒号输入字母「w」就可以将文件保存起来。

E) 离开vi

 「q」:按「q」就是退出,如果无法离开vi,可以在「q」后跟一个「!」强制离开vi。

 「qw」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件。

5、vi命令列表
1、下表列出命令模式下的一些键的功能:

h
左移光标一个字符

l
右移光标一个字符

k
光标上移一行

j
光标下移一行

^
光标移动至行首

0
数字“0”,光标移至文章的开头

G
光标移至文章的最后

$
光标移动至行尾

Ctrl+f
向前翻屏

Ctrl+b
向后翻屏

Ctrl+d
向前翻半屏

Ctrl+u
向后翻半屏

i
在光标位置前插入字符

a
在光标所在位置的后一个字符开始增加

o
插入新的一行,从行首开始输入

ESC
从输入状态退至命令状态

x
删除光标后面的字符

#x
删除光标后的#个字符

X
(大写X),删除光标前面的字符

#X
删除光标前面的#个字符

dd
删除光标所在的行

#dd
删除从光标所在行数的#行

yw
复制光标所在位置的一个字

#yw
复制光标所在位置的#个字

yy
复制光标所在位置的一行

#yy
复制从光标所在行数的#行

p
粘贴

u
取消操作

cw
更改光标所在位置的一个字

#cw
更改光标所在位置的#个字


2、下表列出行命令模式下的一些指令
w filename
储存正在编辑的文件为filename

wq filename
储存正在编辑的文件为filename,并退出vi

q!
放弃所有修改,退出vi

set nu
显示行号

/或?
查找,在/后输入要查找的内容

n
与/或?一起使用,如果查找的内容不是想要找的关键字,按n或向后(与/联用)或向前(与?联用)继续查找,直到找到为止。


对于第一次用vi,有几点注意要提醒一下:
1、用vi打开文件,是处于「命令行模式(command mode)」,您要切换到「插入模式(Insert mode)」才能够输入文字。切换方法:在「命令行模式(command mode)」下按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。
2、编辑好后,需从插入模式切换为命令行模式才能对文件进行保存,切换方法:按「ESC」键。
3、保存并退出文件:在命令模式下输入:wq即可!(别忘了wq前面的:)

ps: 为了更加方便地用vim编写程序(C),需要编辑文件 .vimrc 保存至目录/home/host_name/ 下,其内容为:
set ai
set ts=4
if !exists("autocommands_loaded")
let autocommands_loaded = 1
augroup C
autocmd BufRead *.c set cindent
augroup END
endif
set sw=4
set cin
set mouse=a
syntax on
查询的
#include "ldap.h"
#include "stdio.h"
int main()
{
LDAP  *ld;
LDAPMessage *res,*e;
int i,version;
char *server;
int *port;
char *dn;
char *a;
BerElement *ptr;
char **vals;
char **ppValue = NULL;
char *sdn;
server="192.168.1.17";
port = 389;
//联接服务器
if( (ld = ldap_open(server, port ))  == NULL )
{
printf("NO CONNECT");
exit( 1 );
}
//设置服务器版本
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,&version);
//绑定服务器
if(ldap_simple_bind_s(ld,"cn=root,dc=starxing,dc=com","secret")!=LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_simple_bind_s" );
exit( 1 );
}
//设置查询的根目录
sdn="dc=starxing,dc=com";
//进行同步查询
if (ldap_search_s(ld,sdn,LDAP_SCOPE_SUBTREE,"(objectclass=*)",NULL,0,&res)
!= LDAP_SUCCESS)
{
ldap_perror(ld,"ldap_search_s");
exit(1);
}
//对条目进行逐条分析
for(e=ldap_first_entry(ld,res);e!=NULL;e=ldap_next_entry(ld,e))
{
//取出DN
dn=ldap_get_dn(ld,e);
printf("dn: %s \n",dn);
ldap_memfree( dn );
//对条目的属性进行逐条分析
for ( a = ldap_first_attribute( ld, e, &ptr );a != NULL;a = ldap_next_attribute( ld, e, ptr ) )
{
printf( "   %s:  ",a );
vals = ldap_get_values( ld, e, a );
for ( i = 0; vals[i] != NULL; i++ ) {
printf(" %s ",vals[i]);
}
printf("\n");
ldap_value_free( vals );
}
printf("\n");
}
ldap_msgfree(res);
ldap_unbind(ld);
}
插入的
#include "ldap.h"
#include "stdio.h"


int main()
{
LDAP  *ld;
char *sdn = "cn=qqq11,dc=starxing,dc=com";//要添加条目的DN
//列出要添加条目各个属性的值
char *cn_values[] = {"qqq11",NULL};
char *sn_values[] = {"qqq11",NULL};
char *userPassword_values[] = {"qqqq",NULL};
char *objectClass_values[] = {"person",NULL};
int  version;
LDAPMod mod2 = {LDAP_MOD_ADD,"cn",cn_values};
LDAPMod mod1 = {LDAP_MOD_ADD,"sn",sn_values};
LDAPMod mod0 = {LDAP_MOD_ADD,"objectClass",objectClass_values};
LDAPMod mod3 = {LDAP_MOD_ADD,"userPassword",userPassword_values};
LDAPMod *lmod[] = {&mod0,&mod1,&mod2,&mod3,NULL};
//联接ldap服务器
if( (ld = ldap_open( "192.168.1.17", 389 ))  == NULL ){
ldap_perror(ld,"ldapopen");
exit( 1 );
return 1;
}
//设置ldap版本
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,&version);
//对ldap服务器进行绑定
if(ldap_simple_bind_s(ld,"cn=root,dc=starxing,dc=com","secret")!=LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_simple_bind_s" );
exit( 1 );
return 1;
}
//进行同步绑定
if(ldap_add_s(ld,sdn,lmod)!=LDAP_SUCCESS){
ldap_perror(ld,"ldap_add_s error");
return( 1 );
}
ldap_unbind(ld);
return( 0 );
}
删除的
#include "ldap.h"
#include "stdio.h"


int main()
{
LDAP  *ld;
int version;
char **ppValue = NULL;
//联接和绑定服务器
if( (ld = ldap_open( "192.168.1.17", 389 ))  == NULL )
exit( 1 );
version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,&version);
if(ldap_simple_bind_s(ld,"cn=root,dc=starxing,dc=com","secret")!=LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_simple_bind_s" );
exit( 1 );
}
//进行删除操作
if( ldap_delete_s(ld,"cn=qqq11,dc=starxing,dc=com") == -1)
{
ldap_perror(ld,"ldap_delete_s");
exit(1);        
}
ldap_unbind(ld);
return 0;
}
修改的
#include "ldap.h"
#include <stdio.h>;

int main()
{
LDAP  *ld;
char *sdn;
//设置要更改的值
char *sn_values[] = {"eeee",NULL};
char *homePhone_values[] = {"12345678",NULL};
int version;

LDAPMod mod1 = {LDAP_MOD_ADD,"homePhome",homePhone_values};
LDAPMod mod2 = {LDAP_MOD_REPLACE,"sn",sn_values};
LDAPMod mod3 = {LDAP_MOD_DELETE,"mail",NULL};
LDAPMod *lmod[4] = {&mod1,&mod2,&mod3,NULL};
//联接服务器和绑定服务器
if( (ld = ldap_open( "192.168.1.17", 389 ))  == NULL )
exit( 1 );
version = LDAP_VERSION3;

ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,&version);
if(ldap_simple_bind_s(ld,"cn=root,dc=starxing,dc=com","secret")!=LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_simple_bind_s" );
exit( 1 );
}
sdn = "cn=qqq11222,dc=starxing,dc=com";
//进行更改
if (ldap_modify_s(ld,sdn,lmod)!=LDAP_SUCCESS) {
ldap_perror( ld, "ldap_modify_s" );
return( 1 );
}
ldap_unbind(ld);
}
#include <stdlib.h>
#include <string.h>
int main()
{
char *sfrom="1234567890";
char *sto=(char*) malloc(10);
memcpy(sto,sfrom,3);
printf(sto);
strncpy(sto,sfrom,3);
printf("\n");
printf(sto);
printf("\n");

FILE *fp=fopen("du.txt","r");
char ln[100];
char *out=(char*) malloc(10);
while(!feof(fp))
{
        fgets(ln,1024,fp);
        memcpy(out,ln,4);
        printf(out);
        printf("\n");

}
return 0;
}
note: look the sentence which have comments
当父类定义了一个函数,而子类也定义了一个同名的常函数,这时就会存在函数覆盖:
******illustrate********
#include <iostream.h>
class test
{
public:
void output()
{
cout<<"this is base"<<endl;
}
};
class member
{
public:
void greet()
{
cout<<"this is a member"<<endl;
}
};
class derive:public test
{
private:
member x;//because member have not parameters, so don't need constructor
public:
void output()
{
cout<<"this is a derive"<<endl;
x.greet();
}
                                                                                
};
int main()
{
derive de;
de.output();
de.test::output(); //if want to invoking the father class's function, must append the ::
return 0;
                                                                                
}
****the answer******
this is a derive
this is a member
this is base

now, we modify above programe, append the constructor
成员类和父类构造器的初始化,初始化父类时,使用的是类名,初始化成员类时,使用的是成员名
#include <iostream.h>
class test
{
private:
int x;
public:
test(int y)
{
x=y;
}
void output()
{
cout<<"this is base"<<endl;
}
};
class member
{
private:
float f1;
char ch;
public:
member(float f,char ch1)
{
f1=f;
ch=ch1;
}
void greet()
{
cout<<"this is a member"<<endl;
}
};
class derive:public test
{
private:
member x;//because member have not parameters, so don't need constructor
public:
derive(int x1,float y,char chr):test(x1),x(y,chr) //member class and parent class's initialize
{}
void output()
{
cout<<"this is a derive"<<endl;
x.greet();

}
                                                                                
};
int main()
{
derive de(45,12.32,'h');
de.output();
de.test::output();
return 0;
                                                                                
}
构造和析构的调用顺序
先是父类中的成员类的构造函数先调用,然后才是父类的构造函数,接下来就是子类,调用顺序相同,而析构相反。如果继承了多个类(多重继承),则按它们在定义派生类时的声明顺序有关。
*******illustrate***********
#include <iostream.h>
class member
{
public:
member()
{
cout<<"invoking the parent member constructor"<<endl;
}
~member()
{
cout<<"deconstruct"<<endl;
}
};
class test
{
private:
int x;
member xy;
public:
test(int y)
{
cout<<"parent constructor"<<endl;
x=y;
}
~test()
{
cout<<"the parent destruct"<<endl;
}
void output()
{
cout<<"this is base"<<endl;
}
};
class member1
{
private:
float f1;
char ch;
public:
member1(float f,char ch1)
{
f1=f;
ch=ch1;
}
void greet()
{
cout<<"this is a member"<<endl;
}
};
class derive:public test
{
private:
member1 x;//because member have not parameters, so don't need constructor
public:
derive(int x1,float y,char chr):test(x1),x(y,chr)
{cout<<"invoking the derive constructor"<<endl;}
~derive()
{
cout<<"the derive destruct "<<endl;
}
void output()
{
cout<<"this is a derive"<<endl;
x.greet();
}
                                                                                
};
int main()
{
derive de(45,12.32,'h');
de.output();
de.test::output();
return 0;
                                                                                
}
*********over*********
mapping up-----向上映射
we can use the above programe, define a new object
append the follow contents in main:
test te=derive(25,12.36,'a'); //this is mapping up
te.output();
注意:只有公有继承才能向上映射,private, protected不能向上映射
多重继承的二义性
例如:
class base1
{
void display()
{
cout<<"base1"<<endl;
}
};
class base2
{
void display()
{
cout<<"base2";
}
};
class derive:public base1,public base2
{
void output()
{
cout<<"the derive class"<<endl;
}
};
int main()
{
derive der;
der.display(); //现在这个语句就会出现二义性,不知调用父类的哪一个常函数,加上作用域就可以了
der.base1::display();
der.base2::display();// now, it can run rightly
return 0;
}
访问共同基类成员时的二义性
class base
{
public:
base()
{
cout<<"base constructor"<<endl;
}
void common()
{
cout<<"base"<<endl;
}
};
class base1:public base
{
public:
base1()
{
cout<<"base1.constructor"<<endl;
}
void display()
{
cout<<"base1"<<endl;
}
};
class base2:public base
{
public:
base2()
{
cout<<"base2.constructor"<<endl;
}
void display()
{
cout<<"base2";
}
};
class derive:public base1,public base2
{
public:
void output()
{
cout<<"the derive class"<<endl;
}
};
int main()
{
derive der;
//der.common(); //现在这个就会出现二义性,因为不知是通过哪个父类调用父类的父类
             //解决办法,仍然是用作用域来解决。只不过会对父类有两次拷贝
der.base1::common();
return 0;}
******the result******
base constructor
base1.constructor
base constructor
base2.constructor
base
从以上结果可以看出,基类的构造函数被调用了两次。
*******虚基类*************
利用上面的程序,只需在继承自共同基类的派生类声明时显示地标明继承方式为虚拟(virtual)继承,其格式为:
<derived name>:<virtual><inherit mode><base name>
******illustrate**********
class base
{
public:
base()
{
cout<<"base constructor"<<endl;
}
void common()
{
cout<<"base"<<endl;
}
};
class base1:virtual public base
{
public:
base1()
{
cout<<"base1.constructor"<<endl;
}
void display()
{
cout<<"base1"<<endl;
}
};
class base2:virtual public base
{
public:
base2()
{
cout<<"base2.constructor"<<endl;
}
void display()
{
cout<<"base2";
}
};
class derive:public base1,public base2
{
public:
void output()
{
cout<<"the derive class"<<endl;
}
};
int main()
{
derive der;
der.common(); //现在这个就不会出现二义性
            
//der.base1::common();
return 0;}
the result:
base constructor
base1.constructor
base constructor
base2.constructor
base
从以上结果可以看出,父类的构造函数只调用了一次
虚基类的初始化
在虚拟继承中,必须在最晚派生类的构造函数中显式地调用虚基类的构造函数,其格式如下:
<the last derive class constructor><parameter list><direct base's constructor><member object constructor><virtual base class constructor>{define new append members }
*******illustrate*******
#include <iostream.h>
class base
{
private:
int i;
public:
base(int x)
{
i=x;
cout<<"base constructor"<<endl;
cout<<i<<endl;
}
int geti() const
{
return i;
}
void common()
{
cout<<"base"<<endl;
}
};
class base1:virtual public base
{
int y;
public:
base1(int w,int z):base(w)
{
y=z;
cout<<"base1.constructor"<<endl;
}
void display()
{
cout<<"base1"<<endl;
}
};
class base2:virtual public base
{private:
int aa;
public:
base2(int a, int b):base(b)
{
aa=a;
cout<<"base2.constructor"<<endl;
}
void display()
{
cout<<"base2";
}
int geti() const
{
return aa;
}
};
class derive:public base1,public base2
{
public:
derive(int a1,int a2,int a3,int a4):base1(a1,a2),base2(a3,a4),base(a4){}
                             //initialize the virtual base class constructor
void output()
{
cout<<"the derive class"<<endl;
}
};
int main()
{
derive der(12,25,34,45);
der.common(); //
             //
//der.base1::common();
cout<<der.geti()<<endl;
return 0;}
http://www.baidu.com/s?ie=gb2312&bs=c%2B%2B+%C0%E0&sr=&z=&cl=3&f=8&wd=sun+directory+server&ct=0

ldap学习笔记:http://linliangyi2007.javaeye.com/blog/167125

安装 Sun ONE Directory Server:  http://publib.boulder.ibm.com/tividd/td/ITAME/SC32-1362-00/zh_CN/HTML/am51_install78.htm
折腾sunONE directory server:  http://www.ningoo.net/html/2007/install_sun_one_directory_server.html
Sun Java System Directory Server Enterprise Edition 6.2:
http://docs.sun.com/app/docs/coll/1224.3?l=zh_TW

ldap_API_C URL:http://www.diybl.com/course/7_databases/database_other/20071019/78240.html
Linux的问题往往不是集中在技术或软件的可获得性问题,而是一个选择的问题。用户图形界面(GUI)的选择就体现了这一点。在Linux领域可以选择的GUI很多,商业可用的也有10种左右。当我们面临着如何为LiPS论坛选择可以成为标准的GUI时,技术偏好就必须让位于更多的公共性的因素。常常提起的是,作者个人与主要的GUI提供者例如QT(TrollTech),MiniGUI(魏老师),mGUI(移软)都有着相当长的个人和业务关系,所以这个选择就显得更加的“人情化”。如果没有一个科学的和公正的立场,那么结果可想而知。

从标准化角度和行业利益最大化的角度来看,在选择GUI之前我们设立3个原则:
1、自由原则,GUI的技术和授权方式必须能够为其使用者提供的自由度,既能够支持商业版本的应用,又要能够支持开源的应用。所以,LGPL,BSD, Mozilla等的授权方式比起GPL或者私有的授权协议来说更符合这个原则。
2、成熟原则,GUI必须通过极限应用的检验,例如如果应用环境中平均应用的代码行数在10万行的话,那么这个GUI必须通过超过100万行代码的单个应用的检验。通过这种检验我们可以直观地获得GUI在功能的丰富程度和代码的可扩展和鲁棒性。
3、适用原则,GUI必须是能够直接或者通过裁减运行在嵌入式环境中的,其API兼容性不应该因为这种裁减发生重大改变的(理想状况下是不变的)。

在这3个原则的指导下,我们对QT,miniGUI,GTK等GUI进行了对比,对比结果就不一一列举了。结论是,GTK更接近我们的原则,它是LGPL 的,大量的桌面应用都是基于GTK的,在手机中已经有了超过500万部的使用纪录。但是,美中不足的是,GTK的定制和裁减都是厂商的个体行为,没有回馈到开源社区。

所以,我们将工作的重点放在了加强GTK对“适用原则”的符合程度。我们通过与开源社区合作,提供了一个GTK的嵌入式补丁,目的是通过更改公用部分的处理,例如键盘和鼠标输入的替换等,在不改变Widget接口的情况在,达到嵌入场景的适应性。并且,不断的推动相关人士,加强对GTK社区的说复。目前已经取得初步的进展,首先GTK的主要维护者认识到了嵌入式的需求和目前现实的差距,以及相关机构和个人进行嵌入化的努力,同意在今后的版本中考虑嵌入式的需求。这是关键一步,迈向理想的“适用”,即嵌入并兼容。
#include <stdio.h>;
#include <stdlib.h>;
#include <sys/wait.h>;
#include <sys/types.h>;

int main()
{
        
        pid_t status ;
        
        errno = 0 ;
        status = system("cp hello.c hello.c.bak") ;
                
        if (status == -1)
                printf("system error!") ;
        
        if (WIFEXITED(status)){
                printf("cp exit normal![%d]\n", errno) ;
                printf("exit staus = [%X]\n", WEXITSTATUS(status)) ;
        }else
                printf("cp exit illegal![%d]\n", errno) ;
}

[/code]

测试了一把.结果如下
如果hello.c存在,cp也成功将hello.c拷贝到了hello.c.bak则打印
cp exit normal![0]
exit status = [0]
如果hello.c不存在,则打印
cp exit normal![0]
exit status = [1]

我的问题是.
1.在何种情况下会打印cp exit illegal!
2.如果是exit normal,那么exit status的值是否和system调用执行的进程紧密相关(如本例中的cp)
3.如果上个问题的回答是肯定的,那我该如何得到关于cp的各种退出状态.cp的手册页里好象没有相关说明.

老调重弹--问system返回值

第二个问题我测试了一下,回答是肯定的.
exit status等于被调用程序的用exit(n)或者return n
给出的值.

第一个问题还是不知道.
我在被调用程序运行过程中将他kill掉.
程序还是打印exit normal!可是status取到的值明显是不对的.
faint

老调重弹--问system返回值

UP

老调重弹--问system返回值

WIFEXITED(status)
              returns true if the child terminated normally, that is, by call-
              ing exit() or _exit(), or by returning from main().

老调重弹--问system返回值

楼上的兄弟谢谢了.
我不明白的正是这个问题.

按照上面的描述,我把被调用的进程kill掉.按照我的理解
WIFEXITED(status)不应该返回真才对.

老调重弹--问system返回值

没有人知道吗?

老调重弹--问system返回值

man里面说-->

不管你有没有hello.c文件。你的system都会正常结束,也就都不会返回-1,区别只是cp动作的结束状态不同。

老调重弹--问system返回值

-->

是的,这个我知道,
我现在想知道的是什么情况下
WIFEXITED(status)不会返回真.
我不明白为什么我把被调用的进程中间KILL掉这个宏的值还是真.
但是WEXITSTATUS(status)的值明显不是被调用进程的返回值.

老调重弹--问system返回值

你怎么kill掉的呢?cp之间你kill它?^_^,我也不太明白。

老调重弹--问system返回值

[code]

#include <stdio.h>;
#include <stdlib.h>;
#include <sys/wait.h>;
#include <sys/types.h>;

int main()
{
    
   pid_t status ;
    
   errno = 0 ;
   status = system("./hi") ;
      
   if (status == -1)
      printf("system error!") ;
    
   if (WIFEXITED(status)){
      printf("process exit normal![%d]\n", errno) ;
      printf("exit staus = [%X]\n", WEXITSTATUS(status)) ;
   }else
      printf("process exit illegal![%d]\n", errno) ;
}

[/code]

[code]
int main()
{
   printf("HI!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") ;
   sleep(20) ;
   return 4 ;
}


[/code]

这样就可以做到在运行中把进程"hi"kill掉了.
如过是正常退出WEXITSTATUS(status)的值是4
如果是KILL掉
WIFEXITED(status)J还是真,但是WEXITSTATUS(status)的值却是86
这个86真让人faint.

  这是一个初学者常问的问题,也是初学者问嵌入式该如何入门的根源。我感觉有两个方面,偏硬和偏软.我不认为嵌入式开发软件占绝对比重,相反,软硬件都懂,才是嵌入式高手所应该追求的,也是高手的必由之路。



  硬件道路:

  第一步: pcb设计,一般为开发板的电路裁减和扩充,由开发板原理图为基础,画出PCB和封装库,设计自己的电路。

  第二步: SOPC技术,一般为FPGA,CPLD开发,利用VHDL等硬件描述语言做专用芯片开发,写出自己的逻辑电路,基于ALTER或XILINUX的FPGA做开发。

  第三步: SOC设计,分前端,后端实现,这是硬件设计的核心技术:芯片设计.能做到这步,已经不属于平凡的技术人员。

  软件道路:

  第一步:bootloader的编写,修改, 通过这步熟悉ARM硬件结构,学习ARM汇编语言,阅读ARM的芯片手册,感觉就是像操作51单片机一样操作ARM芯片.这一步最好的两个参考资料就是:芯片手册和bootloader源代码。

  第二步:系统移植, 驱动开发, 我只做过linux方向,所以也推荐学习嵌入式linux系统,作为标准体系,他开源而且可以获得大量学习资料.操作系统是整个计算机科学的核心,熟悉 kernel实属不易,kernel, 驱动开发的学习,没有什么捷径,只有多读代码,多写代码,熟悉系统API.. understanding linux kernel , linux device driver 都是不可多得的好书,值得一看。

  第三步:应用程序的编写,各种GUI的移植,qt , minigui都被大量采用,两种思想都类似,熟悉一种就可以。

  软件道路中,驱动,系统应该是最深入的部分,不是短时间可以掌握的,需要有勇气和耐心。嵌入式开发,软硬结合,因为硬件条件比PC差很多,所以肯定会遇见不少问题,因此实践的勇气更加重要.有问题就解决问题,无数次的实验,也许是解决问题的必由之路。
原帖地址:http://blog.s135.com/read.php/269.htm
 [文章作者:张宴 本文版本:v1.1 最后修改:2007.07.27 转载请注明出处:http://blog.s135.com]

  这两天搭建了一组Apache服务器,每台服务器4G内存,采用的是prefork模式,一开始设置的连接数太少了,需要较长的时间去响应用户的请求,后来修改了一下Apache 2.0.59的配置文件httpd.conf:

引用
# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves

StartServers         10
MinSpareServers      10
MaxSpareServers      15
ServerLimit          2000
MaxClients           2000
MaxRequestsPerChild  10000



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

  查看httpd进程数(即prefork模式下Apache能够处理的并发请求数):
  Linux命令:

引用
ps -ef | grep httpd | wc -l

  返回结果示例:
  1388
  表示Apache能够处理1388个并发请求,这个值Apache可根据负载情况自动调整,我这组服务器中每台的峰值曾达到过2002。

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

  查看Apache的并发请求数及其TCP连接状态:
  Linux命令:

引用
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

  (这条语句是从新浪互动社区事业部技术总监王老大那儿获得的,非常不错)
  返回结果示例:
  LAST_ACK 5
  SYN_RECV 30
  ESTABLISHED 1597
  FIN_WAIT1 51
  FIN_WAIT2 504
  TIME_WAIT 1057
  其中的SYN_RECV表示正在等待处理的请求数;ESTABLISHED表示正常数据传输状态;TIME_WAIT表示处理完毕,等待超时结束的请求数。

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

  关于TCP状态的变迁,可以从下图形象地看出:
    
  状态:描述
  CLOSED:无连接是活动的或正在进行
  LISTEN:服务器在等待进入呼叫
  SYN_RECV:一个连接请求已经到达,等待确认
  SYN_SENT:应用已经开始,打开一个连接
  ESTABLISHED:正常数据传输状态
  FIN_WAIT1:应用说它已经完成
  FIN_WAIT2:另一边已同意释放
  ITMED_WAIT:等待所有分组死掉
  CLOSING:两边同时尝试关闭
  TIME_WAIT:另一边已初始化一个释放
  LAST_ACK:等待所有分组死掉
      var nameVal = Trim(document.form1.name.value);

        if(nameVal == '')
        {
          alert("ÓʼþÁбíÃû³ÆÊäÈëÎÞЧ£¬ÇëÖØÐÂÊäÈë");
          return;
        }
        if (nameVal.indexOf(" ")>=0)
        {
        alert("ÓʼþÁбíÃû³ÆÖм䲻Äܺ¬Óпոñ£¬ÇëÖØÐÂÊýÊäÈë");
        return;
        }

一个是对前后空格的去掉,一个是对中间空格的去掉。
其indexOf是返回其位置,有则大于0,但是前面已经去掉前后空格,因此,可以说这样的alert很准确。。。
分页: 248/272 第一页 上页 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 下页 最后页 [ 显示模式: 摘要 | 列表 ]