/* * Copyright (c) 2005 Rob Braun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Rob Braun nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * 03-Apr-2005 * DRI: Rob Braun */ /* * Portions Copyright 2006, Apple Computer, Inc. * Christopher Ryan */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include "config.h" #ifndef HAVE_ASPRINTF #include "asprintf.h" #endif #include "xar.h" #include "filetree.h" #include "archive.h" #include "b64.h" #include "ea.h" /* Overview: * xar_file_t's exist within a xar_archive_t. xar_prop_t's exist * within xar_file_t's and xar_attr_t's exist within xar_prop_t's * and xar_file_t's. * Basically, a xar_file_t is a container for xar_prop_t's. * xar_attr_t's are things like: blah * In this example, foo is the key of a xar_prop_t, and blah is * the value. bar is the key of a xar_attr_t which is part of * foo's xar_prop_t, and 5 is bar's value. * xar_file_t's have xar_attr_t's for the case of: * * The file has an attribute of "id" with a value of "42". */ struct __xar_iter_t { const void *iter; char *path; void *node; int nochild; }; /* Convenience macros for dereferencing the structs */ #define XAR_ITER(x) ((struct __xar_iter_t *)(x)) /* xar_attr_prop * Returns: a newly allocated and initialized property attribute. * It is the caller's responsibility to associate the attribute * with either a file or a property. */ xar_attr_t xar_attr_new(void) { xar_attr_t ret; ret = malloc(sizeof(struct __xar_attr_t)); if(!ret) return NULL; XAR_ATTR(ret)->key = NULL; XAR_ATTR(ret)->value = NULL; XAR_ATTR(ret)->next = NULL; XAR_ATTR(ret)->ns = NULL; return ret; } int32_t xar_attr_pset(xar_file_t f, xar_prop_t p, const char *key, const char *value) { xar_attr_t a, i; if( !p ) { a = XAR_FILE(f)->attrs; } else { a = XAR_PROP(p)->attrs; } if( !a ) { a = xar_attr_new(); if(!p) XAR_FILE(f)->attrs = a; else XAR_PROP(p)->attrs = a; XAR_ATTR(a)->key = strdup(key); XAR_ATTR(a)->value = strdup(value); return 0; } for(i = a; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) { if(strcmp(XAR_ATTR(i)->key, key)==0) { free((char*)XAR_ATTR(i)->value); XAR_ATTR(i)->value = strdup(value); return 0; } } a = xar_attr_new(); if(!p) { XAR_ATTR(a)->next = XAR_ATTR(XAR_FILE(f)->attrs); XAR_FILE(f)->attrs = a; } else { XAR_ATTR(a)->next = XAR_ATTR(XAR_PROP(p)->attrs); XAR_PROP(p)->attrs = a; } XAR_ATTR(a)->key = strdup(key); XAR_ATTR(a)->value = strdup(value); return 0; } /* xar_attr_set * f: the file the attribute is associated with * prop: The property key the attribute is associated with. This can * be NULL to signify the attribute should be set for the file, * rather than the property. * key: The name of the attribute to set. * value: The value of the attribute. * Returns: 0 on success, -1 on failure. * Summary: Basically, sets an attribute. The only tricky part is * it can set an attribute on a property or a file. */ int32_t xar_attr_set(xar_file_t f, const char *prop, const char *key, const char *value) { if( !prop ) { return xar_attr_pset(f, NULL, key, value); } else { xar_prop_t p = NULL; p = xar_prop_find(XAR_FILE(f)->props, prop); if( !p ) return -1; return xar_attr_pset(f, p, key, value); } } const char *xar_attr_pget(xar_file_t f, xar_prop_t p, const char *key) { xar_attr_t a, i; if( !p ) a = XAR_FILE(f)->attrs; else a = XAR_PROP(p)->attrs; if( !a ) return NULL; for(i = a; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) { if(strcmp(XAR_ATTR(i)->key, key)==0) { return XAR_ATTR(i)->value; } } if( i && (strcmp(XAR_ATTR(i)->key, key)==0)) return XAR_ATTR(i)->value; return NULL; } /* xar_attr_get * f: file to find the associated attribute in * prop: name of the property the attribute is of. May be NULL to specify * the file's attributes. * key: name of the attribute to search for. * Returns: a reference to the value of the attribute. */ const char *xar_attr_get(xar_file_t f, const char *prop, const char *key) { if( !prop ) return xar_attr_pget(f, NULL, key); else { xar_prop_t p = NULL; p = xar_prop_find(XAR_FILE(f)->props, prop); if( !p ) return NULL; return xar_attr_pget(f, p, key); } } /* xar_attr_free * a: attribute to free * Summary: frees the attribute structure and everything inside it. * It is the caller's responsibility to ensure the linked list gets * updated. This will *not* do anything to ensure the consistency * of the attribute linked list. */ void xar_attr_free(xar_attr_t a) { if(!a) return; free((char*)XAR_ATTR(a)->key); free((char*)XAR_ATTR(a)->value); free(XAR_ATTR(a)); return; } /* xar_attr_first * f: file to associate the iterator with * prop: the name of the property within the file to associate the iterator with * i: an iterator as returned by xar_iter_new * Returns: a pointer to the value of the first attribute associated with the * property 'prop' associated with the file 'f' * Summary: This MUST be called prior to calling xar_attr_next, * to iterate over the attributes of property key 'prop'. */ const char *xar_attr_first(xar_file_t f, const char *prop, xar_iter_t i) { xar_prop_t p = NULL; xar_attr_t a; if( !prop ) a = XAR_FILE(f)->attrs; else { p = xar_prop_find(XAR_FILE(f)->props, prop); if( !p ) return NULL; a = XAR_PROP(p)->attrs; } if( !a ) return NULL; XAR_ITER(i)->iter = a; free(XAR_ITER(i)->node); XAR_ITER(i)->node = strdup(XAR_ATTR(a)->key); return XAR_ITER(i)->node; } /* xar_attr_next * i: iterator allocated by xar_iter_new, and initialized with xar_attr_first * Returns: a pointer to the key of the next attribute associated with * the iterator. NULL will be returned when there are no more attributes * to find. */ const char *xar_attr_next(xar_iter_t i) { xar_attr_t a = XAR_ITER(i)->iter; if( XAR_ATTR(a)->next == NULL ) return NULL; XAR_ITER(i)->iter = XAR_ATTR(a)->next; free(XAR_ITER(i)->node); XAR_ITER(i)->node = strdup(XAR_ATTR(XAR_ITER(i)->iter)->key); return XAR_ITER(i)->node; } /* xar_iter_new * Returns a newly allocated iterator for use on files, properties, or * attributes. */ xar_iter_t xar_iter_new() { xar_iter_t ret = malloc(sizeof(struct __xar_iter_t)); if(!ret) return NULL; XAR_ITER(ret)->iter = NULL; XAR_ITER(ret)->path = NULL; XAR_ITER(ret)->node = NULL; XAR_ITER(ret)->nochild = 0; return ret; } /* xar_iter_free * Frees memory associated with the specified iterator */ void xar_iter_free(xar_iter_t i) { free(XAR_ITER(i)->node); free(XAR_ITER(i)); } const char *xar_prop_getkey(xar_prop_t p) { return XAR_PROP(p)->key; } const char *xar_prop_getvalue(xar_prop_t p) { return XAR_PROP(p)->value; } int32_t xar_prop_setkey(xar_prop_t p, const char *key) { free((char *)XAR_PROP(p)->key); if(key) XAR_PROP(p)->key = strdup(key); return 0; } int32_t xar_prop_setvalue(xar_prop_t p, const char *value) { free((char *)XAR_PROP(p)->value); if(value) XAR_PROP(p)->value = strdup(value); return 0; } /* xar_prop_pfirst * f: file to retrieve the first property from * Returns: a xar_prop_t corresponding to the first xar_prop_t associated with f * NULL if there are no properties associated with the file. */ xar_prop_t xar_prop_pfirst(xar_file_t f) { return XAR_FILE(f)->props; } /* xar_prop_pnext * p: previous property used to retrieve the next * Returns: a xar_prop_t if there is a next, NULL otherwise */ xar_prop_t xar_prop_pnext(xar_prop_t p) { return XAR_PROP(p)->next; } /* xar_prop_first * f: file to associate the iterator with * i: an iterator as returned by xar_iter_new * Returns: a pointer to the value of the first property associated with * the file 'f'. * Summary: This MUST be called first prior to calling xar_prop_next, * to iterate over properties of file 'f'. This has the side effect of * associating the iterator with the file's properties, which is needed * before xar_prop_next. */ const char *xar_prop_first(xar_file_t f, xar_iter_t i) { XAR_ITER(i)->iter = XAR_FILE(f)->props; free(XAR_ITER(i)->node); XAR_ITER(i)->node = strdup(XAR_PROP(XAR_ITER(i)->iter)->key); return XAR_ITER(i)->node; } /* xar_prop_next * i: iterator allocated by xar_iter_new, and initialized with xar_prop_first * Returns: a pointer to the value of the next property associated with * the iterator. NULL will be returned when there are no more properties * to find. If a property has a NULL value, the string "" will be returned. * This will recurse down child properties, flattening the namespace and * adding separators. For instance a1->b1->c1, a1 will first be returned, * the subsequent call will return "a1/b1", and the next call will return * "a1/b1/c1", etc. */ const char *xar_prop_next(xar_iter_t i) { xar_prop_t p = XAR_ITER(i)->iter; if( !(XAR_ITER(i)->nochild) && XAR_PROP(p)->children ) { char *tmp = XAR_ITER(i)->path; if( tmp ) { asprintf(&XAR_ITER(i)->path, "%s/%s", tmp, XAR_PROP(p)->key); free(tmp); } else XAR_ITER(i)->path = strdup(XAR_PROP(p)->key); XAR_ITER(i)->iter = p = XAR_PROP(p)->children; goto SUCCESS; } XAR_ITER(i)->nochild = 0; if( XAR_PROP(p)->next ) { XAR_ITER(i)->iter = p = XAR_PROP(p)->next; goto SUCCESS; } if( XAR_PROP(p)->parent ) { char *tmp1, *tmp2; char *dname; if( strstr(XAR_ITER(i)->path, "/") ) { tmp1 = tmp2 = XAR_ITER(i)->path; dname = dirname(tmp2); XAR_ITER(i)->path = strdup(dname); free(tmp1); } else { free(XAR_ITER(i)->path); XAR_ITER(i)->path = NULL; } XAR_ITER(i)->iter = p = XAR_PROP(p)->parent; XAR_ITER(i)->nochild = 1; return xar_prop_next(i); } return NULL; SUCCESS: free(XAR_ITER(i)->node); if( XAR_ITER(i)->path ) asprintf((char **)&XAR_ITER(i)->node, "%s/%s", XAR_ITER(i)->path, XAR_PROP(p)->key); else { if(XAR_PROP(p)->key == NULL) XAR_ITER(i)->node = strdup(""); else XAR_ITER(i)->node = strdup(XAR_PROP(p)->key); } return XAR_ITER(i)->node; } /* xar_prop_new * f: file to associate the new file with. May not be NULL * parent: the parent property of the new property. May be NULL * Returns: a newly allocated and initialized property. * Summary: in addition to allocating the new property, it * will be inserted into the parent node's list of children, * and/or added to the file's list of properties, as appropriate. */ xar_prop_t xar_prop_new(xar_file_t f, xar_prop_t parent) { xar_prop_t p; p = malloc(sizeof(struct __xar_prop_t)); if( !p ) return NULL; XAR_PROP(p)->key = NULL; XAR_PROP(p)->value = NULL; XAR_PROP(p)->children = NULL; XAR_PROP(p)->next = NULL; XAR_PROP(p)->attrs = NULL; XAR_PROP(p)->parent = parent; XAR_PROP(p)->file = f; XAR_PROP(p)->prefix = XAR_FILE(f)->prefix; XAR_PROP(p)->ns = NULL; if(parent) { if( !XAR_PROP(parent)->children ) { XAR_PROP(parent)->children = p; } else { XAR_PROP(p)->next = XAR_PROP(parent)->children; XAR_PROP(parent)->children = p; } } else { if( XAR_FILE(f)->props == NULL ) { XAR_FILE(f)->props = p; } else { XAR_PROP(p)->next = XAR_FILE(f)->props; XAR_FILE(f)->props = p; } } return p; } /* xar_prop_find * p: property to check * key: name of property to find. * Returns: reference to the property with the specified key * Summary: A node's name may be specified by a path, such as * "a1/b1/c1", and child nodes will be searched for each * "/" separator. */ xar_prop_t xar_prop_find(xar_prop_t p, const char *key) { xar_prop_t i, ret; char *tmp1, *tmp2, *tmp3; if( !p ) return NULL; tmp2 = tmp1 = strdup(key); tmp3 = strsep(&tmp2, "/"); i = p; do { if( strcmp(tmp3, XAR_PROP(i)->key) == 0 ) { if( tmp2 == NULL ) { free(tmp1); return i; } ret = xar_prop_find(XAR_PROP(i)->children, tmp2); free(tmp1); return ret; } i = XAR_PROP(i)->next; } while(i); free(tmp1); return NULL; } /* xar_prop_set_r * p: property to recurse down and set the property of * key: key of the property to set * value: desired value of the property * Returns: 0 on sucess, -1 on failure. * Summary: This is an internal helper function for xar_prop_set() which * does the recursion down the property tree. */ static xar_prop_t xar_prop_set_r(xar_file_t f, xar_prop_t p, const char *key, const char *value, int overwrite) { xar_prop_t i, ret, ret2, start; char *tmp1, *tmp2, *tmp3; tmp2 = tmp1 = strdup(key); tmp3 = strsep(&tmp2, "/"); if( !p ) { start = XAR_FILE(f)->props; } else { start = XAR_PROP(p)->children; } for( i = start; i; i = XAR_PROP(i)->next ) { if( strcmp(tmp3, XAR_PROP(i)->key) == 0 ) { if( !tmp2 ) { if( overwrite ) { xar_prop_setvalue(i, value); free(tmp1); return i; } else { ret = xar_prop_new(f, p); if( !ret ) { free(tmp1); return ret; } xar_prop_setvalue(ret, value); xar_prop_setkey(ret, tmp3); free(tmp1); return ret; } } ret2 = xar_prop_set_r(f, i, tmp2, value, overwrite); free(tmp1); return ret2; } } ret = xar_prop_new(f, p); if( !ret ) { free(tmp1); return ret; } if( !tmp2 ) { xar_prop_setvalue(ret, value); xar_prop_setkey(ret, tmp3); free(tmp1); return ret; } xar_prop_setkey(ret, tmp3); xar_prop_setvalue(ret, NULL); ret2 = xar_prop_set_r(f, ret, tmp2, value, overwrite); free(tmp1); return ret2; } /* xar_prop_set * f: file to set the property on * key: key of the property to set * value: desired value of the property * Returns: 0 on success, -1 on failure * Summary: If the property already exists, its value is overwritten * by 'value'. If the property does not exist, it is created. * Copies of key and value are kept in the tree. The caller may do * what they wish with these values after the call returns. * References to these copies will be returned by iterating over * the properties, or by calling xar_prop_get(). * These copies will be released when the property is released. * * Note that you *CANNOT* have a node with a value and children. * This implementation will let you, but the serialization to xml * will not be what you're hoping for. */ int32_t xar_prop_set(xar_file_t f, const char *key, const char *value) { if( xar_prop_set_r(f, NULL, key, value, 1) ) return 0; return -1; } /* xar_prop_pset * Same as xar_prop_set, except it takes a xar_prop_t which will be * treated as the root property. * Returns a xar_prop_t that was created or set. Returns NULL if error. */ xar_prop_t xar_prop_pset(xar_file_t f, xar_prop_t p, const char *key, const char *value) { return xar_prop_set_r(f, p, key, value, 1); } /* xar_prop_create * Identical to xar_prop_set, except it will not overwrite an existing * property, it will create another one. */ int32_t xar_prop_create(xar_file_t f, const char *key, const char *value) { if( xar_prop_set_r(f, NULL, key, value, 0) ) return 0; return -1; } /* xar_prop_get * f: file to look for the property in * key: name of property to find. * value: on return, *value will point to the value of the property * value may be NULL, in which case, only the existence of the property * is tested. * Returns: 0 for success, -1 on failure * Summary: A node's name may be specified by a path, such as * "a1/b1/c1", and child nodes will be searched for each * "/" separator. */ int32_t xar_prop_get(xar_file_t f, const char *key, const char **value) { xar_prop_t r = xar_prop_find(XAR_FILE(f)->props, key); if( !r ) { if(value) *value = NULL; return -1; } if(value) *value = XAR_PROP(r)->value; return 0; } xar_prop_t xar_prop_pget(xar_prop_t p, const char *key) { char *tmp; const char *k; xar_prop_t ret; k = XAR_PROP(p)->key; asprintf(&tmp, "%s/%s", k, key); ret = xar_prop_find(p, tmp); free(tmp); return ret; } /* xar_prop_replicate_r * f: file to attach property * p: property (list) to iterate and add * parent: parent property * Summary: Recursivley adds property list (p) to file (f) and parent (parent). */ void xar_prop_replicate_r(xar_file_t f, xar_prop_t p, xar_prop_t parent ) { xar_prop_t property = p; /* look through properties */ for( property = p; property; property = property->next ){ xar_prop_t newprop = xar_prop_new( f, parent ); /* copy the key value for the property */ XAR_PROP(newprop)->key = strdup(property->key); if(property->value) XAR_PROP(newprop)->value = strdup(property->value); /* loop through the attributes and copy them */ xar_attr_t a = NULL; xar_attr_t last = NULL; /* copy attributes for file */ for(a = property->attrs; a; a = a->next) { if( NULL == newprop->attrs ){ last = xar_attr_new(); XAR_PROP(newprop)->attrs = last; }else{ XAR_ATTR(last)->next = xar_attr_new(); last = XAR_ATTR(last)->next; } XAR_ATTR(last)->key = strdup(a->key); if(a->value) XAR_ATTR(last)->value = strdup(a->value); } /* loop through the children properties and recursively add them */ xar_prop_replicate_r(f, property->children, newprop ); } } /* xar_prop_free * p: property to free * Summary: frees the specified property and all its children. * Stored copies of the key and value will be released, as will * all attributes. */ void xar_prop_free(xar_prop_t p) { xar_prop_t i; xar_attr_t a; while( XAR_PROP(p)->children ) { i = XAR_PROP(p)->children; XAR_PROP(p)->children = XAR_PROP(i)->next; xar_prop_free(i); } while(XAR_PROP(p)->attrs) { a = XAR_PROP(p)->attrs; XAR_PROP(p)->attrs = XAR_ATTR(a)->next; xar_attr_free(a); } free((char*)XAR_PROP(p)->key); free((char*)XAR_PROP(p)->value); free(XAR_PROP(p)); } void xar_prop_punset(xar_file_t f, xar_prop_t p) { xar_prop_t i; if( !p ) { return; } if( XAR_PROP(p)->parent ) { i = XAR_PROP(p)->parent->children; if( i == p ) { XAR_PROP(XAR_PROP(p)->parent)->children = XAR_PROP(p)->next; xar_prop_free(p); return; } } else { i = XAR_FILE(f)->props; if( i == p ) { XAR_FILE(f)->props = XAR_PROP(p)->next; xar_prop_free(p); return; } } while( i && (XAR_PROP(i)->next != XAR_PROP(p)) ) { i = XAR_PROP(i)->next; } if( i && (XAR_PROP(i)->next == XAR_PROP(p)) ) { XAR_PROP(i)->next = XAR_PROP(p)->next; xar_prop_free(p); } return; } void xar_prop_unset(xar_file_t f, const char *key) { xar_prop_t r = xar_prop_find(XAR_FILE(f)->props, key); xar_prop_punset(f, r); return; } /* xar_file_new * f: parent file of the file to be created. May be NULL * Returns: a newly allocated file structure. */ xar_file_t xar_file_new(xar_file_t f) { xar_file_t ret, i; ret = calloc(1, sizeof(struct __xar_file_t)); if(!ret) return NULL; XAR_FILE(ret)->parent = f; XAR_FILE(ret)->next = NULL; XAR_FILE(ret)->children = NULL; XAR_FILE(ret)->props = NULL; XAR_FILE(ret)->attrs = NULL; XAR_FILE(ret)->prefix = NULL; XAR_FILE(ret)->ns = NULL; XAR_FILE(ret)->fspath = NULL; XAR_FILE(ret)->eas = NULL; XAR_FILE(ret)->nexteaid = 0; if( f ) { if( !XAR_FILE(f)->children ) { XAR_FILE(f)->children = ret; } else { for(i = XAR_FILE(f)->children; XAR_FILE(i)->next; i = XAR_FILE(i)->next); XAR_FILE(i)->next = ret; } } return ret; } xar_file_t xar_file_replicate(xar_file_t original, xar_file_t newparent) { xar_file_t ret = xar_file_new(newparent); xar_attr_t a; /* copy attributes for file */ for(a = XAR_FILE(original)->attrs; a; a = XAR_ATTR(a)->next) { /* skip the id attribute */ if( 0 == strcmp(a->key, "id" ) ) continue; xar_attr_set(ret, NULL , a->key, a->value ); } /* recursively copy properties */ xar_prop_replicate_r(ret, XAR_FILE(original)->props, NULL); return ret; } /* xar_file_free * f: file to free * Summary: frees the specified file and all children, * properties, and attributes associated with the file. */ void xar_file_free(xar_file_t f) { xar_file_t i; xar_prop_t n; xar_attr_t a; while(XAR_FILE(f)->children) { i = XAR_FILE(f)->children; XAR_FILE(f)->children = XAR_FILE(i)->next; xar_file_free(i); } while(XAR_FILE(f)->props) { n = XAR_FILE(f)->props; XAR_FILE(f)->props = XAR_PROP(n)->next; xar_prop_free(n); } while(XAR_FILE(f)->attrs) { a = XAR_FILE(f)->attrs; XAR_FILE(f)->attrs = XAR_ATTR(a)->next; xar_attr_free(a); } free((char *)XAR_FILE(f)->fspath); free(XAR_FILE(f)); } /* xar_file_first * x: archive to associate the iterator with * i: an iterator as returned by xar_iter_new * Returns: a pointer to the name of the first file associated with * the archive 'x'. * Summary: This MUST be called first prior to calling xar_file_next, * to iterate over files of archive 'x'. This has the side effect of * associating the iterator with the archive's files, which is needed * before xar_file_next. */ xar_file_t xar_file_first(xar_t x, xar_iter_t i) { XAR_ITER(i)->iter = XAR(x)->files; free(XAR_ITER(i)->node); return XAR_ITER(i)->iter; } /* xar_file_next * i: iterator allocated by xar_iter_new, and initialized with xar_file_first * Returns: a pointer to the name of the next file associated with * the iterator. NULL will be returned when there are no more files * to find. * This will recurse down child files (directories), flattening the * namespace and adding separators. For instance a1->b1->c1, a1 will * first be returned, the subsequent call will return "a1/b1", and the * next call will return "a1/b1/c1", etc. */ xar_file_t xar_file_next(xar_iter_t i) { xar_file_t f = XAR_ITER(i)->iter; const char *name; if( !(XAR_ITER(i)->nochild) && XAR_FILE(f)->children ) { char *tmp = XAR_ITER(i)->path; xar_prop_get(f, "name", &name); if( tmp ) { asprintf(&XAR_ITER(i)->path, "%s/%s", tmp, name); free(tmp); } else XAR_ITER(i)->path = strdup(name); XAR_ITER(i)->iter = f = XAR_FILE(f)->children; goto FSUCCESS; } XAR_ITER(i)->nochild = 0; if( XAR_FILE(f)->next ) { XAR_ITER(i)->iter = f = XAR_FILE(f)->next; goto FSUCCESS; } if( XAR_FILE(f)->parent ) { char *tmp1, *tmp2; char *dname; if( strstr(XAR_ITER(i)->path, "/") ) { tmp1 = tmp2 = XAR_ITER(i)->path; dname = dirname(tmp2); XAR_ITER(i)->path = strdup(dname); free(tmp1); } else { free(XAR_ITER(i)->path); XAR_ITER(i)->path = NULL; } XAR_ITER(i)->iter = f = XAR_FILE(f)->parent; XAR_ITER(i)->nochild = 1; return xar_file_next(i); } return NULL; FSUCCESS: xar_prop_get(f, "name", &name); XAR_ITER(i)->iter = (void *)f; return XAR_ITER(i)->iter; } /* xar_file_find * f: file subtree to look under * path: path to file to find * Returns the file_t describing the file, or NULL if not found. */ xar_file_t xar_file_find(xar_file_t f, const char *path) { xar_file_t i, ret; char *tmp1, *tmp2, *tmp3; if( !f ) return NULL; tmp2 = tmp1 = strdup(path); tmp3 = strsep(&tmp2, "/"); i = f; do { const char *name; xar_prop_get(i, "name", &name); if( name == NULL ) continue; if( strcmp(tmp3, name) == 0 ) { if( tmp2 == NULL ) { free(tmp1); return i; } ret = xar_file_find(XAR_FILE(i)->children, tmp2); free(tmp1); return ret; } i = XAR_FILE(i)->next; } while(i); free(tmp1); return NULL; } /* xar_prop_serialize * p: property to serialize * writer: the xmlTextWriterPtr allocated by xmlNewTextWriter*() * Summary: recursively serializes the property passed to it, including * children, siblings, attributes, etc. */ void xar_prop_serialize(xar_prop_t p, xmlTextWriterPtr writer) { xar_prop_t i; xar_attr_t a; if( !p ) return; i = p; do { if( XAR_PROP(i)->prefix || XAR_PROP(i)->ns ) xmlTextWriterStartElementNS(writer, BAD_CAST(XAR_PROP(i)->prefix), BAD_CAST(XAR_PROP(i)->key), NULL); else xmlTextWriterStartElement(writer, BAD_CAST(XAR_PROP(i)->key)); for(a = XAR_PROP(i)->attrs; a; a = XAR_ATTR(a)->next) { xmlTextWriterWriteAttributeNS(writer, BAD_CAST(XAR_ATTR(a)->ns), BAD_CAST(XAR_ATTR(a)->key), NULL, BAD_CAST(XAR_ATTR(a)->value)); } if( XAR_PROP(i)->value ) { if( strcmp(XAR_PROP(i)->key, "name") == 0 ) { unsigned char *tmp; int outlen = strlen(XAR_PROP(i)->value); int inlen, len; inlen = len = outlen; tmp = malloc(len); assert(tmp); if( UTF8Toisolat1(tmp, &len, BAD_CAST(XAR_PROP(i)->value), &inlen) < 0 ) { xmlTextWriterWriteAttribute(writer, BAD_CAST("enctype"), BAD_CAST("base64")); xmlTextWriterWriteBase64(writer, XAR_PROP(i)->value, 0, strlen(XAR_PROP(i)->value)); } else xmlTextWriterWriteString(writer, BAD_CAST(XAR_PROP(i)->value)); free(tmp); } else xmlTextWriterWriteString(writer, BAD_CAST(XAR_PROP(i)->value)); } if( XAR_PROP(i)->children ) { xar_prop_serialize(XAR_PROP(i)->children, writer); } xmlTextWriterEndElement(writer); i = XAR_PROP(i)->next; } while(i); } /* xar_file_serialize * f: file to serialize * writer: the xmlTextWriterPtr allocated by xmlNewTextWriter*() * Summary: recursively serializes the file passed to it, including * children, siblings, properties, attributes, etc. */ void xar_file_serialize(xar_file_t f, xmlTextWriterPtr writer) { xar_file_t i; xar_attr_t a; i = f; do { xmlTextWriterStartElement(writer, BAD_CAST("file")); for(a = XAR_FILE(i)->attrs; a; a = XAR_ATTR(a)->next) { xmlTextWriterWriteAttribute(writer, BAD_CAST(XAR_ATTR(a)->key), BAD_CAST(XAR_ATTR(a)->value)); } xar_prop_serialize(XAR_FILE(i)->props, writer); if( XAR_FILE(i)->children ) xar_file_serialize(XAR_FILE(i)->children, writer); xmlTextWriterEndElement(writer); i = XAR_FILE(i)->next; } while(i); return; } /* xar_prop_unserialize * f: file the property is to belong to * p: parent property, may be NULL * reader: xmlTextReaderPtr already allocated */ int32_t xar_prop_unserialize(xar_file_t f, xar_prop_t parent, xmlTextReaderPtr reader) { const char *name, *value, *ns; int type, i, isempty = 0; int isname = 0, isencoded = 0; xar_prop_t p; p = xar_prop_new(f, parent); if( xmlTextReaderIsEmptyElement(reader) ) isempty = 1; i = xmlTextReaderAttributeCount(reader); name = (const char *)xmlTextReaderConstLocalName(reader); XAR_PROP(p)->key = strdup(name); ns = (const char *)xmlTextReaderConstPrefix(reader); if( ns ) XAR_PROP(p)->prefix = strdup(ns); if( strcmp(name, "name") == 0 ) isname = 1; if( i > 0 ) { for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) { xar_attr_t a; const char *name = (const char *)xmlTextReaderConstLocalName(reader); const char *value = (const char *)xmlTextReaderConstValue(reader); const char *ns = (const char *)xmlTextReaderConstPrefix(reader); if( isname && (strcmp(name, "enctype") == 0) && (strcmp(value, "base64") == 0) ) { isencoded = 1; } else { a = xar_attr_new(); XAR_ATTR(a)->key = strdup(name); XAR_ATTR(a)->value = strdup(value); if(ns) XAR_ATTR(a)->ns = strdup(ns); XAR_ATTR(a)->next = XAR_PROP(p)->attrs; XAR_PROP(p)->attrs = a; } } } if( isempty ) return 0; while( xmlTextReaderRead(reader) == 1) { type = xmlTextReaderNodeType(reader); switch(type) { case XML_READER_TYPE_ELEMENT: xar_prop_unserialize(f, p, reader); break; case XML_READER_TYPE_TEXT: value = (const char *)xmlTextReaderConstValue(reader); free((char*)XAR_PROP(p)->value); if( isencoded ) XAR_PROP(p)->value = (const char *)xar_from_base64(BAD_CAST(value), strlen(value)); else XAR_PROP(p)->value = strdup(value); if( isname ) { if( XAR_FILE(f)->parent ) { asprintf((char **)&XAR_FILE(f)->fspath, "%s/%s", XAR_FILE(XAR_FILE(f)->parent)->fspath, XAR_PROP(p)->value); } else { XAR_FILE(f)->fspath = strdup(XAR_PROP(p)->value); } } break; case XML_READER_TYPE_END_ELEMENT: return 0; break; } } /* XXX: Should never be reached */ return 0; } /* xar_file_unserialize * x: archive we're unserializing to * parent: The parent file of the file to be unserialized. May be NULL * reader: The xmlTextReaderPtr we are reading the xml from. * Summary: Takes a node, and adds all attributes, child properties, * and child files. */ xar_file_t xar_file_unserialize(xar_t x, xar_file_t parent, xmlTextReaderPtr reader) { xar_file_t ret; const char *name; int type, i; ret = xar_file_new(parent); i = xmlTextReaderAttributeCount(reader); if( i > 0 ) { for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) { xar_attr_t a; const char *name = (const char *)xmlTextReaderConstLocalName(reader); const char *value = (const char *)xmlTextReaderConstValue(reader); a = xar_attr_new(); XAR_ATTR(a)->key = strdup(name); XAR_ATTR(a)->value = strdup(value); XAR_ATTR(a)->next = XAR_FILE(ret)->attrs; XAR_FILE(ret)->attrs = a; } } while( xmlTextReaderRead(reader) == 1 ) { type = xmlTextReaderNodeType(reader); name = (const char *)xmlTextReaderConstLocalName(reader); if( (type == XML_READER_TYPE_END_ELEMENT) && (strcmp(name, "file")==0) ) { const char *opt; xar_prop_get(ret, "type", &opt); if( opt && (strcmp(opt, "hardlink") == 0) ) { opt = xar_attr_get(ret, "type", "link"); if( opt && (strcmp(opt, "original") == 0) ) { opt = xar_attr_get(ret, NULL, "id"); xmlHashAddEntry(XAR(x)->link_hash, BAD_CAST(opt), XAR_FILE(ret)); } } return ret; } if( type == XML_READER_TYPE_ELEMENT ) { if( strcmp(name, "file")==0 ) { xar_file_unserialize(x, ret, reader); } else xar_prop_unserialize(ret, NULL, reader); } } /* XXX Should never be reached */ return ret; }