Skip to content

Instantly share code, notes, and snippets.

@smx-smx
Created July 27, 2015 23:48
Show Gist options
  • Save smx-smx/d51c7faea62a565adeda to your computer and use it in GitHub Desktop.
Save smx-smx/d51c7faea62a565adeda to your computer and use it in GitHub Desktop.
Basic plist parsing library
/*
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;
}
}
/*
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