七、对配置文件进行分析
即然配置文件有if语句,那么就需要对其进行分析。由于if语句采用类似于xml的格式,因此无需使用编译原理的相关知识进行分析。在分析之前,需要打开绝对路径所指向的文件,并进行读取等操作。这些功能由四个函数来完成,它们是OpenFile、CloseFile、GetNextChar和PutBackChar。这四个函数的实现如下。
ifstream *p_ifs;
//打开文件,如果成功,返回true,失败,返回false
bool OpenFile(string fn)
{
p_ifs = new ifstream(fn.c_str());
if(p_ifs->is_open())
return true;
else
return false;
}
//关闭被打开的文件
void CloseFile()
{
p_ifs->close();
delete p_ifs;
}
//从这个被打开的文件中得到一个字符
char GetNextChar()
{
char c;
p_ifs->read(&c, 1);
if(p_ifs->eof())
return 0;
else
return c;
}
//向文件流回退一个字符
void PutBackChar(char c)
{
p_ifs->putback(c);
}
本例中将得到最终配置文件内容的功能单独封装在GetFileContent函数中,函数实现如下。
string GetFileContent(string query_original)
{
string content = ""; //通过对if语句的判断,得到最终的文件内容
map<string, string> query;
int state = 0;
char c;
string syntax;
AnalyzeQuery(query_original, query);
while((c = GetNextChar()) != 0)
{
switch(state)
{
case 0:
// 未遇到if语句,直接将当前字符加入到content中
if(c != '<')
content.push_back(c);
else
{
char c1;
c1 = GetNextChar();
if(c1 != '%')
{
content.push_back(c);
content.push_back(c1);
}
else
{
string cmd = GetKeyword();
if(cmd.compare("if") != 0)
{
return "if语句出错!";
}
else
{
state = 1; //转到处理if语句条件的状态
}
}
}
break;
case 1: // 处理if后的条件,得到true或false
syntax = GetSyntax();
if(syntax == "")
return "语法错误!";
else
{
//这个条件满足,转到状态
if(VerifyCondition(syntax, query))
{
state = 2;
}
else //不满足这个条件,转到状态
{
state = 3;
}
}
break;
case 2: //将满足条件的部分加入到content中,在遇到<%endif终止
if(c != '<')
content.push_back(c);
else
{
char c1;
c1 = GetNextChar();
if(c1 != '%')
{
content.push_back(c);
content.push_back(c1);
}
else
{
string cmd = GetKeyword();
if(cmd.compare("endif") == 0)
{
state = 0;
}
else
{
return "if语句错误!";
}
}
}
break;
case 3: //忽略不满足条件的部分
if(c == '<')
{
char c1;
c1 = GetNextChar();
if(c1 == '%')
{
string cmd = GetKeyword();
if(cmd.compare("endif") == 0)
state = 0; //回到开始状态
else
return "if语句错误!";
}
}
break;
}
}
return content;
}
这个函数有一个参数:query_original,表示一个连在一起的查询字符串,如name=xyz&age=16。GetFileContent通过对原始字符串以及配置文件的内容进行扫描,返回最终送到客户端的字符串。
GetFileContent的基本原理是利用一个状态state。当GetNextChar得到一个字符时,判断是否以<%开头,如果不是,则按原样输出,否则,判断是否为if或endif语句,如果一开始遇到的是if语句,就得到它的语法部分,进行解析,判断这个if语句是否为true,如果为true,返回if和endif之间的内容,否则略过if和endif之间的内容,接着扫描endif后的内容。如果if语句没有以endif结尾,那么返回“if语句错误!”信息。
在GetFileContent中有几个函数需要解释一下,其中GetKeyword的功能是得到关键字,即得到if和endif。在调用GetKeyword时,文件流指针已经指向<%的下一个字符。因此调用GetKeyword读到的第一个字符就是if和endif的第一个字符。关键字和后面的语法部分用空格、tab键或%(只有endif是以%结尾的)隔开。GetKeyword的函数实现如下。
string GetKeyword() //得到关键字,有个关键字,if, endif
{
string keyword = "";
char c;
while((c = GetNextChar()) != 0)
{
//命令后可用空格或tab键和后面的语句隔开
if(c != ' ' && c != 9 && c != '%')
{
keyword.push_back(c);
}
else
break;
}
if(c == '%')
GetNextChar();
else if(c == ' ' || c == 9)
PutBackChar(c);
return keyword;
}
和GetKeyword相对应的就是GetSyntax函数,这个函数的功能是得到if语句的语法部分,语法部分以%>结尾。GetSyntax的实现如下。
string GetSyntax() //得到关键字的语法字符串,
{
string syntax = "";
char c;
while((c = GetNextChar()) != 0)
{
if(c != '%')
{
syntax.push_back(c);
}
else
{
char c1;
c1 = GetNextChar();
if(c1 != '>')
{
syntax.push_back(c);
if(c1 != 0)
syntax.push_back(c1);
else
return "";
}
else //语法部分结束,返回相应的语句
break;
}
}
return syntax;
}
在这个ISAPI中最关键的要算VerifyCondition函数了,它的功能是验证if语句后面的条件是true还是false。它的实现代码如下。
bool VerifyCondition(string condition, map<string, string> &query)
{
vector<string> condition_set;
vector<char> relation_set;
bool result;
if(condition == "") return false;
ProcessCondition(condition, condition_set, relation_set);
result = VerifySingleCondition(condition_set[0], query);
for(size_t i = 1, j = 0; i < condition_set.size() && j < relation_set.size(); i++, j++)
{
if(relation_set[j] == '&')
result = result && VerifySingleCondition(condition_set[i], query);
else
result = result || VerifySingleCondition(condition_set[i], query);
}
return result;
}
在处理if语句的条件时首先要将条件字符串分解,完成这个功能的函数是ProcessCondition,它将条件保存在condition_set中,而将它们之间的关系(&代表and,|代表or)放到relation_set中。ProcessCondition的实现代码如下。
void ProcessCondition(string condition, vector<string> &condition_set, vector<char> &relation_set)
{
string s;
int last_pos = -1, new_pos = 0;
while(true)
{
new_pos = Find(condition, "&|", last_pos + 1);
if(new_pos == condition.npos)
{
break;
}
else
{
relation_set.push_back(condition[new_pos]);
last_pos++;
condition_set.push_back(condition.substr(last_pos, new_pos - last_pos));
last_pos = new_pos;
}
}
s = condition.substr(++last_pos);
if(s != "")
{
condition_set.push_back(s);
}
}
condition_set中的每一个值是单独的条件串,型如name=xyz。因此,将判断每一个条件串是否为true的功能单独封装到VerifySingleCondition函数中。然后在VerifyCondition函数中利用relation_set中保存的关系将每个条件串的逻辑值组合起来。VerifySingleCondition函数有两个参数,第一个是条件串,第二个是查询字符串集。如果条件满足查询串,返回true,否则返回false。VerifySingleCondition函数的实现如下。
bool VerifySingleCondition(string condition, map<string, string> &query)
{
int pos = condition.find('=');
if(pos != condition.npos)
{
string q, v;
vector<string> values;
q = Trim(condition.substr(0, pos));
v = condition.substr(pos + 1);
SeparateString(v, ',', values);
if(query.find(q) == query.end()) return false;
for(size_t i = 0; i < values.size(); i++)
{
if(query[q].compare(values[i]) == 0)
return true;
}
}
return false;
}
在编写完以上函数后,在HttpExtensionProc接口函数中利用pECB的lpszPathTranslated属性打开文件后,将lpszQueryString属性的值传入GetFileContent函数中,最后用WriteClient函数将GetFileContent的返回值发送到客户端。