Created
July 27, 2015 23:48
-
-
Save smx-smx/d51c7faea62a565adeda to your computer and use it in GitHub Desktop.
Basic plist parsing library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (C) 2015 Smx | |
*/ | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <ctype.h> | |
#include <fcntl.h> | |
#include "plist.h" | |
const int plist_debug = false; | |
void hexdump(void *pAddressIn, long lSize) { | |
char szBuf[100]; | |
long lIndent = 1; | |
long lOutLen, lIndex, lIndex2, lOutLen2; | |
long lRelPos; | |
struct { | |
char *pData; | |
unsigned long lSize; | |
} buf; | |
unsigned char *pTmp, ucTmp; | |
unsigned char *pAddress = (unsigned char *) pAddressIn; | |
buf.pData = (char *) pAddress; | |
buf.lSize = lSize; | |
while (buf.lSize > 0) { | |
pTmp = (unsigned char *) buf.pData; | |
lOutLen = (int) buf.lSize; | |
if (lOutLen > 16) lOutLen = 16; | |
// create a 64-character formatted output line: | |
sprintf(szBuf, " > %08zX", pTmp - pAddress); | |
lOutLen2 = lOutLen; | |
for (lIndex = 1 + lIndent, lIndex2 = 53 - 15 + lIndent, lRelPos = 0; lOutLen2; lOutLen2--, lIndex += 2, lIndex2++) { | |
ucTmp = *pTmp++; | |
sprintf(szBuf + lIndex, "%02X ", (unsigned short) ucTmp); | |
if (!isprint(ucTmp)) | |
ucTmp = '.'; // nonprintable char | |
szBuf[lIndex2] = ucTmp; | |
if (!(++lRelPos & 3)) { // extra blank after 4 bytes | |
lIndex++; | |
szBuf[lIndex + 2] = ' '; | |
} | |
} | |
if (!(lRelPos & 3)) lIndex--; | |
szBuf[lIndex] = '<'; | |
szBuf[lIndex + 1] = ' '; | |
printf("%s\n", szBuf); | |
buf.pData += lOutLen; | |
buf.lSize -= lOutLen; | |
} | |
} | |
int plist_readTo(PLIST *plist, char *to, int size){ | |
int count; | |
for(count=0; ; plist->offset++, plist->data++, count++){ | |
if(!strncmp(plist->data, to, size)){ | |
return count-1; | |
} | |
if(plist_feof(plist)){ | |
return -1; | |
} | |
} | |
} | |
int plist_nextLine(PLIST *plist){ | |
int ret = PLIST_READTO(plist, "\n"); | |
plist->data++; | |
if(++plist->offset > plist->size || !ret){ | |
return ret; | |
} | |
return true; | |
} | |
void plist_rewind(PLIST *plist){ | |
plist->offset = 0; | |
plist->data = plist->dataStart; | |
} | |
int plist_fgetc(PLIST *plist){ | |
if(plist_feof(plist)){ | |
return -1; | |
} | |
plist->data++; plist->offset++; | |
return (char)*(plist->data); | |
} | |
int plist_feof(PLIST *plist){ | |
if(plist->offset >= plist->size){ | |
return true; | |
} | |
return false; | |
} | |
void debug(const char *format, ...){ | |
if(plist_debug){ | |
va_list args; | |
va_start(args, format); | |
vprintf(format, args); | |
va_end(args); | |
} | |
} | |
int plist_fseek(PLIST *plist, int offset, int origin){ | |
switch(origin){ | |
case SEEK_SET: | |
plist->offset = offset; | |
plist->data = plist->dataStart + offset; | |
break; | |
case SEEK_CUR: | |
plist->offset += offset; | |
plist->data += offset; | |
break; | |
case SEEK_END: | |
plist->offset = plist->size + offset; | |
plist->data = plist->dataStart + plist->size + offset; | |
break; | |
default: | |
return false; | |
break; | |
} | |
return true; | |
} | |
int plist_isValid(PLIST *plist){ | |
int plistCheckLevel = 0; | |
PLIST_READTO(plist, "<"); | |
if(!strlencmp(plist->data, "<?xml")){ | |
debug("<?xml matches\n"); | |
plistCheckLevel++; | |
if(!plist_nextLine(plist)) return false; | |
} | |
if(!strlencmp(plist->data, "<!DOCTYPE plist PUBLIC")){ | |
debug("<!DOCTYPE plist PUBLIC matches\n"); | |
plistCheckLevel++; | |
if(!plist_nextLine(plist)) return false; | |
} | |
if(!strlencmp(plist->data, "<plist")){ | |
debug("<plist matches\n"); | |
plistCheckLevel++; | |
} else { | |
debug("Fatal!, cannot find the root node!\n"); | |
return false; | |
} | |
if(plistCheckLevel < 1) return false; | |
debug("Alright then, it's a valid plist :)\n"); | |
return true; | |
} | |
int plist_text2node(PLIST *plist, PLIST_NODE *prev, NODE_TYPE type){ | |
debug("\n"); | |
if(*(plist->data) != '<'){ | |
return false; | |
} | |
PLIST_NODE *node = calloc(1, sizeof(PLIST_NODE)); | |
if(!plist->root || !prev){ | |
debug("setting root node\n"); | |
plist->root = node; | |
} else { | |
switch(type){ | |
case NODE_RIGHT: | |
debug("setting next node\n"); | |
prev->next = node; | |
debug("setting prev node\n"); | |
node->prev = prev; | |
debug("copying parent node\n"); | |
node->parent = prev->parent; | |
break; | |
case NODE_CHILD: | |
debug("setting child node\n"); | |
prev->children = node; | |
debug("setting parent node\n"); | |
node->parent = prev; | |
break; | |
} | |
} | |
int tagNameSize = PLIST_READTO(plist, ">"); | |
debug("Tagsize: %d\n",tagNameSize); | |
if(tagNameSize <= 0){ | |
return false; | |
} | |
plist_fseek(plist, -tagNameSize, SEEK_CUR); | |
char *tagName = calloc(1, tagNameSize); | |
strncpy(tagName, plist->data, tagNameSize); | |
debug("Tag: %s\n", tagName); | |
node->tagName = tagName; | |
plist_fseek(plist, tagNameSize, SEEK_CUR); | |
int countentSize = PLIST_READTO(plist, "<"); | |
if(plist_fgetc(plist) != '/'){ //it's not tag end. a children starts | |
debug("child node detected\n"); | |
plist_fseek(plist, -1, SEEK_CUR); | |
plist_text2node(plist, node, NODE_CHILD); | |
} else { | |
plist_fseek(plist, -(1+countentSize), SEEK_CUR); | |
char *textContent = calloc(1, countentSize); | |
strncpy(textContent, plist->data, countentSize); | |
debug("Content: %s\n", textContent); | |
node->textContent = textContent; | |
} | |
while(!plist_feof(plist)){ | |
int c = PLIST_READTO(plist, "<"); | |
if(c < 0) break; | |
if(plist_fgetc(plist) == '/'){ | |
if(!strlencmp(plist->data, tagName)){ | |
break; //we reached the end of the level (right/left nodes) | |
} else { | |
continue; //it's the end of a tag in the same level | |
} | |
} | |
debug("right node detected\n"); | |
plist_fseek(plist, -1, SEEK_CUR); | |
plist_text2node(plist, node, NODE_RIGHT); | |
} | |
return true; | |
} | |
PLIST_NODE *plist_getNodebyField(PLIST_NODE *node, NODE_FIELD field, char *fieldValue){ | |
debug("%s: ", node->tagName); | |
if(node->textContent){ | |
debug("%s", node->textContent); | |
} | |
debug("\n"); | |
switch(field){ | |
case NODE_TAGNAME: | |
if(node->tagName && !strlencmp(node->tagName, fieldValue)){ | |
return node; | |
} | |
break; | |
case NODE_TEXTCONTENT: | |
if(node->textContent && !strlencmp(node->textContent, fieldValue)){ | |
return node; | |
} | |
break; | |
} | |
PLIST_NODE *result; | |
if(node->next){ | |
debug("going next\n"); | |
result = plist_getNodebyField(node->next, field, fieldValue); | |
if(result){ | |
return result; | |
} | |
} | |
if(node->children){ | |
debug("going child %s\n"); | |
result = plist_getNodebyField(node->children, field, fieldValue); | |
if(result){ | |
return result; | |
} | |
} | |
return false; | |
} | |
PLIST_NODE *plist_getNodeByTagName(PLIST *plist, char *tagName, PLIST_NODE *prev){ | |
if(prev){ | |
return plist_getNodebyField(prev, NODE_TAGNAME, tagName); | |
} else { | |
return plist_getNodebyField(plist->root, NODE_TAGNAME, tagName); | |
} | |
} | |
PLIST_NODE *plist_getNodeByTextContent(PLIST *plist, char *textContent, PLIST_NODE *prev){ | |
if(prev){ | |
return plist_getNodebyField(prev, NODE_TEXTCONTENT, textContent); | |
} else { | |
return plist_getNodebyField(plist->root, NODE_TEXTCONTENT, textContent); | |
} | |
} | |
PLIST_NODE *plist_getNodeByKey(PLIST *plist, char *keyName, PLIST_NODE *prev){ | |
PLIST_NODE *result; | |
if(prev){ | |
result = plist_getNodeByTextContent(plist, keyName, prev); | |
} else { | |
result = plist_getNodeByTextContent(plist, keyName, plist->root); | |
} | |
if(result && result->next){ | |
return result->next; | |
} | |
return false; | |
} | |
void plist_node_free(PLIST_NODE *node){ | |
if(node && node->next){ | |
debug("going next\n"); | |
plist_node_free(node->next); | |
} | |
if(node && node->children){ | |
debug("going child %s\n"); | |
plist_node_free(node->children); | |
} else { | |
if(node){ | |
debug("free call\n"); | |
free(node); | |
} | |
} | |
} | |
int plist_close(PLIST *plist){ | |
if(plist){ | |
if(plist->data){ | |
debug("munmap\n"); | |
munmap(plist->data, plist->size); | |
} | |
if(plist->root){ | |
debug("root free\n"); | |
plist_node_free(plist->root); | |
} | |
if(plist){ | |
free(plist); | |
} | |
return true; | |
} else { | |
return false; | |
} | |
} | |
int plist_open(const char *filename, PLIST *file){ | |
int fd, fileSize; | |
fd = open(filename, O_RDONLY); | |
if(!fd){ | |
debug("open fail\n"); | |
return false; | |
} else { | |
struct stat statbuf; | |
if(fstat(fd, &statbuf) < 0){ | |
return false; | |
} else { | |
file->size = statbuf.st_size; | |
if(file->size <= 0){ | |
debug("size inval\n"); | |
return false; | |
} | |
} | |
} | |
if( (file->data = mmap(0, file->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { | |
debug("mmap fail\n"); | |
return false; | |
} | |
file->dataStart = file->data; | |
if(!(file->data)){ | |
debug("data fail\n"); | |
return false; | |
} | |
if(plist_isValid(file)){ | |
return plist_text2node(file, 0, NODE_PARENT); | |
} else { | |
debug("verif fail\n"); | |
munmap(file->data, file->size); | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (C) 2015 Smx | |
*/ | |
#ifndef _PLIST_H | |
#define _PLIST_H | |
#define PLIST_READTO(plist, end) plist_readTo(plist, end, strlen(end)) | |
#define strlencmp(x, y) strncmp(x, y, strlen(y)) | |
#define true 1 | |
#define false 0 | |
typedef struct PLIST_NODE { | |
struct PLIST_NODE *parent; //previous level dad node | |
char *tagName; | |
char *textContent; | |
struct PLIST_NODE *prev; //previous node in the same level | |
struct PLIST_NODE *next; //next node in the same level | |
struct PLIST_NODE *children; //next level first node | |
} PLIST_NODE; | |
typedef struct { | |
unsigned int offset; | |
unsigned int size; | |
unsigned char *dataStart; | |
unsigned char *data; | |
PLIST_NODE *root; | |
} PLIST; | |
typedef enum { | |
NODE_PARENT = 0, | |
NODE_LEFT, | |
NODE_RIGHT, | |
NODE_CHILD | |
} NODE_TYPE; | |
typedef enum { | |
NODE_TAGNAME = 0, | |
NODE_TEXTCONTENT | |
} NODE_FIELD; | |
int plist_readTo(PLIST *plist, char *to, int size); | |
int plist_nextLine(PLIST *plist); | |
void plist_rewind(PLIST *plist); | |
int plist_fgetc(PLIST *plist); | |
int plist_feof(PLIST *plist); | |
int plist_fseek(PLIST *plist, int offset, int origin); | |
int plist_isValid(PLIST *plist); | |
int plist_text2node(PLIST *plist, PLIST_NODE *prev, NODE_TYPE type); | |
int plist_open(const char *filename, PLIST *dest); | |
int plist_close(PLIST *plist); | |
int plist_isvalid(PLIST *plist); | |
PLIST_NODE *plist_getNodeByTagName(PLIST *plist, char *tagName, PLIST_NODE *prev); | |
PLIST_NODE *plist_getNodeByTextContent(PLIST *plist, char *textContent, PLIST_NODE *prev); | |
PLIST_NODE *plist_getNodeByKey(PLIST *plist, char *tagName, PLIST_NODE *prev); | |
PLIST_NODE *plist_getNodebyField(PLIST_NODE *node, NODE_FIELD field, char *fieldValue); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment