//=======================================================================================
// cminixml.c,v 1.23 2003/05/21
//
// Purpose: Implementation of MiniXML Parser and handling.
//=======================================================================================
//***************************************************************************************
// 『版本维护记录』
//
//***************************************************************************************
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <unistd.h>
#include <libgen.h>
#include <time.h>
#include "minixml.h"
#define CPLAssert(a) do {} while (0)
#define EQUALN(a,b,c) (0==strncasecmp(a,b,c))
#define EQUAL(a,b) (0==strcasecmp(a,b))
#define MAX(a,b) ((a>b) ? a : b)
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef int BOOL;
static volatile unsigned int g_error_num = 0;
//***************************************************************************************
// 解释 XML 需要用到的局部结构和类型
//***************************************************************************************
// 短语类型
typedef enum {
TNone, //
TString, // 字符串
TOpen, // <
TClose, // >
TEqual, // 等号
TToken, //
TSlashClose, // '/' 结束符
TQuestionClose, //
TComment, // 注释
TLiteral
} TheTokenType;
// 解释 XML 文件的中间结果
typedef struct {
const char* pszInput; // 输入字符串,等待解释的内容
int nInputOffset; // 管理待解释内容的变量,字符指针
int nInputLine; // 行数
BOOL bInElement; // 在<>之间
TheTokenType eTokenType;
char* pszToken; // 当前短语字符串
int nTokenMaxSize; // 管理短语缓冲区的变量,分配的内存空间
int nTokenSize; // 已使用的
int nStackMaxSize; // 管理解释栈的变量,分配的内存空间
int nStackSize; // 已使用的
CPLXMLNode** papsStack; // 解释栈,中间过程
CPLXMLNode* psFirstNode; // 解释结果
} ParseContext;
//***************************************************************************************
// 本地函数
//***************************************************************************************
//---------------------------------------------------------------------------------------
// 处理 MBCS 字符串时仍可正常工作,但是不能用于处理 Unicode 字符串
//---------------------------------------------------------------------------------------
// 读一个字符
static char ReadChar(ParseContext* psContext)
{
char chReturn;
// 注意 psContext->nInputOffset 同时也增加了
chReturn = psContext->pszInput[psContext->nInputOffset++];
// 判断读出的内容
if( chReturn == '\0' )
// 保持 psContext->nInputOffset 指向最后一个字符
psContext->nInputOffset --;
else if( chReturn == 0xA )
// 新的一行
psContext->nInputLine ++;
return chReturn;
}
// 恢复到读某个字符之前的状态
static void UnreadChar(ParseContext* psContext, char chToUnread)
{
if( chToUnread == '\0' )
{
/* do nothing */
}
else
{ // 只能取消刚读出那个字符的操作
CPLAssert( chToUnread == psContext->pszInput[psContext->nInputOffset-1] );
// 指针前移
psContext->nInputOffset--;
// 统计行数
if( chToUnread == 0xA )
psContext->nInputLine--;
}
}
static void AddToToken(ParseContext* psContext, char chNewChar)
{
if( psContext->pszToken == NULL )
{ // 尚为分配内存,分配 10 个字节
psContext->nTokenMaxSize = 10;
psContext->pszToken = (char*) malloc(psContext->nTokenMaxSize);
}
else if( psContext->nTokenSize >= psContext->nTokenMaxSize-2 )
{ // 空间不够,分配更大的
psContext->nTokenMaxSize *= 2;
psContext->pszToken = (char*) realloc(psContext->pszToken,psContext->nTokenMaxSize);
}
// 在尾部添加 "chNewChar",共2个字符,相当于 strcat()
psContext->pszToken[psContext->nTokenSize++] = chNewChar;
psContext->pszToken[psContext->nTokenSize] = '\0';
}
// 从 psContext 中读取一个短语
static TheTokenType ReadToken(ParseContext* psContext)
{
char chNext;
// 清除原来的内容,并不释放内存
psContext->nTokenSize = 0;
psContext->pszToken[0] = '\0';
// 读取首字符
chNext = ReadChar( psContext );
// 忽略后面的空白符:white-space character(0x09 ~ 0x0D or 0x20)
while( isspace(chNext) )
chNext = ReadChar( psContext );
//-------------------------------------------------------------------------
// 注释
//-------------------------------------------------------------------------
if( chNext == '<' &&
EQUALN(psContext->pszInput+psContext->nInputOffset, "!--", 3) )
{ // 注释的形式为:<!--正文-->
psContext->eTokenType = TComment;
// 忽略 "!--" 这3个字符
ReadChar(psContext);
ReadChar(psContext);
ReadChar(psContext);
// 每读一个字符就需要比较一次
while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"-->",3) &&
(chNext = ReadChar(psContext)) != '\0' )
{ // 将 "-->" 前的内容全部加入短语字符串
AddToToken( psContext, chNext );
}
// 忽略 "-->" 这3个字符
ReadChar(psContext);
ReadChar(psContext);
ReadChar(psContext);
}
//-------------------------------------------------------------------------
// DOCTYPE or other literals
//-------------------------------------------------------------------------
else if( chNext == '<' &&
EQUALN(psContext->pszInput+psContext->nInputOffset, "!DOCTYPE", 8) )
{
int bInQuotes = FALSE;
psContext->eTokenType = TLiteral;
AddToToken( psContext, '<' );
do {
chNext = ReadChar(psContext);
if( chNext == '\0' )
{ // reached end of file without '>'.
// 没有读到 '>' 内容就非法结束了
//CPLError( CE_Failure, CPLE_AppDefined );
g_error_num ++;
break;
}
// 位于“""”之间
if( chNext == '\"' )
bInQuotes = !bInQuotes;
// 结束
if( chNext == '>' && !bInQuotes )
{
AddToToken( psContext, '>' );
break;
}
// 最后得出的短语为:<正文>
AddToToken( psContext, chNext );
} while(1);
}
//-------------------------------------------------------------------------
// Simple single tokens of interest.
//-------------------------------------------------------------------------
else if( chNext == '<' && !psContext->bInElement )
{ // 位于“<>”之间
psContext->eTokenType = TOpen;
psContext->bInElement = TRUE;
}
else if( chNext == '>' && psContext->bInElement )
{
psContext->eTokenType = TClose;
psContext->bInElement = FALSE;
}
else if( chNext == '=' && psContext->bInElement )
{ // 等式
psContext->eTokenType = TEqual;
}
else if( chNext == '\0' )
{ // 没有内容了
psContext->eTokenType = TNone;
}
//-------------------------------------------------------------------------
// Handle the /> token terminator.
// /> 为短语结束符
//-------------------------------------------------------------------------
else if( chNext == '/' &&
psContext->bInElement &&
psContext->pszInput[psContext->nInputOffset] == '>' )
{ // '/' 之后是 '>',忽略 '>'
chNext = ReadChar( psContext );
CPLAssert(chNext == '>');
psContext->eTokenType = TSlashClose;
psContext->bInElement = FALSE;
}
//-------------------------------------------------------------------------
// Handle the ?> token terminator.
//-------------------------------------------------------------------------
else if( chNext == '?' &&
psContext->bInElement &&
psContext->pszInput[psContext->nInputOffset] == '>' )
{
chNext = ReadChar( psContext );
CPLAssert( chNext == '>' );
psContext->eTokenType = TQuestionClose;
psContext->bInElement = FALSE;
}
//-------------------------------------------------------------------------
// Collect a quoted string.
// 收集位于单/双引号之间的内容
//-------------------------------------------------------------------------
else if( psContext->bInElement && chNext == '"' )
{ // 双引号
psContext->eTokenType = TString;
while( (chNext = ReadChar(psContext)) != '"' &&
chNext != '\0' )
{ // 读取双引号之间的内容
AddToToken( psContext, chNext );
}
// 读到文件尾,没有找到 " 的结束对
if( chNext != '"' )
{
psContext->eTokenType = TNone;
// Reached EOF before closing quote.
//CPLError( CE_Failure, CP