美文网首页
c语言解析plist

c语言解析plist

作者: 一路向后 | 来源:发表于2020-12-13 19:41 被阅读0次

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

相关文章

网友评论

      本文标题:c语言解析plist

      本文链接:https://www.haomeiwen.com/subject/kqmpgktx.html