1.plist.h
#ifndef _PLIST_H_
#define _PLIST_H_
enum EToken
{
ET_NONE,
ET_EOF,
ET_DICT_START,
ET_DICT_END,
ET_KEY_START,
ET_KEY_END,
ET_ARRAY_START,
ET_ARRAY_END,
ET_INT_START,
ET_INT_END,
ET_TRUE,
ET_FALSE,
ET_STRING_START,
ET_STRING_END,
ET_REAL_START,
ET_REAL_END,
ET_DATA_START,
ET_DATA_END,
ET_DATE_START,
ET_DATE_END,
ET_ETOKEN_STR,
};
enum EDataType
{
ED_BOOL,
ED_INT,
ED_REAL,
ED_STRING,
ED_DATE,
ED_DATA,
ED_DICT,
ED_ARRAY
};
struct ParseStatus
{
const char *source;
const char *parsePos;
int len;
char *currentTokenStr;
enum EToken currentToken;
};
struct AnyType
{
enum EDataType type;
union {
short b;
int i;
double d;
char t[20+1];
char *s;
char *a;
struct {
char *key;
struct AnyType *value;
struct AnyType *next;
} dict;
struct {
int key;
struct AnyType *value;
struct AnyType *next;
} arry;
} value;
};
struct keytoken
{
const char *value;
enum EToken type;
enum EToken mateToken;
};
struct AnyType *parsePlist(const char *source);
struct AnyType *parsePlistByFileName(char *filename);
#endif
2.plist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include "plist.h"
static struct keytoken keywords[] =
{
{ "<dict>", ET_DICT_START, ET_NONE },
{ "</dict>", ET_DICT_END, ET_DICT_START },
{ "<key>", ET_KEY_START, ET_NONE },
{ "</key>", ET_KEY_END, ET_KEY_START },
{ "<array>", ET_ARRAY_START, ET_NONE },
{ "</array>", ET_ARRAY_END, ET_ARRAY_START },
{ "<integer>", ET_INT_START, ET_NONE },
{ "</integer>", ET_INT_END, ET_INT_START },
{ "<true/>", ET_TRUE, ET_NONE },
{ "<false/>", ET_FALSE, ET_NONE },
{ "<string>", ET_STRING_START, ET_NONE },
{ "</string>", ET_STRING_END, ET_STRING_START },
{ "<real>", ET_REAL_START, ET_NONE },
{ "</real>", ET_REAL_END, ET_REAL_START },
{ "<data>", ET_DATA_START, ET_NONE },
{ "</data>", ET_DATA_END, ET_DATA_START },
{ "<date>", ET_DATE_START, ET_NONE },
{ "</date>", ET_DATE_END, ET_DATE_START }
};
static struct AnyType *parseAny(struct ParseStatus *ps);
static struct keytoken findKeytokenByKeywordType(enum EToken keyword)
{
struct keytoken ret = { "", ET_NONE, ET_NONE };
int len = sizeof(keywords) / sizeof(struct keytoken);
int i = 0;
for(i = 0; i < len; ++i)
{
if(keywords[i].mateToken == keyword)
{
ret = keywords[i];
break;
}
}
return ret;
}
static char *load_file(const char *filename)
{
FILE *fp = NULL;
char *content;
char *buffer = NULL;
int file_size = 0;
fp = fopen(filename, "rb");
if(fp != NULL)
{
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = (char*)malloc(file_size + 1);
fread(buffer, file_size, 1, fp);
buffer[file_size] = 0;
fclose(fp);
content = buffer;
}
return content;
}
static const char *skipSpace(const char *input)
{
while(1)
{
char ch = *input;
if(ch != '\t' && ch != '\n' && ch != ' ' && ch != '\r')
{
break;
}
input++;
}
return input;
}
static void skipSpaceOfPS(struct ParseStatus *ps)
{
if(ps->parsePos < (ps->source + ps->len))
{
ps->parsePos = skipSpace(ps->parsePos);
}
}
static enum EToken getToken(struct ParseStatus *ps)
{
skipSpaceOfPS(ps);
enum EToken ret = ET_ETOKEN_STR;
int len = sizeof(keywords) / sizeof(struct keytoken);
if(ps->parsePos >= (ps->source + ps->len))
{
ret = ET_EOF;
}
else if(ps->parsePos[0] == '<')
{
const char *find = strchr(ps->parsePos, '>');
char tmp[64];
int i = 0;
if(find != NULL)
{
find++;
memset(tmp, 0x00, sizeof(tmp));
assert(find-ps->parsePos < 64);
strncpy(tmp, ps->parsePos, find-ps->parsePos);
ps->parsePos = find;
for(i=0; i<len; i++)
{
if(strcmp(keywords[i].value, tmp) == 0)
{
ret = keywords[i].type;
break;
}
}
}
}
else
{
struct keytoken mateToken = findKeytokenByKeywordType(ps->currentToken);
if(mateToken.mateToken != ET_NONE)
{
const char *find = strstr(ps->parsePos, mateToken.value);
if(find != NULL)
{
len = find-ps->parsePos;
if(ps->currentTokenStr)
{
free(ps->currentTokenStr);
ps->currentTokenStr = NULL;
}
ps->currentTokenStr = (char *)malloc(len+1);
ps->currentTokenStr[len] = 0x00;
strncpy(ps->currentTokenStr, ps->parsePos, find-ps->parsePos);
ps->parsePos = find;
}
}
}
ps->currentToken = ret;
return ret;
}
static void matchToken(struct ParseStatus *ps, enum EToken token)
{
enum EToken now = getToken(ps);
if(now != token)
{
assert(now == token);
}
}
static struct AnyType *parseInt(struct ParseStatus *ps)
{
struct AnyType *ret = (struct AnyType *)malloc(sizeof(struct AnyType));
getToken(ps);
int value = atoi(ps->currentTokenStr);
ret->type = ED_INT;
ret->value.i = value;
matchToken(ps, ET_INT_END);
return ret;
}
static struct AnyType *parseString(struct ParseStatus *ps)
{
struct AnyType *ret = (struct AnyType *)malloc(sizeof(struct AnyType));
char *str = NULL;
int len = 0;
matchToken(ps, ET_ETOKEN_STR);
len = strlen(ps->currentTokenStr);
str = (char *)malloc(len+1);
strcpy(str, ps->currentTokenStr);
ret->type = ED_STRING;
ret->value.s = str;
matchToken(ps, ET_STRING_END);
return ret;
}
static struct AnyType *parseReal(struct ParseStatus *ps)
{
struct AnyType *ret = (struct AnyType *)malloc(sizeof(struct AnyType));
getToken(ps);
ret->type = ED_REAL;
ret->value.d = atof(ps->currentTokenStr);
matchToken(ps, ET_REAL_END);
return ret;
}
static struct AnyType *parseDate(struct ParseStatus *ps)
{
struct AnyType *ret = (struct AnyType *)malloc(sizeof(struct AnyType));
getToken(ps);
ret->type = ED_DATE;
memset(ret->value.t, 0x00, sizeof(ret->value.t));
strncpy(ret->value.t, ps->currentTokenStr, 20);
matchToken(ps, ET_DATE_END);
return ret;
}
static struct AnyType *parseData(struct ParseStatus *ps)
{
struct AnyType *ret = (struct AnyType *)malloc(sizeof(struct AnyType));
char *str = NULL;
int len = 0;
matchToken(ps, ET_ETOKEN_STR);
len = strlen(ps->currentTokenStr);
str = (char *)malloc(len+1);
strcpy(str, ps->currentTokenStr);
ret->type = ED_DATA;
ret->value.a = str;
matchToken(ps, ET_DATA_END);
return ret;
}
static struct AnyType *parseDict(struct ParseStatus *ps)
{
struct AnyType *tmp = (struct AnyType *)malloc(sizeof(struct AnyType));
struct AnyType *ret = tmp;
struct AnyType *prev = NULL;
enum EToken token;
char *str = NULL;
int len = 0;
tmp->type = ED_DICT;
tmp->value.dict.key = NULL;
tmp->value.dict.value = NULL;
tmp->value.dict.next = NULL;
skipSpaceOfPS(ps);
while(1)
{
skipSpaceOfPS(ps);
token = getToken(ps);
if(token == ET_KEY_START)
{
getToken(ps);
len = strlen(ps->currentTokenStr);
str = NULL;
str = (char *)malloc(len+1);
strcpy(str, ps->currentTokenStr);
tmp->value.dict.key = str;
matchToken(ps, ET_KEY_END);
tmp->value.dict.value = parseAny(ps);
if(prev != NULL)
{
prev->value.dict.next = tmp;
if(tmp->value.dict.value == NULL)
{
if(tmp->value.dict.key)
free(tmp->value.dict.key);
tmp->value.dict.key = NULL;
free(tmp);
prev->value.dict.next = NULL;
}
}
}
else
{
break;
}
prev = tmp;
tmp = (struct AnyType *)malloc(sizeof(struct AnyType));
tmp->type = ED_DICT;
tmp->value.dict.key = NULL;
tmp->value.dict.value = NULL;
tmp->value.dict.next = NULL;
}
assert(ps->currentToken == ET_DICT_END);
return ret;
}
static struct AnyType *parseArray(struct ParseStatus *ps)
{
struct AnyType *tmp = (struct AnyType *)malloc(sizeof(struct AnyType));
struct AnyType *ret = tmp;
struct AnyType *prev = NULL;
int i = 0;
tmp->type = ED_ARRAY;
tmp->value.arry.value = NULL;
tmp->value.arry.next = NULL;
skipSpaceOfPS(ps);
while(1)
{
tmp->value.arry.key = i;
tmp->value.arry.value = parseAny(ps);
if(prev != NULL)
{
prev->value.arry.next = tmp;
if(tmp->value.arry.value == NULL)
{
free(tmp);
prev->value.arry.next = NULL;
break;
}
}
else
{
if(tmp->value.arry.value == NULL)
break;
}
prev = tmp;
tmp = (struct AnyType *)malloc(sizeof(struct AnyType));
tmp->type = ED_ARRAY;
tmp->value.arry.value = NULL;
tmp->value.arry.next = NULL;
i++;
}
assert(ET_ARRAY_END == ps->currentToken);
return ret;
}
static struct AnyType *parseAny(struct ParseStatus *ps)
{
struct AnyType *ret = NULL;
enum EToken token = getToken(ps);
skipSpaceOfPS(ps);
switch(token)
{
case ET_DICT_START:
ret = parseDict(ps);
break;
case ET_ARRAY_START:
ret = parseArray(ps);
break;
case ET_STRING_START:
ret = parseString(ps);
break;
case ET_INT_START:
ret = parseInt(ps);
break;
case ET_TRUE:
ret = (struct AnyType *)malloc(sizeof(struct AnyType));
ret->type = ED_BOOL;
ret->value.b = 1;
break;
case ET_FALSE:
ret = (struct AnyType *)malloc(sizeof(struct AnyType));
ret->type = ED_BOOL;
ret->value.b = 0;
break;
case ET_REAL_START:
ret = parseReal(ps);
break;
case ET_DATE_START:
ret = parseDate(ps);
break;
case ET_DATA_START:
ret = parseData(ps);
break;
default:
break;
}
return ret;
}
void freeAny(struct AnyType *plist)
{
enum EDataType type = plist->type;
struct AnyType *tmp = plist;
struct AnyType *prev = NULL;
switch(type)
{
case ED_DICT:
while(1)
{
if(!tmp)
break;
if(tmp->value.dict.key)
{
free((char *)(tmp->value.dict.key));
tmp->value.dict.key = NULL;
}
if(tmp->value.dict.value)
{
freeAny(tmp->value.dict.value);
tmp->value.dict.value = NULL;
}
prev = tmp;
tmp = tmp->value.dict.next;
if(prev)
{
free(prev);
}
}
break;
case ED_ARRAY:
while(1)
{
if(!tmp)
break;
if(tmp->value.arry.value)
{
freeAny(tmp->value.arry.value);
tmp->value.arry.value = NULL;
}
prev = tmp;
tmp = tmp->value.arry.next;
if(prev)
{
free(prev);
}
}
break;
case ED_STRING:
if(plist->value.s)
free(plist->value.s);
free(plist);
break;
case ED_BOOL:
free(plist);
break;
case ED_INT:
free(plist);
break;
case ED_REAL:
free(plist);
break;
case ED_DATE:
free(plist);
break;
case ED_DATA:
if(plist->value.a)
free(plist->value.a);
free(plist);
break;
default:
break;
}
}
void printAny(struct AnyType *plist)
{
enum EDataType type = plist->type;
struct AnyType *tmp = plist;
struct AnyType *prev = NULL;
switch(type)
{
case ED_DICT:
while(1)
{
if(!tmp)
break;
if(tmp->value.dict.key)
{
printf("key=%s\n", tmp->value.dict.key);
}
if(tmp->value.dict.value)
{
printf("value=\n");
printAny(tmp->value.dict.value);
}
prev = tmp;
tmp = tmp->value.dict.next;
if(prev)
{
printf("dict prev=%p\n", prev);
}
}
break;
case ED_ARRAY:
while(1)
{
if(!tmp)
break;
printf("key=%d\n", tmp->value.arry.key);
if(tmp->value.arry.value)
{
printf("array=\n");
printAny(tmp->value.arry.value);
}
prev = tmp;
tmp = tmp->value.arry.next;
if(prev)
{
printf("array prev=%p\n", prev);
}
}
break;
case ED_STRING:
if(plist->value.s)
{
printf("string=%s\n", plist->value.s);
}
break;
case ED_BOOL:
printf("bool=%d\n", plist->value.b);
break;
case ED_INT:
printf("integer=%d\n", plist->value.i);
break;
case ED_REAL:
printf("real=%lf\n", plist->value.d);
break;
case ED_DATE:
printf("date=%s\n", plist->value.t);
break;
case ED_DATA:
if(plist->value.a)
printf("data=%s\n", plist->value.a);
break;
default:
break;
}
}
struct AnyType *parsePlist(const char *source)
{
struct AnyType *ret = NULL;
struct ParseStatus tmp;
char *str = NULL;
tmp.len = strlen(source) + 1;
tmp.source = (char*)malloc(tmp.len);
str = (char *)tmp.source;
strcpy(str, source);
str[tmp.len] = 0x00;
tmp.parsePos = tmp.source;
tmp.currentTokenStr = NULL;
free((char *)source);
const char *find = strstr(tmp.parsePos, "plist version=");
if(find != NULL)
{
const char *findstart = strchr(find, '>');
if(findstart != NULL)
{
tmp.parsePos = findstart + 1;
}
}
ret = parseAny(&tmp);
if(tmp.currentTokenStr)
{
free(tmp.currentTokenStr);
tmp.currentTokenStr = NULL;
}
if(str)
{
free(str);
str = NULL;
}
return ret;
}
struct AnyType *parsePlistByFileName(char *filename)
{
return parsePlist(load_file(filename));
}
3.main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "plist.h"
int main()
{
struct AnyType *plist = parsePlistByFileName("./test.plist");
printAny(plist);
freeAny(plist);
return 0;
}
4.test.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>testArray</key>
<array>
<integer>34</integer>
<string>string item in array</string>
</array>
<key>testArrayLarge</key>
<array>
<integer>0</integer>
</array>
<key>testBoolFalse</key>
<false/>
<key>testBoolTrue</key>
<true/>
<key>testDict</key>
<dict>
<key>test string</key>
<string>inner dict item</string>
</dict>
<key>testDictLarge</key>
<dict>
<key>000</key>
<integer>0</integer>
<key>001</key>
<integer>1</integer>
</dict>
<key>testDouble</key>
<real>1.34223</real>
<key>testFloat</key>
<real>1.34223</real>
<key>testDate</key>
<date>2011-09-25T02:31:04Z</date>
<key>testImage</key>
<string>
asgegaseg<
agaesgaegfab
</string>
</dict>
</plist>
5.编译
$ gcc -o libplist.so -fPIC -shared plist.c -std=c89
$ gcc -o test -L. -lplist main.c -std=c89 -Wl,-rpath=.
6.运行结果
$ ./test
key=testArray
value=
key=0
array=
integer=34
array prev=0x116a1080
key=1
array=
string=string item in array
array prev=0x116a10e0
dict prev=0x116a1010
key=testArrayLarge
value=
key=0
array=
integer=0
array prev=0x116a11b0
dict prev=0x116a1160
key=testBoolFalse
value=
bool=0
dict prev=0x116a1210
key=testBoolTrue
value=
bool=1
dict prev=0x116a1290
key=testDict
value=
key=test string
value=
string=inner dict item
dict prev=0x116a1360
dict prev=0x116a1310
key=testDictLarge
value=
key=000
value=
integer=0
dict prev=0x116a1480
key=001
value=
integer=1
dict prev=0x116a1500
dict prev=0x116a1430
key=testDouble
value=
real=1.342230
dict prev=0x116a1950
key=testFloat
value=
real=1.342230
dict prev=0x116a19b0
key=testDate
value=
date=2011-09-25T02:31:04Z
dict prev=0x116a1a30
key=testImage
value=
string=asgegaseg<
agaesgaegfab
dict prev=0x116a1ab0
网友评论