From 20e2c9d37821570fda9054c9d445f794c0725e22 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Thu, 14 Mar 2019 04:35:49 +0800 Subject: [PATCH 01/22] Implement xlsxioread_sheet_next_cell_struct with cell types (First commit message, yay!) --- .gitignore | 5 + examples/example_xlsxio_read.c | 12 +- include/utarray.h | 238 +++++++ include/uthash.h | 1227 ++++++++++++++++++++++++++++++++ include/xlsxio_read.h | 24 + lib/xlsxio_read.c | 347 +++++++-- 6 files changed, 1807 insertions(+), 46 deletions(-) create mode 100755 include/utarray.h create mode 100755 include/uthash.h diff --git a/.gitignore b/.gitignore index 47ac5bc..a3056c5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.URL *.a *.so +*.dylib obj/ bin/ doc/ @@ -18,3 +19,7 @@ example_xlsxio_write example_xlsxio_write_getversion xlsxio_csv2xlsx xlsxio_xlsx2csv +lib/*.o +src/*.o +examples/*.o + diff --git a/examples/example_xlsxio_read.c b/examples/example_xlsxio_read.c index a5456a5..f3294d7 100644 --- a/examples/example_xlsxio_read.c +++ b/examples/example_xlsxio_read.c @@ -134,14 +134,20 @@ int main (int argc, char* argv[]) } xlsxioread_sheetlist_close(sheetlist); } + XML_Char_printf(X("\n")); //read values from first sheet - XLSXIOCHAR* value; + xlsxioread_cell value; + const char *typeNames[] = { "none", "value", "boolean", "string", "date" }; + printf("Contents of first sheet:\n"); xlsxioreadersheet sheet = xlsxioread_sheet_open(xlsxioread, NULL, XLSXIOREAD_SKIP_EMPTY_ROWS); while (xlsxioread_sheet_next_row(sheet)) { - while ((value = xlsxioread_sheet_next_cell(sheet)) != NULL) { - XML_Char_printf(X("%s\t"), value); + while ((value = xlsxioread_sheet_next_cell_struct(sheet)) != NULL) { + XML_Char_printf(X("%s"), value->data); + // print with row num, cell num and type + // XML_Char_printf("cell %zu %zu %s %s %s\n", value->row_num, value->col_num, + // typeNames[value->cell_type], value->number_fmt, value->data); free(value); } printf("\n"); diff --git a/include/utarray.h b/include/utarray.h new file mode 100755 index 0000000..6ed0dce --- /dev/null +++ b/include/utarray.h @@ -0,0 +1,238 @@ +/* +Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +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. +*/ + +/* a dynamic array implementation using macros + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 2.1.0 + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#ifdef __GNUC__ +#define UTARRAY_UNUSED __attribute__((__unused__)) +#else +#define UTARRAY_UNUSED +#endif + +#ifndef oom +#define oom() exit(-1) +#endif + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd = *(_icd); \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + (a) = (UT_array*)malloc(sizeof(UT_array)); \ + if ((a) == NULL) oom(); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+(by)) > (a)->n) { \ + char *utarray_tmp; \ + while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ + if (utarray_tmp == NULL) oom(); \ + (a)->d=utarray_tmp; \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((a)->d + ((a)->icd.sz * (j))) + +#define utarray_insert(a,p,j) do { \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + unsigned _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + unsigned _ut_i; \ + if ((dst)->i > (unsigned)(num)) { \ + if ((dst)->icd.dtor) { \ + for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ + (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ + } \ + } \ + } else if ((dst)->i < (unsigned)(num)) { \ + utarray_reserve(dst, (num) - (dst)->i); \ + if ((dst)->icd.init) { \ + for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ + (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ + } \ + } \ + (dst)->i = (num); \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta(dst, src, utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ + } \ + } \ + if ((a)->i > ((pos) + (len))) { \ + memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ + ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new(a, u); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc != NULL) free(*eltc); +} +static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/include/uthash.h b/include/uthash.h new file mode 100755 index 0000000..76bdca6 --- /dev/null +++ b/include/uthash.h @@ -0,0 +1,1227 @@ +/* +Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +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. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.1.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined(_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#elif defined(__GNUC__) && !defined(__VXWORKS__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifdef uthash_memcmp +/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */ +#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead" +#else +#define uthash_memcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FCN(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6bu; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35u; \ + _h ^= _h >> 16; \ +} while (0) + +#define HASH_MUR(key,keylen,hashv) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (int)(keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353u; \ + uint32_t _mur_c1 = 0xcc9e2d51u; \ + uint32_t _mur_c2 = 0x1b873593u; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ + int _mur_i; \ + for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ + _mur_k1=0; \ + switch ((keylen) & 3U) { \ + case 0: break; \ + case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ + case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ + case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (uint32_t)(keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ +} while (0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/include/xlsxio_read.h b/include/xlsxio_read.h index b3d05a0..1b0f3f9 100644 --- a/include/xlsxio_read.h +++ b/include/xlsxio_read.h @@ -94,6 +94,28 @@ DLL_EXPORT_XLSXIO const XLSXIOCHAR* xlsxioread_get_version_string (); /*! \brief read handle for .xlsx object */ typedef struct xlsxio_read_struct* xlsxioreader; +DLL_EXPORT_XLSXIO typedef enum { + cell_type_none, + cell_type_value, + cell_type_boolean, + cell_type_string, + cell_type_date +} cell_type_enum; + +struct data_sheet_cell_data { + size_t row_num; + size_t col_num; + size_t cols; + char* data; + size_t datalen; + cell_type_enum cell_type; + char* number_fmt; +}; + +typedef struct data_sheet_cell_data* xlsxioread_cell; + +//typedef struct data_sheet_callback_data* xlsxioread_cell; + /*! \brief open .xlsx file * \param filename path of .xlsx file to open * \return read handle for .xlsx object or NULL on error @@ -254,6 +276,8 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_row (xlsxioreadersheet sheethandle); */ DLL_EXPORT_XLSXIO XLSXIOCHAR* xlsxioread_sheet_next_cell (xlsxioreadersheet sheethandle); +DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct (xlsxioreadersheet sheethandle); + /*! \brief get next cell from worksheet as a string * \param sheethandle read handle for worksheet object * \param pvalue pointer where string will be stored if data is available (caller must free the result) diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 5d33705..510a5f6 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -6,6 +6,8 @@ #include #include #include +#include "uthash.h" +#include "utarray.h" #ifdef USE_MINIZIP # include @@ -37,6 +39,10 @@ #define DLL_EXPORT_XLSXIO #endif +#if defined(_MSC_VER) || (defined(__MINGW32__) && !defined(__MINGW64__)) +#define strcasecmp _stricmp +#endif + #define PARSE_BUFFER_SIZE 256 //#define PARSE_BUFFER_SIZE 4 @@ -181,7 +187,7 @@ int expat_process_zip_file (ZIPFILETYPE* zip, const XML_Char* filename, XML_Star #endif //return (status == XML_STATUS_ERROR != XML_ERROR_FINISHED ? 1 : 0); return 0; -} +} XML_Parser expat_process_zip_file_suspendable (ZIPFILEENTRYTYPE* zipfile, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler, XML_CharacterDataHandler data_handler, void* callbackdata) { @@ -246,7 +252,7 @@ int XML_Char_icmp_ins (const XML_Char* value, const XML_Char* name) return -1; } #endif - + //get expat attribute by name, returns NULL if not found const XML_Char* get_expat_attr_by_name (const XML_Char** atts, const XML_Char* name) { @@ -347,6 +353,19 @@ size_t get_row_nr (const XML_Char* A1col) //////////////////////////////////////////////////////////////////////// +struct date_struct { + int id; /* key */ + int isDate; + XML_Char* fmt; + UT_hash_handle hh; /* makes this structure hashable */ +}; + +struct date_struct *g_date_formats = NULL; /* important! initialize to NULL */ + +UT_array *g_number_formats = NULL; + +void get_styles(xlsxioreader handle); + struct xlsxio_read_struct { ZIPFILETYPE* zip; }; @@ -364,6 +383,10 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open (const char* filename) return NULL; } } + + HASH_CLEAR(hh, g_date_formats); + get_styles(result); + return result; } @@ -378,8 +401,8 @@ voidpf ZCALLBACK minizip_io_filehandle_open_file_fn (voidpf opaque, const char* return NULL; return &((struct minizip_io_filehandle_data*)opaque)->filehandle; } - -uLong ZCALLBACK minizip_io_filehandle_read_file_fn (voidpf opaque, voidpf stream, void* buf, uLong size) + +uLong ZCALLBACK minizip_io_filehandle_read_file_fn (voidpf opaque, voidpf stream, void* buf, uLong size) { IOSIZETYPE len; if (!opaque || !stream || !buf || size == 0) @@ -389,32 +412,32 @@ uLong ZCALLBACK minizip_io_filehandle_read_file_fn (voidpf opaque, voidpf stream return len; } -/* -uLong ZCALLBACK minizip_io_filehandle_write_file_fn (voidpf opaque, voidpf stream, const void* buf, uLong size) +/* +uLong ZCALLBACK minizip_io_filehandle_write_file_fn (voidpf opaque, voidpf stream, const void* buf, uLong size) { return 0; } */ - -int ZCALLBACK minizip_io_filehandle_close_file_fn (voidpf opaque, voidpf stream) + +int ZCALLBACK minizip_io_filehandle_close_file_fn (voidpf opaque, voidpf stream) { if (stream) close(*(int*)stream); free(opaque); return 0; } - -int ZCALLBACK minizip_io_filehandle_testerror_file_fn (voidpf opaque, voidpf stream) + +int ZCALLBACK minizip_io_filehandle_testerror_file_fn (voidpf opaque, voidpf stream) { return 0; } - -long ZCALLBACK minizip_io_filehandle_tell_file_fn (voidpf opaque, voidpf stream) + +long ZCALLBACK minizip_io_filehandle_tell_file_fn (voidpf opaque, voidpf stream) { return IOFN(lseek)(*(int*)stream, 0, SEEK_CUR); } - -long ZCALLBACK minizip_io_filehandle_seek_file_fn (voidpf opaque, voidpf stream, uLong offset, int origin) + +long ZCALLBACK minizip_io_filehandle_seek_file_fn (voidpf opaque, voidpf stream, uLong offset, int origin) { int whence; if (!opaque || !stream) @@ -465,6 +488,10 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_filehandle (int filehandle) } #endif } + + HASH_CLEAR(hh, g_date_formats); + get_styles(result); + return result; } @@ -489,8 +516,8 @@ voidpf ZCALLBACK minizip_io_memory_open_file_fn (voidpf opaque, const char* file } return result; } - -uLong ZCALLBACK minizip_io_memory_read_file_fn (voidpf opaque, voidpf stream, void* buf, uLong size) + +uLong ZCALLBACK minizip_io_memory_read_file_fn (voidpf opaque, voidpf stream, void* buf, uLong size) { uLong len; if (!opaque || !stream || !buf || size == 0) @@ -504,14 +531,14 @@ uLong ZCALLBACK minizip_io_memory_read_file_fn (voidpf opaque, voidpf stream, vo return len; } -/* -uLong ZCALLBACK minizip_io_memory_write_file_fn (voidpf opaque, voidpf stream, const void* buf, uLong size) +/* +uLong ZCALLBACK minizip_io_memory_write_file_fn (voidpf opaque, voidpf stream, const void* buf, uLong size) { return 0; } */ - -int ZCALLBACK minizip_io_memory_close_file_fn (voidpf opaque, voidpf stream) + +int ZCALLBACK minizip_io_memory_close_file_fn (voidpf opaque, voidpf stream) { free(stream); if (opaque && ((struct minizip_io_memory_data*)opaque)->freedata) @@ -519,20 +546,20 @@ int ZCALLBACK minizip_io_memory_close_file_fn (voidpf opaque, voidpf stream) free(opaque); return 0; } - -int ZCALLBACK minizip_io_memory_testerror_file_fn (voidpf opaque, voidpf stream) + +int ZCALLBACK minizip_io_memory_testerror_file_fn (voidpf opaque, voidpf stream) { return 0; } - -long ZCALLBACK minizip_io_memory_tell_file_fn (voidpf opaque, voidpf stream) + +long ZCALLBACK minizip_io_memory_tell_file_fn (voidpf opaque, voidpf stream) { if (!opaque || !stream) return 0; return ((struct minizip_io_memory_handle*)stream)->pos; } - -long ZCALLBACK minizip_io_memory_seek_file_fn (voidpf opaque, voidpf stream, uLong offset, int origin) + +long ZCALLBACK minizip_io_memory_seek_file_fn (voidpf opaque, voidpf stream, uLong offset, int origin) { switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : @@ -614,6 +641,10 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data } } #endif + + HASH_CLEAR(hh, g_date_formats); + get_styles(result); + return result; } @@ -628,6 +659,8 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) #endif free(handle); } + HASH_CLEAR(hh, g_date_formats); + utarray_free(g_number_formats); } //////////////////////////////////////////////////////////////////////// @@ -786,6 +819,82 @@ struct main_sheet_get_rels_callback_data { XML_Char* stylesfile; }; +struct styles_callback_data { + XML_Parser xmlparser; + int lookForStyles; + int lookForDates; +}; + + +int is_date_format(const XML_Char* ch) { + // 'Red' is used in Excel general formats, for its 'd' not to be interpreted as 'd' in day the string is removed + char* pRed; + if ((pRed = strstr(ch, "Red")) != NULL) { + pRed[0] = pRed[1] = pRed[2] = ' '; + } + + if (strstr(ch, "y")) // year + return 1; + if (strstr(ch, "m")) // month/minute + return 1; + if (strstr(ch, "d")) // day + return 1; + if (strstr(ch, "h")) // hour + return 1; + if (strstr(ch, "s")) // second + return 1; + + return 0; +} + +void format_types_start_callback(void* callbackdata, const XML_Char* name, const XML_Char** atts) { + struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; + + if (strcmp(name, "cellXfs") == 0) { + pdata->lookForStyles = 1; + } else if (strcmp(name, "numFmts") == 0) { + pdata->lookForDates = 1; + } else if (pdata->lookForStyles && strcmp(name, "xf") == 0) { + const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + int iVal = atoi(numFmtId); + utarray_push_back(g_number_formats, &iVal); + } else if (strcmp(name, "numFmt") == 0) { + const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + struct date_struct *dateStruct; + dateStruct = malloc(sizeof(struct date_struct)); + numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + dateStruct->id = atoi(numFmtId); + + const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); + + dateStruct->isDate = is_date_format(formatCode); + dateStruct->fmt = XML_Char_dupchar(formatCode); + HASH_ADD_INT(g_date_formats, id, dateStruct); /* id: name of key field */ + } else if (pdata->lookForDates && strcmp(name, "xf") == 0) { + const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + struct date_struct *dateStruct; + dateStruct = malloc(sizeof(struct date_struct)); + numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + dateStruct->id = atoi(numFmtId); + + const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); + + dateStruct->isDate = is_date_format(formatCode); + dateStruct->fmt = XML_Char_dupchar(formatCode); + HASH_ADD_INT(g_date_formats, id, dateStruct); /* id: name of key field */ + } +} + +void format_types_end_callback(void* callbackdata, const XML_Char* name) { + if (strcmp(name, "cellXfs") == 0) { + struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; + pdata->lookForStyles = 0; + } else if (strcmp(name, "numFmts") == 0) { + struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; + pdata->lookForDates = 0; + } +} + //determine relationship id for specific sheet name void main_sheet_get_relid_expat_callback_element_start (void* callbackdata, const XML_Char* name, const XML_Char** atts) { @@ -888,6 +997,8 @@ struct data_sheet_callback_data { XML_Char* celldata; size_t celldatalen; cell_string_type_enum cell_string_type; + cell_type_enum cell_type; + XML_Char* number_fmt; unsigned int flags; XML_Char* skiptag; //tag to skip size_t skiptagcount; //nesting level for current tag to skip @@ -899,6 +1010,18 @@ struct data_sheet_callback_data { void* callbackdata; }; +// public struct +//#ifndef data_sheet_cell_data +// struct data_sheet_cell_data { +// size_t row_num; +// size_t col_num; +// size_t cols; +// XML_Char* data; +// size_t datalen; +// cell_type_enum cell_type; +// }; +//#endif + void data_sheet_callback_data_initialize (struct data_sheet_callback_data* data, struct sharedstringlist* sharedstrings, unsigned int flags, xlsxioread_process_cell_callback_fn cell_callback, xlsxioread_process_row_callback_fn row_callback, void* callbackdata) { data->xmlparser = NULL; @@ -909,6 +1032,8 @@ void data_sheet_callback_data_initialize (struct data_sheet_callback_data* data, data->celldata = NULL; data->celldatalen = 0; data->cell_string_type = none; + data->cell_type = cell_type_none; + data->number_fmt = NULL; data->flags = flags; data->skiptag = NULL; data->skiptagcount = 0; @@ -1064,11 +1189,13 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch if (XML_Char_icmp_ins(name, X("c")) == 0) { const XML_Char* t = get_expat_attr_by_name(atts, X("r")); size_t cellcolnr = get_col_nr(t); + //skip everything when out of bounds if (cellcolnr && data->cols && (data->flags & XLSXIOREAD_SKIP_EXTRA_CELLS) && cellcolnr > data->cols) { data->colnr = cellcolnr - 1; return; } + //insert empty rows if needed if (data->colnr == 0) { size_t cellrownr = get_row_nr(t); @@ -1100,6 +1227,7 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch } } } + //insert empty columns if needed if (cellcolnr) { cellcolnr--; @@ -1117,12 +1245,70 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch } } } + //determing value type - if ((t = get_expat_attr_by_name(atts, X("t"))) != NULL && XML_Char_icmp(t, X("s")) == 0) + t = get_expat_attr_by_name(atts, X("t")); + if (t != NULL && XML_Char_icmp(t, X("s")) == 0) { data->cell_string_type = shared_string; - else + } else { data->cell_string_type = value_string; - //prepare empty value data + } + + data->cell_type = cell_type_none; + data->number_fmt = NULL; + + // determing cell data type + if (t != NULL) { + if (strcasecmp(t, "s") == 0 || strcasecmp(t, "str") == 0 || strcasecmp(t, "inlineStr") == 0) { + data->cell_type = cell_type_string; + } else if (strcasecmp(t, "b") == 0) { + data->cell_type = cell_type_boolean; + } else if (strcasecmp(t, "n") == 0) { + data->cell_type = cell_type_value; + } else { + data->cell_type = cell_type_none; // We should never get here! + } + } else { + // + // date field may look something like this (excel dates is a number of days from 1st jan 1900) + // + // 43537.51777806081 + // + // + // s="2" is a number of format that described in styles.xml + // if related format has "y" or "m" or "d" or "h" or "s" then it's date/time format + // + + const XML_Char* s; + s = get_expat_attr_by_name(atts, "s"); + + if (s == NULL) { + data->cell_type = cell_type_value; + } else { + int formatIndex = atoi(s); + int formatNum = *(int*)utarray_eltptr(g_number_formats, formatIndex); + + struct date_struct *dateStruct = NULL; + + HASH_FIND_INT(g_date_formats, &formatNum, dateStruct); + + int isDateTime = dateStruct ? dateStruct->isDate : 0; + + // builtin date formats are 14 - 22 + if ((formatNum > 13 && formatNum < 23) || isDateTime) { + data->cell_type = cell_type_date; + } else { + data->cell_type = cell_type_value; + } + + // apply for dates and numbers + if (dateStruct) { + data->number_fmt = dateStruct->fmt; + } + } + } + + //prepare empty value data, it will be changed in *_end callback free(data->celldata); data->celldata = NULL; data->celldatalen = 0; @@ -1228,6 +1414,33 @@ void data_sheet_expat_callback_value_data (void* callbackdata, const XML_Char* b } } +void get_styles(xlsxioreader handle) { + struct styles_callback_data stylescallbackdata; + stylescallbackdata.lookForStyles = 0; + stylescallbackdata.lookForDates = 0; + utarray_new(g_number_formats, &ut_int_icd); + + expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylescallbackdata, NULL); + + // debugging for extracting formats from styles.xml + /* + printf("got styles\n"); + + int *p, i = 0; + for (p = (int*)utarray_front(g_number_formats); p != NULL; p = (int*)utarray_next(g_number_formats, p)) { + printf(" g_number_formats %i - %d\n", i, *p); + i += 1; + } + + struct date_struct *dateStruct = NULL; + + for (dateStruct=g_date_formats; dateStruct != NULL; dateStruct=(struct dateStruct*)(dateStruct->hh.next)) { + printf(" dateStruct id %d isDate %d fmt %s\n", dateStruct->id, dateStruct->isDate, dateStruct->fmt); + } + */ +} + + //////////////////////////////////////////////////////////////////////// struct xlsxio_read_sheet_struct { @@ -1431,11 +1644,22 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_row (xlsxioreadersheet sheethandle) return (status == XML_STATUS_SUSPENDED ? 1 : 0); } -DLL_EXPORT_XLSXIO XLSXIOCHAR* xlsxioread_sheet_next_cell (xlsxioreadersheet sheethandle) +DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct (xlsxioreadersheet sheethandle) { XML_Char* result; - if (!sheethandle) + if (!sheethandle) { return NULL; + } + + struct data_sheet_cell_data *res; + res = (struct data_sheet_cell_data *) malloc(sizeof(struct data_sheet_cell_data)); + res->row_num = sheethandle->processcallbackdata.rownr; + res->col_num = sheethandle->lastcolnr; //sheethandle->processcallbackdata.colnr; + res->cols = sheethandle->processcallbackdata.cols; + res->datalen = 0; + res->number_fmt = sheethandle->processcallbackdata.number_fmt; + res->cell_type = sheethandle->processcallbackdata.cell_type; + //append empty column if needed if (sheethandle->paddingcol) { if (sheethandle->paddingcol > sheethandle->processcallbackdata.cols) { @@ -1453,41 +1677,78 @@ DLL_EXPORT_XLSXIO XLSXIOCHAR* xlsxioread_sheet_next_cell (xlsxioreadersheet shee } else { //add another empty column sheethandle->paddingcol++; - return XML_Char_dup(X("")); + //return XML_Char_dup(X("")); + + //sheethandle->processcallbackdata.celldata = XML_Char_dup(X("")); + //return &sheethandle->processcallbackdata; + + res->data = XML_Char_dup(X("")); + return res; } } + //get value - if (!sheethandle->processcallbackdata.celldata) - if (expat_process_zip_file_resume(sheethandle->zipfile, sheethandle->processcallbackdata.xmlparser) != XML_STATUS_SUSPENDED) + if (!sheethandle->processcallbackdata.celldata) { + if (expat_process_zip_file_resume(sheethandle->zipfile, sheethandle->processcallbackdata.xmlparser) != XML_STATUS_SUSPENDED) { sheethandle->processcallbackdata.celldata = NULL; + res->data = NULL; + } + } + //insert empty rows if needed if (!(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_ROWS) && sheethandle->lastrownr + 1 < sheethandle->processcallbackdata.rownr) { sheethandle->paddingrow = sheethandle->lastrownr + 1; - sheethandle->paddingcol = sheethandle->processcallbackdata.colnr*0 + 1; - return xlsxioread_sheet_next_cell(sheethandle); + sheethandle->paddingcol = sheethandle->processcallbackdata.colnr * 0 + 1; + + return xlsxioread_sheet_next_cell_struct(sheethandle); } + //insert empty column before if needed if (!(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_CELLS)) { if (sheethandle->lastcolnr + 1 < sheethandle->processcallbackdata.colnr) { sheethandle->lastcolnr++; - return XML_Char_dup(X("")); + //return XML_Char_dup(X("")); + //sheethandle->processcallbackdata.celldata = XML_Char_dup(X("")); + //return &sheethandle->processcallbackdata; + + res->data = XML_Char_dup(X("")); + return res; } } - result = sheethandle->processcallbackdata.celldata; + + res->data = sheethandle->processcallbackdata.celldata; sheethandle->processcallbackdata.celldata = NULL; + res->number_fmt = sheethandle->processcallbackdata.number_fmt; + res->cell_type = sheethandle->processcallbackdata.cell_type; + //end of row - if (!result) { + if (!res->data) { sheethandle->lastrownr = sheethandle->processcallbackdata.rownr; - //insert empty column at end if row if needed - if (!result && !(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_CELLS) && sheethandle->processcallbackdata.colnr < sheethandle->processcallbackdata.cols) { + //insert empty column at end of row if needed + if (!res->data && !(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_CELLS) && sheethandle->processcallbackdata.colnr < sheethandle->processcallbackdata.cols) { sheethandle->paddingcol = sheethandle->lastcolnr + 1; - return xlsxioread_sheet_next_cell(sheethandle); + sheethandle->processcallbackdata.colnr += 1; + + return xlsxioread_sheet_next_cell_struct(sheethandle); } } sheethandle->lastcolnr = sheethandle->processcallbackdata.colnr; - return result; + + return res->data ? res : NULL; + //return result; +} + +DLL_EXPORT_XLSXIO XLSXIOCHAR* xlsxioread_sheet_next_cell (xlsxioreadersheet sheethandle) { + struct data_sheet_cell_data* cell_struct = xlsxioread_sheet_next_cell_struct(sheethandle); + + if (cell_struct) { + return cell_struct->data; + } else { + return NULL; + } } + DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_cell_string (xlsxioreadersheet sheethandle, XLSXIOCHAR** pvalue) { XML_Char* result; From 93085f562b4072b8476f9858f6069d7b160161ae Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Thu, 14 Mar 2019 22:34:10 +0800 Subject: [PATCH 02/22] Store number & date formats inside reader handle Move structs to the top Add xlsxioread_debug_internals(xlsxioreader) Add xlsxio_read_struct in xlsxio_read.h (now its with alias and without) --- examples/example_xlsxio_read.c | 22 +- include/xlsxio_read.h | 3 + lib/xlsxio_read.c | 487 ++++++++++++++++++--------------- 3 files changed, 275 insertions(+), 237 deletions(-) diff --git a/examples/example_xlsxio_read.c b/examples/example_xlsxio_read.c index f3294d7..148c612 100644 --- a/examples/example_xlsxio_read.c +++ b/examples/example_xlsxio_read.c @@ -54,11 +54,11 @@ THE SOFTWARE. #include "xlsxio_read.h" #if !defined(XML_UNICODE_WCHAR_T) && !defined(XML_UNICODE) -//UTF-8 version +// UTF-8 version #define X(s) s #define XML_Char_printf printf #else -//UTF-16 version +// UTF-16 version #define X(s) L##s #define XML_Char_printf wprintf #endif @@ -68,7 +68,7 @@ const char* filename = "example.xlsx"; int main (int argc, char* argv[]) { #ifdef _WIN32 - //switch Windows console to UTF-8 + // switch Windows console to UTF-8 SetConsoleOutputCP(CP_UTF8); #endif @@ -81,7 +81,7 @@ int main (int argc, char* argv[]) #if defined(PROCESS_FROM_MEMORY) { - //load file in memory + // load file in memory int filehandle; char* buf = NULL; size_t buflen = 0; @@ -106,7 +106,7 @@ int main (int argc, char* argv[]) } } #elif defined(PROCESS_FROM_FILEHANDLE) - //open .xlsx file for reading + // open .xlsx file for reading int filehandle; if ((filehandle = open(filename, O_RDONLY | O_BINARY, 0)) == -1) { fprintf(stderr, "Error opening .xlsx file\n"); @@ -117,14 +117,14 @@ int main (int argc, char* argv[]) return 1; } #else - //open .xlsx file for reading + // open .xlsx file for reading if ((xlsxioread = xlsxioread_open(filename)) == NULL) { fprintf(stderr, "Error opening .xlsx file\n"); return 1; } #endif - //list available sheets + // list available sheets xlsxioreadersheetlist sheetlist; const XLSXIOCHAR* sheetname; printf("Available sheets:\n"); @@ -136,7 +136,7 @@ int main (int argc, char* argv[]) } XML_Char_printf(X("\n")); - //read values from first sheet + // read values from first sheet xlsxioread_cell value; const char *typeNames[] = { "none", "value", "boolean", "string", "date" }; @@ -144,9 +144,9 @@ int main (int argc, char* argv[]) xlsxioreadersheet sheet = xlsxioread_sheet_open(xlsxioread, NULL, XLSXIOREAD_SKIP_EMPTY_ROWS); while (xlsxioread_sheet_next_row(sheet)) { while ((value = xlsxioread_sheet_next_cell_struct(sheet)) != NULL) { - XML_Char_printf(X("%s"), value->data); + XML_Char_printf(X("%s\t"), value->data); // print with row num, cell num and type - // XML_Char_printf("cell %zu %zu %s %s %s\n", value->row_num, value->col_num, + //XML_Char_printf("cell %zu %zu %s %s %s\n", value->row_num, value->col_num, // typeNames[value->cell_type], value->number_fmt, value->data); free(value); } @@ -154,7 +154,7 @@ int main (int argc, char* argv[]) } xlsxioread_sheet_close(sheet); - //clean up + // clean up xlsxioread_close(xlsxioread); return 0; } diff --git a/include/xlsxio_read.h b/include/xlsxio_read.h index 1b0f3f9..a3ad899 100644 --- a/include/xlsxio_read.h +++ b/include/xlsxio_read.h @@ -93,6 +93,7 @@ DLL_EXPORT_XLSXIO const XLSXIOCHAR* xlsxioread_get_version_string (); /*! \brief read handle for .xlsx object */ typedef struct xlsxio_read_struct* xlsxioreader; +typedef struct xlsxio_read_struct xlsxio_read_struct; DLL_EXPORT_XLSXIO typedef enum { cell_type_none, @@ -314,6 +315,8 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_cell_float (xlsxioreadersheet sheeth */ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_cell_datetime (xlsxioreadersheet sheethandle, time_t* pvalue); +void xlsxioread_debug_internals(xlsxioreader handle); + #ifdef __cplusplus } #endif diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 510a5f6..a975604 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -123,22 +123,6 @@ static ZIPFILEENTRYTYPE* XML_Char_openzip (ZIPFILETYPE* archive, const XML_Char* #endif - -DLL_EXPORT_XLSXIO void xlsxioread_get_version (int* pmajor, int* pminor, int* pmicro) -{ - if (pmajor) - *pmajor = XLSXIO_VERSION_MAJOR; - if (pminor) - *pminor = XLSXIO_VERSION_MINOR; - if (pmicro) - *pmicro = XLSXIO_VERSION_MICRO; -} - -DLL_EXPORT_XLSXIO const XLSXIOCHAR* xlsxioread_get_version_string () -{ - return (const XLSXIOCHAR*)XLSXIO_VERSION_STRING; -} - //////////////////////////////////////////////////////////////////////// //process XML file contents @@ -351,46 +335,8 @@ size_t get_row_nr (const XML_Char* A1col) return result; } -//////////////////////////////////////////////////////////////////////// - -struct date_struct { - int id; /* key */ - int isDate; - XML_Char* fmt; - UT_hash_handle hh; /* makes this structure hashable */ -}; - -struct date_struct *g_date_formats = NULL; /* important! initialize to NULL */ - -UT_array *g_number_formats = NULL; - -void get_styles(xlsxioreader handle); - -struct xlsxio_read_struct { - ZIPFILETYPE* zip; -}; - -DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open (const char* filename) -{ - xlsxioreader result; - if ((result = (xlsxioreader)malloc(sizeof(struct xlsxio_read_struct))) != NULL) { #ifdef USE_MINIZIP - if ((result->zip = unzOpen(filename)) == NULL) { -#else - if ((result->zip = zip_open(filename, ZIP_RDONLY, NULL)) == NULL) { -#endif - free(result); - return NULL; - } - } - HASH_CLEAR(hh, g_date_formats); - get_styles(result); - - return result; -} - -#ifdef USE_MINIZIP struct minizip_io_filehandle_data { int filehandle; }; @@ -457,45 +403,7 @@ long ZCALLBACK minizip_io_filehandle_seek_file_fn (voidpf opaque, voidpf stream, } return (IOFN(lseek)(*(int*)stream, offset, whence) >= 0 ? 0 : -1); } -#endif - -DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_filehandle (int filehandle) -{ - xlsxioreader result; - if ((result = (xlsxioreader)malloc(sizeof(struct xlsxio_read_struct))) != NULL) { -#ifdef USE_MINIZIP - zlib_filefunc_def minizip_io_filehandle_functions; - if ((minizip_io_filehandle_functions.opaque = malloc(sizeof(struct minizip_io_filehandle_data))) == NULL) { - free(result); - return NULL; - } - minizip_io_filehandle_functions.zopen_file = minizip_io_filehandle_open_file_fn; - minizip_io_filehandle_functions.zread_file = minizip_io_filehandle_read_file_fn; - minizip_io_filehandle_functions.zwrite_file = /*minizip_io_filehandle_write_file_fn*/NULL; - minizip_io_filehandle_functions.ztell_file = minizip_io_filehandle_tell_file_fn; - minizip_io_filehandle_functions.zseek_file = minizip_io_filehandle_seek_file_fn; - minizip_io_filehandle_functions.zclose_file = minizip_io_filehandle_close_file_fn; - minizip_io_filehandle_functions.zerror_file = minizip_io_filehandle_testerror_file_fn; - ((struct minizip_io_filehandle_data*)minizip_io_filehandle_functions.opaque)->filehandle = filehandle; - if ((result->zip = unzOpen2(NULL, &minizip_io_filehandle_functions)) == NULL) { - free(result); - return NULL; - } -#else - if ((result->zip = zip_fdopen(filehandle, ZIP_RDONLY, NULL)) == NULL) { - free(result); - return NULL; - } -#endif - } - - HASH_CLEAR(hh, g_date_formats); - get_styles(result); - - return result; -} -#ifdef USE_MINIZIP struct minizip_io_memory_data { void* data; uint64_t datalen; @@ -603,9 +511,202 @@ long ZCALLBACK minizip_io_memory_seek_file_fn (voidpf opaque, voidpf stream, uLo } #endif + +//////////////////////////////////////////////////////////////////////// + + +//// +// STRUCTS +//// + +// callback data structure used by main_sheet_get_sheetfile_callback +struct main_sheet_get_rels_callback_data { + XML_Parser xmlparser; + const XML_Char* sheetname; + XML_Char* basepath; + XML_Char* sheetrelid; + XML_Char* sheetfile; + XML_Char* sharedstringsfile; + XML_Char* stylesfile; +}; + +struct date_struct { + int id; /* key */ + int isDate; + XML_Char* fmt; + UT_hash_handle hh; /* makes this structure hashable */ +}; + +// aka xlsxioreader +struct xlsxio_read_struct { + ZIPFILETYPE* zip; + const char* filename; + struct date_struct *date_formats; + UT_array *number_formats; +}; + +struct styles_callback_data { + XML_Parser xmlparser; + int lookForStyles; + int lookForDates; + struct xlsxio_read_struct* handle; +}; + +typedef enum { + none, + value_string, + inline_string, + shared_string +} cell_string_type_enum; + +#define XLSXIOREAD_NO_CALLBACK 0x80 + +struct data_sheet_callback_data { + XML_Parser xmlparser; + struct xlsxio_read_struct* handle; + struct sharedstringlist* sharedstrings; + size_t rownr; + size_t colnr; + size_t cols; + XML_Char* celldata; + size_t celldatalen; + cell_string_type_enum cell_string_type; + cell_type_enum cell_type; + XML_Char* number_fmt; + unsigned int flags; + XML_Char* skiptag; //tag to skip + size_t skiptagcount; //nesting level for current tag to skip + XML_StartElementHandler skip_start; //start handler to set after skipping + XML_EndElementHandler skip_end; //end handler to set after skipping + XML_CharacterDataHandler skip_data; //data handler to set after skipping + xlsxioread_process_row_callback_fn sheet_row_callback; + xlsxioread_process_cell_callback_fn sheet_cell_callback; + void* callbackdata; +}; + +// callback function definition for use with iterate_files_by_contenttype +typedef void (*contenttype_file_callback_fn)(ZIPFILETYPE* zip, const XML_Char* filename, const XML_Char* contenttype, void* callbackdata); + +struct iterate_files_by_contenttype_callback_data { + ZIPFILETYPE* zip; + const XML_Char* contenttype; + contenttype_file_callback_fn filecallbackfn; + void* filecallbackdata; +}; + +struct xlsxio_read_sheet_struct { + xlsxioreader handle; + ZIPFILEENTRYTYPE* zipfile; + struct data_sheet_callback_data processcallbackdata; + size_t lastrownr; + size_t paddingrow; + size_t lastcolnr; + size_t paddingcol; +}; + +// callback structure used by main_sheet_list_expat_callback_element_start +struct main_sheet_list_callback_data { + XML_Parser xmlparser; + xlsxioread_list_sheets_callback_fn callback; + void* callbackdata; +}; + +struct xlsxio_read_sheetlist_struct { + xlsxioreader handle; + ZIPFILEENTRYTYPE* zipfile; + struct main_sheet_list_callback_data sheetcallbackdata; + XML_Parser xmlparser; + XML_Char* nextsheetname; +}; + +//// +// FUNCTIONS +//// + +DLL_EXPORT_XLSXIO void xlsxioread_get_version (int* pmajor, int* pminor, int* pmicro) +{ + if (pmajor) + *pmajor = XLSXIO_VERSION_MAJOR; + if (pminor) + *pminor = XLSXIO_VERSION_MINOR; + if (pmicro) + *pmicro = XLSXIO_VERSION_MICRO; +} + +DLL_EXPORT_XLSXIO const XLSXIOCHAR* xlsxioread_get_version_string () +{ + return (const XLSXIOCHAR*)XLSXIO_VERSION_STRING; +} + +void get_styles(xlsxioreader handle); + +DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open (const char* filename) +{ + xlsxioreader result; + + if ((result = (xlsxioreader)malloc(sizeof(struct xlsxio_read_struct))) != NULL) { +#ifdef USE_MINIZIP + if ((result->zip = unzOpen(filename)) == NULL) { +#else + if ((result->zip = zip_open(filename, ZIP_RDONLY, NULL)) == NULL) { +#endif + free(result); + return NULL; + } + } + + result->filename = filename; + result->number_formats = NULL; + result->date_formats = NULL; + + get_styles(result); + + return result; +} + +DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_filehandle (int filehandle) +{ + xlsxioreader result; + if ((result = (xlsxioreader)malloc(sizeof(struct xlsxio_read_struct))) != NULL) { +#ifdef USE_MINIZIP + zlib_filefunc_def minizip_io_filehandle_functions; + if ((minizip_io_filehandle_functions.opaque = malloc(sizeof(struct minizip_io_filehandle_data))) == NULL) { + free(result); + return NULL; + } + minizip_io_filehandle_functions.zopen_file = minizip_io_filehandle_open_file_fn; + minizip_io_filehandle_functions.zread_file = minizip_io_filehandle_read_file_fn; + minizip_io_filehandle_functions.zwrite_file = /*minizip_io_filehandle_write_file_fn*/NULL; + minizip_io_filehandle_functions.ztell_file = minizip_io_filehandle_tell_file_fn; + minizip_io_filehandle_functions.zseek_file = minizip_io_filehandle_seek_file_fn; + minizip_io_filehandle_functions.zclose_file = minizip_io_filehandle_close_file_fn; + minizip_io_filehandle_functions.zerror_file = minizip_io_filehandle_testerror_file_fn; + ((struct minizip_io_filehandle_data*)minizip_io_filehandle_functions.opaque)->filehandle = filehandle; + if ((result->zip = unzOpen2(NULL, &minizip_io_filehandle_functions)) == NULL) { + free(result); + return NULL; + } +#else + if ((result->zip = zip_fdopen(filehandle, ZIP_RDONLY, NULL)) == NULL) { + free(result); + return NULL; + } +#endif + } + + result->filename = "*filehandle*"; + result->number_formats = NULL; + result->date_formats = NULL; + + get_styles(result); + + return result; +} + DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t datalen, int freedata) { xlsxioreader result; + #ifdef USE_MINIZIP if ((result = (xlsxioreader)malloc(sizeof(struct xlsxio_read_struct))) != NULL) { zlib_filefunc_def minizip_io_memory_functions; @@ -642,7 +743,10 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data } #endif - HASH_CLEAR(hh, g_date_formats); + result->filename = "*memory*"; + result->number_formats = NULL; + result->date_formats = NULL; + get_styles(result); return result; @@ -651,31 +755,40 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) { if (handle) { + + // if we call "free()" more then once then it will crash with "pointer being freed was not allocated" + // so we can call xlsxioread_close multiple times + int needFreeItself = handle->zip != NULL || handle->date_formats != NULL || handle->number_formats != NULL; + //note: no need to call zip_source_free() after successful use in zip_open_from_source() + if (handle->zip) { #ifdef USE_MINIZIP - unzClose(handle->zip); + unzClose(handle->zip); #else - zip_close(handle->zip); + zip_close(handle->zip); #endif - free(handle); + handle->zip = NULL; + } + + if (handle->date_formats) { + HASH_CLEAR(hh, handle->date_formats); + handle->date_formats = NULL; + } + + if (handle->number_formats) { + utarray_free(handle->number_formats); + handle->number_formats = NULL; + } + + if (needFreeItself) { + free(handle); + } } - HASH_CLEAR(hh, g_date_formats); - utarray_free(g_number_formats); } //////////////////////////////////////////////////////////////////////// -//callback function definition for use with iterate_files_by_contenttype -typedef void (*contenttype_file_callback_fn)(ZIPFILETYPE* zip, const XML_Char* filename, const XML_Char* contenttype, void* callbackdata); - -struct iterate_files_by_contenttype_callback_data { - ZIPFILETYPE* zip; - const XML_Char* contenttype; - contenttype_file_callback_fn filecallbackfn; - void* filecallbackdata; -}; - -//expat callback function for element start used by iterate_files_by_contenttype +// expat callback function for element start used by iterate_files_by_contenttype void iterate_files_by_contenttype_expat_callback_element_start (void* callbackdata, const XML_Char* name, const XML_Char** atts) { struct iterate_files_by_contenttype_callback_data* data = (struct iterate_files_by_contenttype_callback_data*)callbackdata; @@ -739,7 +852,7 @@ unzGetGlobalInfo(data->zip, &zipglobalinfo); } } -//list file names by content type +// list file names by content type int iterate_files_by_contenttype (ZIPFILETYPE* zip, const XML_Char* contenttype, contenttype_file_callback_fn filecallbackfn, void* filecallbackdata, XML_Parser* xmlparser) { struct iterate_files_by_contenttype_callback_data callbackdata = { @@ -753,14 +866,7 @@ int iterate_files_by_contenttype (ZIPFILETYPE* zip, const XML_Char* contenttype, //////////////////////////////////////////////////////////////////////// -//callback structure used by main_sheet_list_expat_callback_element_start -struct main_sheet_list_callback_data { - XML_Parser xmlparser; - xlsxioread_list_sheets_callback_fn callback; - void* callbackdata; -}; - -//callback used by xlsxioread_list_sheets +// callback used by xlsxioread_list_sheets void main_sheet_list_expat_callback_element_start (void* callbackdata, const XML_Char* name, const XML_Char** atts) { struct main_sheet_list_callback_data* data = (struct main_sheet_list_callback_data*)callbackdata; @@ -785,14 +891,14 @@ void main_sheet_list_expat_callback_element_start (void* callbackdata, const XML } } -//process contents each sheet listed in main sheet +// process contents each sheet listed in main sheet void xlsxioread_list_sheets_callback (ZIPFILETYPE* zip, const XML_Char* filename, const XML_Char* contenttype, void* callbackdata) { - //get sheet information from file + // get sheet information from file expat_process_zip_file(zip, filename, main_sheet_list_expat_callback_element_start, NULL, NULL, callbackdata, &((struct main_sheet_list_callback_data*)callbackdata)->xmlparser); } -//list all worksheets +// list all worksheets DLL_EXPORT_XLSXIO void xlsxioread_list_sheets (xlsxioreader handle, xlsxioread_list_sheets_callback_fn callback, void* callbackdata) { if (!handle || !callback) @@ -808,24 +914,6 @@ DLL_EXPORT_XLSXIO void xlsxioread_list_sheets (xlsxioreader handle, xlsxioread_l //////////////////////////////////////////////////////////////////////// -//callback data structure used by main_sheet_get_sheetfile_callback -struct main_sheet_get_rels_callback_data { - XML_Parser xmlparser; - const XML_Char* sheetname; - XML_Char* basepath; - XML_Char* sheetrelid; - XML_Char* sheetfile; - XML_Char* sharedstringsfile; - XML_Char* stylesfile; -}; - -struct styles_callback_data { - XML_Parser xmlparser; - int lookForStyles; - int lookForDates; -}; - - int is_date_format(const XML_Char* ch) { // 'Red' is used in Excel general formats, for its 'd' not to be interpreted as 'd' in day the string is removed char* pRed; @@ -857,7 +945,7 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const } else if (pdata->lookForStyles && strcmp(name, "xf") == 0) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); int iVal = atoi(numFmtId); - utarray_push_back(g_number_formats, &iVal); + utarray_push_back(pdata->handle->number_formats, &iVal); } else if (strcmp(name, "numFmt") == 0) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); struct date_struct *dateStruct; @@ -869,7 +957,7 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const dateStruct->isDate = is_date_format(formatCode); dateStruct->fmt = XML_Char_dupchar(formatCode); - HASH_ADD_INT(g_date_formats, id, dateStruct); /* id: name of key field */ + HASH_ADD_INT(pdata->handle->date_formats, id, dateStruct); /* id: name of key field */ } else if (pdata->lookForDates && strcmp(name, "xf") == 0) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); struct date_struct *dateStruct; @@ -881,7 +969,7 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const dateStruct->isDate = is_date_format(formatCode); dateStruct->fmt = XML_Char_dupchar(formatCode); - HASH_ADD_INT(g_date_formats, id, dateStruct); /* id: name of key field */ + HASH_ADD_INT(pdata->handle->date_formats, id, dateStruct); /* id: name of key field */ } } @@ -895,7 +983,7 @@ void format_types_end_callback(void* callbackdata, const XML_Char* name) { } } -//determine relationship id for specific sheet name +// determine relationship id for specific sheet name void main_sheet_get_relid_expat_callback_element_start (void* callbackdata, const XML_Char* name, const XML_Char** atts) { struct main_sheet_get_rels_callback_data* data = (struct main_sheet_get_rels_callback_data*)callbackdata; @@ -912,7 +1000,7 @@ void main_sheet_get_relid_expat_callback_element_start (void* callbackdata, cons } } -//determine file names for specific relationship id +// determine file names for specific relationship id void main_sheet_get_sheetfile_expat_callback_element_start (void* callbackdata, const XML_Char* name, const XML_Char** atts) { struct main_sheet_get_rels_callback_data* data = (struct main_sheet_get_rels_callback_data*)callbackdata; @@ -944,7 +1032,7 @@ void main_sheet_get_sheetfile_expat_callback_element_start (void* callbackdata, } } -//determine the file name for a specified sheet name +// determine the file name for a specified sheet name void main_sheet_get_sheetfile_callback (ZIPFILETYPE* zip, const XML_Char* filename, const XML_Char* contenttype, void* callbackdata) { struct main_sheet_get_rels_callback_data* data = (struct main_sheet_get_rels_callback_data*)callbackdata; @@ -979,50 +1067,7 @@ void main_sheet_get_sheetfile_callback (ZIPFILETYPE* zip, const XML_Char* filena //////////////////////////////////////////////////////////////////////// -typedef enum { - none, - value_string, - inline_string, - shared_string -} cell_string_type_enum; - -#define XLSXIOREAD_NO_CALLBACK 0x80 - -struct data_sheet_callback_data { - XML_Parser xmlparser; - struct sharedstringlist* sharedstrings; - size_t rownr; - size_t colnr; - size_t cols; - XML_Char* celldata; - size_t celldatalen; - cell_string_type_enum cell_string_type; - cell_type_enum cell_type; - XML_Char* number_fmt; - unsigned int flags; - XML_Char* skiptag; //tag to skip - size_t skiptagcount; //nesting level for current tag to skip - XML_StartElementHandler skip_start; //start handler to set after skipping - XML_EndElementHandler skip_end; //end handler to set after skipping - XML_CharacterDataHandler skip_data; //data handler to set after skipping - xlsxioread_process_row_callback_fn sheet_row_callback; - xlsxioread_process_cell_callback_fn sheet_cell_callback; - void* callbackdata; -}; - -// public struct -//#ifndef data_sheet_cell_data -// struct data_sheet_cell_data { -// size_t row_num; -// size_t col_num; -// size_t cols; -// XML_Char* data; -// size_t datalen; -// cell_type_enum cell_type; -// }; -//#endif - -void data_sheet_callback_data_initialize (struct data_sheet_callback_data* data, struct sharedstringlist* sharedstrings, unsigned int flags, xlsxioread_process_cell_callback_fn cell_callback, xlsxioread_process_row_callback_fn row_callback, void* callbackdata) +void data_sheet_callback_data_initialize (xlsxioreader handle, struct data_sheet_callback_data* data, struct sharedstringlist* sharedstrings, unsigned int flags, xlsxioread_process_cell_callback_fn cell_callback, xlsxioread_process_row_callback_fn row_callback, void* callbackdata) { data->xmlparser = NULL; data->sharedstrings = sharedstrings; @@ -1043,6 +1088,7 @@ void data_sheet_callback_data_initialize (struct data_sheet_callback_data* data, data->sheet_cell_callback = cell_callback; data->sheet_row_callback = row_callback; data->callbackdata = callbackdata; + data->handle = handle; } void data_sheet_callback_data_cleanup (struct data_sheet_callback_data* data) @@ -1286,11 +1332,11 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch data->cell_type = cell_type_value; } else { int formatIndex = atoi(s); - int formatNum = *(int*)utarray_eltptr(g_number_formats, formatIndex); + int formatNum = *(int*)utarray_eltptr(data->handle->number_formats, formatIndex); struct date_struct *dateStruct = NULL; - HASH_FIND_INT(g_date_formats, &formatNum, dateStruct); + HASH_FIND_INT(data->handle->date_formats, &formatNum, dateStruct); int isDateTime = dateStruct ? dateStruct->isDate : 0; @@ -1414,45 +1460,42 @@ void data_sheet_expat_callback_value_data (void* callbackdata, const XML_Char* b } } +// extract number and date formats from styles.xml +// save it in handle->number_formats and handle->date_formats void get_styles(xlsxioreader handle) { - struct styles_callback_data stylescallbackdata; - stylescallbackdata.lookForStyles = 0; - stylescallbackdata.lookForDates = 0; - utarray_new(g_number_formats, &ut_int_icd); + struct styles_callback_data stylesCallbackData; + stylesCallbackData.lookForStyles = 0; + stylesCallbackData.lookForDates = 0; + stylesCallbackData.handle = handle; + + utarray_new(handle->number_formats, &ut_int_icd); + HASH_CLEAR(hh, handle->date_formats); - expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylescallbackdata, NULL); + expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylesCallbackData, NULL); +} - // debugging for extracting formats from styles.xml - /* - printf("got styles\n"); +DLL_EXPORT_XLSXIO void xlsxioread_debug_internals(xlsxioreader handle) { + printf("\n"); + printf("XLSXIO_READ DEBUG - %s\n", handle->filename); + printf("number_formats:\n"); int *p, i = 0; - for (p = (int*)utarray_front(g_number_formats); p != NULL; p = (int*)utarray_next(g_number_formats, p)) { - printf(" g_number_formats %i - %d\n", i, *p); + for (p = (int*)utarray_front(handle->number_formats); p != NULL; p = (int*)utarray_next(handle->number_formats, p)) { + printf(" number_format %i - %d\n", i, *p); i += 1; } - struct date_struct *dateStruct = NULL; + printf("date_formats:\n"); - for (dateStruct=g_date_formats; dateStruct != NULL; dateStruct=(struct dateStruct*)(dateStruct->hh.next)) { - printf(" dateStruct id %d isDate %d fmt %s\n", dateStruct->id, dateStruct->isDate, dateStruct->fmt); + struct date_struct *dateFmt = NULL; + for (dateFmt = handle->date_formats; dateFmt != NULL; dateFmt = (struct date_struct*)(dateFmt->hh.next)) { + printf(" date_format id=%d isDate=%d fmt=%s\n", dateFmt->id, dateFmt->isDate, dateFmt->fmt); } - */ + printf("\n"); } - //////////////////////////////////////////////////////////////////////// -struct xlsxio_read_sheet_struct { - xlsxioreader handle; - ZIPFILEENTRYTYPE* zipfile; - struct data_sheet_callback_data processcallbackdata; - size_t lastrownr; - size_t paddingrow; - size_t lastcolnr; - size_t paddingcol; -}; - DLL_EXPORT_XLSXIO int xlsxioread_process (xlsxioreader handle, const XLSXIOCHAR* sheetname, unsigned int flags, xlsxioread_process_cell_callback_fn cell_callback, xlsxioread_process_row_callback_fn row_callback, void* callbackdata) { int result = 0; @@ -1485,13 +1528,13 @@ DLL_EXPORT_XLSXIO int xlsxioread_process (xlsxioreader handle, const XLSXIOCHAR* if (!(flags & XLSXIOREAD_NO_CALLBACK)) { //use callback mechanism struct data_sheet_callback_data processcallbackdata; - data_sheet_callback_data_initialize(&processcallbackdata, sharedstrings, flags, cell_callback, row_callback, callbackdata); + data_sheet_callback_data_initialize(handle, &processcallbackdata, sharedstrings, flags, cell_callback, row_callback, callbackdata); expat_process_zip_file(handle->zip, getrelscallbackdata.sheetfile, data_sheet_expat_callback_find_worksheet_start, NULL, NULL, &processcallbackdata, &processcallbackdata.xmlparser); data_sheet_callback_data_cleanup(&processcallbackdata); } else { //use simplified interface by suspending the XML parser when data is found xlsxioreadersheet sheethandle = (xlsxioreadersheet)callbackdata; - data_sheet_callback_data_initialize(&sheethandle->processcallbackdata, sharedstrings, flags, NULL, NULL, sheethandle); + data_sheet_callback_data_initialize(handle, &sheethandle->processcallbackdata, sharedstrings, flags, NULL, NULL, sheethandle); if ((sheethandle->zipfile = XML_Char_openzip(sheethandle->handle->zip, getrelscallbackdata.sheetfile, 0)) == NULL) { result = 1; } @@ -1511,14 +1554,6 @@ DLL_EXPORT_XLSXIO int xlsxioread_process (xlsxioreader handle, const XLSXIOCHAR* //////////////////////////////////////////////////////////////////////// -struct xlsxio_read_sheetlist_struct { - xlsxioreader handle; - ZIPFILEENTRYTYPE* zipfile; - struct main_sheet_list_callback_data sheetcallbackdata; - XML_Parser xmlparser; - XML_Char* nextsheetname; -}; - int xlsxioread_list_sheets_resumable_callback (const XLSXIOCHAR* name, void* callbackdata) { //struct main_sheet_list_callback_data* data = (struct main_sheet_list_callback_data*)callbackdata; From c8a731fa8614e06cf7ced21908f6167814f1ec65 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 15:16:38 +0800 Subject: [PATCH 03/22] Cleanup code --- include/xlsxio_read.h | 2 - lib/xlsxio_read.c | 101 +++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 57 deletions(-) diff --git a/include/xlsxio_read.h b/include/xlsxio_read.h index a3ad899..64777f2 100644 --- a/include/xlsxio_read.h +++ b/include/xlsxio_read.h @@ -106,9 +106,7 @@ DLL_EXPORT_XLSXIO typedef enum { struct data_sheet_cell_data { size_t row_num; size_t col_num; - size_t cols; char* data; - size_t datalen; cell_type_enum cell_type; char* number_fmt; }; diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index a975604..a6cf3f0 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -260,8 +260,9 @@ XML_Char* get_relationship_filename (const XML_Char* filename) if ((result = XML_Char_malloc(filenamelen + 12)) != NULL) { size_t i = filenamelen; while (i > 0) { - if (filename[i - 1] == '/') + if (filename[i - 1] == '/') { break; + } i--; } XML_Char_poscpy(result, 0, filename, i); @@ -278,10 +279,10 @@ XML_Char* join_basepath_filename (const XML_Char* basepath, const XML_Char* file XML_Char* result = NULL; if (filename && *filename) { if (filename[0] == '/' && filename[1]) { - //file is absolute: remove leading slash + // file is absolute: remove leading slash result = XML_Char_dup(filename + 1); } else { - //file is relative: prepend base path + // file is relative: prepend base path size_t basepathlen = (basepath ? XML_Char_len(basepath) : 0); size_t filenamelen = XML_Char_len(filename); if ((result = XML_Char_malloc(basepathlen + filenamelen + 1)) != NULL) { @@ -531,10 +532,10 @@ struct main_sheet_get_rels_callback_data { }; struct date_struct { - int id; /* key */ - int isDate; - XML_Char* fmt; - UT_hash_handle hh; /* makes this structure hashable */ + int id; /* key */ + int isDate; + XML_Char* fmt; + UT_hash_handle hh; /* makes this structure hashable */ }; // aka xlsxioreader @@ -546,10 +547,10 @@ struct xlsxio_read_struct { }; struct styles_callback_data { - XML_Parser xmlparser; - int lookForStyles; - int lookForDates; - struct xlsxio_read_struct* handle; + XML_Parser xmlparser; + int lookForStyles; + int lookForDates; + struct xlsxio_read_struct* handle; }; typedef enum { @@ -755,7 +756,6 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) { if (handle) { - // if we call "free()" more then once then it will crash with "pointer being freed was not allocated" // so we can call xlsxioread_close multiple times int needFreeItself = handle->zip != NULL || handle->date_formats != NULL || handle->number_formats != NULL; @@ -781,7 +781,8 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) } if (needFreeItself) { - free(handle); + // if we have 2 pointers to same struct, this may cause seg fault + // free(handle); } } } @@ -915,24 +916,24 @@ DLL_EXPORT_XLSXIO void xlsxioread_list_sheets (xlsxioreader handle, xlsxioread_l //////////////////////////////////////////////////////////////////////// int is_date_format(const XML_Char* ch) { - // 'Red' is used in Excel general formats, for its 'd' not to be interpreted as 'd' in day the string is removed - char* pRed; - if ((pRed = strstr(ch, "Red")) != NULL) { - pRed[0] = pRed[1] = pRed[2] = ' '; - } + // 'Red' is used in Excel general formats, for its 'd' not to be interpreted as 'd' in day the string is removed + char* pRed; + if ((pRed = strstr(ch, "Red")) != NULL) { + pRed[0] = pRed[1] = pRed[2] = ' '; + } - if (strstr(ch, "y")) // year - return 1; - if (strstr(ch, "m")) // month/minute - return 1; - if (strstr(ch, "d")) // day - return 1; - if (strstr(ch, "h")) // hour - return 1; - if (strstr(ch, "s")) // second - return 1; + if (strstr(ch, "y")) // year + return 1; + if (strstr(ch, "m")) // month/minute + return 1; + if (strstr(ch, "d")) // day + return 1; + if (strstr(ch, "h")) // hour + return 1; + if (strstr(ch, "s")) // second + return 1; - return 0; + return 0; } void format_types_start_callback(void* callbackdata, const XML_Char* name, const XML_Char** atts) { @@ -974,13 +975,13 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const } void format_types_end_callback(void* callbackdata, const XML_Char* name) { - if (strcmp(name, "cellXfs") == 0) { - struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; - pdata->lookForStyles = 0; - } else if (strcmp(name, "numFmts") == 0) { - struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; - pdata->lookForDates = 0; - } + if (strcmp(name, "cellXfs") == 0) { + struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; + pdata->lookForStyles = 0; + } else if (strcmp(name, "numFmts") == 0) { + struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; + pdata->lookForDates = 0; + } } // determine relationship id for specific sheet name @@ -1044,8 +1045,9 @@ void main_sheet_get_sheetfile_callback (ZIPFILETYPE* zip, const XML_Char* filena //determine base name (including trailing slash) size_t i = XML_Char_len(filename); while (i > 0) { - if (filename[i - 1] == '/') + if (filename[i - 1] == '/') { break; + } i--; } if (data->basepath) @@ -1663,7 +1665,7 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_row (xlsxioreadersheet sheethandle) return 0; } sheethandle->lastcolnr = 0; - //when padding rows don't retrieve new data + // when padding rows don't retrieve new data if (sheethandle->paddingrow) { if (sheethandle->paddingrow < sheethandle->processcallbackdata.rownr) { return 3; @@ -1673,7 +1675,7 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_row (xlsxioreadersheet sheethandle) } } sheethandle->paddingcol = 0; - //go to beginning of next row + // go to beginning of next row while ((status = expat_process_zip_file_resume(sheethandle->zipfile, sheethandle->processcallbackdata.xmlparser)) == XML_STATUS_SUSPENDED && sheethandle->processcallbackdata.colnr != 0) { } return (status == XML_STATUS_SUSPENDED ? 1 : 0); @@ -1689,18 +1691,16 @@ DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct struct data_sheet_cell_data *res; res = (struct data_sheet_cell_data *) malloc(sizeof(struct data_sheet_cell_data)); res->row_num = sheethandle->processcallbackdata.rownr; - res->col_num = sheethandle->lastcolnr; //sheethandle->processcallbackdata.colnr; - res->cols = sheethandle->processcallbackdata.cols; - res->datalen = 0; + res->col_num = sheethandle->lastcolnr; res->number_fmt = sheethandle->processcallbackdata.number_fmt; res->cell_type = sheethandle->processcallbackdata.cell_type; - //append empty column if needed + // append empty column if needed if (sheethandle->paddingcol) { if (sheethandle->paddingcol > sheethandle->processcallbackdata.cols) { //last empty column added, finish row sheethandle->paddingcol = 0; - //when padding rows prepare for the next one + // when padding rows prepare for the next one if (sheethandle->paddingrow) { sheethandle->lastrownr++; sheethandle->paddingrow++; @@ -1710,13 +1710,8 @@ DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct } return NULL; } else { - //add another empty column + // add another empty column sheethandle->paddingcol++; - //return XML_Char_dup(X("")); - - //sheethandle->processcallbackdata.celldata = XML_Char_dup(X("")); - //return &sheethandle->processcallbackdata; - res->data = XML_Char_dup(X("")); return res; } @@ -1730,7 +1725,7 @@ DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct } } - //insert empty rows if needed + // insert empty rows if needed if (!(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_ROWS) && sheethandle->lastrownr + 1 < sheethandle->processcallbackdata.rownr) { sheethandle->paddingrow = sheethandle->lastrownr + 1; sheethandle->paddingcol = sheethandle->processcallbackdata.colnr * 0 + 1; @@ -1742,9 +1737,6 @@ DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct if (!(sheethandle->processcallbackdata.flags & XLSXIOREAD_SKIP_EMPTY_CELLS)) { if (sheethandle->lastcolnr + 1 < sheethandle->processcallbackdata.colnr) { sheethandle->lastcolnr++; - //return XML_Char_dup(X("")); - //sheethandle->processcallbackdata.celldata = XML_Char_dup(X("")); - //return &sheethandle->processcallbackdata; res->data = XML_Char_dup(X("")); return res; @@ -1770,7 +1762,6 @@ DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct sheethandle->lastcolnr = sheethandle->processcallbackdata.colnr; return res->data ? res : NULL; - //return result; } DLL_EXPORT_XLSXIO XLSXIOCHAR* xlsxioread_sheet_next_cell (xlsxioreadersheet sheethandle) { From d88307e8eb3323fdf0dcc7a0a07a1af9f6032382 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 15:26:56 +0800 Subject: [PATCH 04/22] Better formatting for xlsxioread_debug_internals --- lib/xlsxio_read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index a6cf3f0..e887396 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -1491,7 +1491,7 @@ DLL_EXPORT_XLSXIO void xlsxioread_debug_internals(xlsxioreader handle) { struct date_struct *dateFmt = NULL; for (dateFmt = handle->date_formats; dateFmt != NULL; dateFmt = (struct date_struct*)(dateFmt->hh.next)) { - printf(" date_format id=%d isDate=%d fmt=%s\n", dateFmt->id, dateFmt->isDate, dateFmt->fmt); + printf(" date_format id = %d\t isDate = %d\t fmt = %s\n", dateFmt->id, dateFmt->isDate, dateFmt->fmt); } printf("\n"); } From b1135f8f0faae3b395c8e991311ddb50441cec43 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 16:18:50 +0800 Subject: [PATCH 05/22] Try with travis --- .travis.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b627098 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +language: c + +os: + - osx + - linux + - windows + +compiler: + - clang + - gcc + + +before_install: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libbsd-dev libssl-dev; fi + +before_script: + - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip + - unzip 2.8.4.zip + - cd minizip-2.8.4 + - mkdir build + - cd build + - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release + - cmake --build . --config Release --target install + - cd ../.. + + +script: + - make + - make examples From a5f0064d7879d59df1d1a6102e2c857adbbf5ea3 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 16:22:45 +0800 Subject: [PATCH 06/22] Try with travis --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b627098..054fe01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,9 @@ os: - linux - windows -compiler: - - clang - - gcc +#compiler: +# - clang +# - gcc before_install: @@ -20,7 +20,7 @@ before_script: - mkdir build - cd build - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release - - cmake --build . --config Release --target install + - sudo cmake --build . --config Release --target install - cd ../.. From b708ea6a009e583e3089b6d18aa4b4fe36501698 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:20:11 +0800 Subject: [PATCH 07/22] Remove files generated by cmake --- build/example_xlsxio_read.cbp | 106 ------------------ build/example_xlsxio_read_advanced.cbp | 105 ------------------ build/example_xlsxio_read_cpp.cbp | 102 ------------------ build/example_xlsxio_read_wchar.cbp | 106 ------------------ build/example_xlsxio_write.cbp | 104 ------------------ build/example_xlsxio_write_cpp.cbp | 102 ------------------ build/example_xlsxio_write_getversion.cbp | 104 ------------------ build/libxlsxio_read.cbp | 116 -------------------- build/libxlsxio_read_minizip_shared.cbp | 125 ---------------------- build/libxlsxio_read_shared.cbp | 122 --------------------- build/libxlsxio_read_wchar.cbp | 117 -------------------- build/libxlsxio_read_wchar_shared.cbp | 123 --------------------- build/libxlsxio_write.cbp | 122 --------------------- build/libxlsxio_write_minizip_shared.cbp | 118 -------------------- build/libxlsxio_write_shared.cbp | 117 -------------------- build/libxlsxio_write_wchar.cbp | 123 --------------------- build/libxlsxio_write_wchar_shared.cbp | 118 -------------------- build/xlsxio.workspace | 36 ------- build/xlsxio_csv2xlsx.cbp | 110 ------------------- build/xlsxio_read.cbp | 103 ------------------ build/xlsxio_write.cbp | 102 ------------------ build/xlsxio_xlsx2csv.cbp | 110 ------------------- 22 files changed, 2391 deletions(-) delete mode 100644 build/example_xlsxio_read.cbp delete mode 100644 build/example_xlsxio_read_advanced.cbp delete mode 100644 build/example_xlsxio_read_cpp.cbp delete mode 100644 build/example_xlsxio_read_wchar.cbp delete mode 100644 build/example_xlsxio_write.cbp delete mode 100644 build/example_xlsxio_write_cpp.cbp delete mode 100644 build/example_xlsxio_write_getversion.cbp delete mode 100644 build/libxlsxio_read.cbp delete mode 100644 build/libxlsxio_read_minizip_shared.cbp delete mode 100644 build/libxlsxio_read_shared.cbp delete mode 100644 build/libxlsxio_read_wchar.cbp delete mode 100644 build/libxlsxio_read_wchar_shared.cbp delete mode 100644 build/libxlsxio_write.cbp delete mode 100644 build/libxlsxio_write_minizip_shared.cbp delete mode 100644 build/libxlsxio_write_shared.cbp delete mode 100644 build/libxlsxio_write_wchar.cbp delete mode 100644 build/libxlsxio_write_wchar_shared.cbp delete mode 100644 build/xlsxio.workspace delete mode 100644 build/xlsxio_csv2xlsx.cbp delete mode 100644 build/xlsxio_read.cbp delete mode 100644 build/xlsxio_write.cbp delete mode 100644 build/xlsxio_xlsx2csv.cbp diff --git a/build/example_xlsxio_read.cbp b/build/example_xlsxio_read.cbp deleted file mode 100644 index a372bd7..0000000 --- a/build/example_xlsxio_read.cbp +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_read_advanced.cbp b/build/example_xlsxio_read_advanced.cbp deleted file mode 100644 index d6225ec..0000000 --- a/build/example_xlsxio_read_advanced.cbp +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_read_cpp.cbp b/build/example_xlsxio_read_cpp.cbp deleted file mode 100644 index 27ab82d..0000000 --- a/build/example_xlsxio_read_cpp.cbp +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_read_wchar.cbp b/build/example_xlsxio_read_wchar.cbp deleted file mode 100644 index 77faf8a..0000000 --- a/build/example_xlsxio_read_wchar.cbp +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_write.cbp b/build/example_xlsxio_write.cbp deleted file mode 100644 index 1ba85d8..0000000 --- a/build/example_xlsxio_write.cbp +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_write_cpp.cbp b/build/example_xlsxio_write_cpp.cbp deleted file mode 100644 index 975d435..0000000 --- a/build/example_xlsxio_write_cpp.cbp +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - diff --git a/build/example_xlsxio_write_getversion.cbp b/build/example_xlsxio_write_getversion.cbp deleted file mode 100644 index 6909018..0000000 --- a/build/example_xlsxio_write_getversion.cbp +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_read.cbp b/build/libxlsxio_read.cbp deleted file mode 100644 index 44933da..0000000 --- a/build/libxlsxio_read.cbp +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_read_minizip_shared.cbp b/build/libxlsxio_read_minizip_shared.cbp deleted file mode 100644 index 4fa26a3..0000000 --- a/build/libxlsxio_read_minizip_shared.cbp +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_read_shared.cbp b/build/libxlsxio_read_shared.cbp deleted file mode 100644 index bcea4dc..0000000 --- a/build/libxlsxio_read_shared.cbp +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_read_wchar.cbp b/build/libxlsxio_read_wchar.cbp deleted file mode 100644 index 453910c..0000000 --- a/build/libxlsxio_read_wchar.cbp +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_read_wchar_shared.cbp b/build/libxlsxio_read_wchar_shared.cbp deleted file mode 100644 index a5d8c68..0000000 --- a/build/libxlsxio_read_wchar_shared.cbp +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_write.cbp b/build/libxlsxio_write.cbp deleted file mode 100644 index f816dc2..0000000 --- a/build/libxlsxio_write.cbp +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_write_minizip_shared.cbp b/build/libxlsxio_write_minizip_shared.cbp deleted file mode 100644 index fd73009..0000000 --- a/build/libxlsxio_write_minizip_shared.cbp +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_write_shared.cbp b/build/libxlsxio_write_shared.cbp deleted file mode 100644 index c563b9c..0000000 --- a/build/libxlsxio_write_shared.cbp +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_write_wchar.cbp b/build/libxlsxio_write_wchar.cbp deleted file mode 100644 index b84fd4f..0000000 --- a/build/libxlsxio_write_wchar.cbp +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - diff --git a/build/libxlsxio_write_wchar_shared.cbp b/build/libxlsxio_write_wchar_shared.cbp deleted file mode 100644 index 381c962..0000000 --- a/build/libxlsxio_write_wchar_shared.cbp +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - diff --git a/build/xlsxio.workspace b/build/xlsxio.workspace deleted file mode 100644 index 6fcd3bc..0000000 --- a/build/xlsxio.workspace +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/xlsxio_csv2xlsx.cbp b/build/xlsxio_csv2xlsx.cbp deleted file mode 100644 index 72c3c3e..0000000 --- a/build/xlsxio_csv2xlsx.cbp +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - diff --git a/build/xlsxio_read.cbp b/build/xlsxio_read.cbp deleted file mode 100644 index 8778c35..0000000 --- a/build/xlsxio_read.cbp +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - diff --git a/build/xlsxio_write.cbp b/build/xlsxio_write.cbp deleted file mode 100644 index 9924032..0000000 --- a/build/xlsxio_write.cbp +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - diff --git a/build/xlsxio_xlsx2csv.cbp b/build/xlsxio_xlsx2csv.cbp deleted file mode 100644 index 9aa245c..0000000 --- a/build/xlsxio_xlsx2csv.cbp +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - From e32a7023eb2b37271ab7eb59965a6028d9d76819 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:20:29 +0800 Subject: [PATCH 08/22] Keep trying with travis --- .travis.yml | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 054fe01..df38972 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,21 +9,34 @@ os: # - clang # - gcc +matrix: + include: + - stage: macintosh + os: osx + before_script: + - brew install minizip + - os: linux + before_script: + - apt-get update + - apt-get install minizip -before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libbsd-dev libssl-dev; fi -before_script: - - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip - - unzip 2.8.4.zip - - cd minizip-2.8.4 - - mkdir build - - cd build - - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release - - sudo cmake --build . --config Release --target install - - cd ../.. +#before_install: +# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libbsd-dev libssl-dev; fi + +#before_script: +# - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip +# - unzip 2.8.4.zip +# - cd minizip-2.8.4 +# - mkdir build +# - cd build +# - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release +# - sudo cmake --build . --config Release +# - cd ../.. script: - - make - - make examples + - cmake . + - cmake --build . + - ./example_xlsxio_write + - ls -lah example.xlsx From ed9b88e82b98500624916bbcc634941ba3294d00 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:28:00 +0800 Subject: [PATCH 09/22] Keep trying with travis --- .travis.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index df38972..29db5cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,17 +9,25 @@ os: # - clang # - gcc -matrix: - include: - - stage: macintosh - os: osx - before_script: - - brew install minizip - - os: linux - before_script: - - apt-get update - - apt-get install minizip - +addons: + apt: + update: true + packages: ["minizip"] + homebrew: + packages: ["minizip"] + +#matrix: +# include: +# - os: linux +# addons: +# apt: +# update: true +# packages: ["minizip"] +# - os: osx +# addons: +# homebrew: +# packages: ["minizip"] +# #before_install: # - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libbsd-dev libssl-dev; fi From a7589dc5fbe0a7c478487f4904cbb54e70077257 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:34:29 +0800 Subject: [PATCH 10/22] Keep trying with travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29db5cc..4e5515e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,7 @@ os: addons: apt: - update: true - packages: ["minizip"] + packages: ["libminizip-dev"] homebrew: packages: ["minizip"] @@ -44,6 +43,7 @@ addons: script: + - which choco && choco install minizip - cmake . - cmake --build . - ./example_xlsxio_write From 49de0ef323b65d1fa8c3bb53e2d2ed7d1ee52299 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:41:30 +0800 Subject: [PATCH 11/22] Keep trying with travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4e5515e..b4523ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ os: - linux - windows +dist: xenial + #compiler: # - clang # - gcc From c8088d2103cd39e4c3c47803d51a7bd26084c9db Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 17:45:44 +0800 Subject: [PATCH 12/22] Travis: Allow windows to fail --- .travis.yml | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4523ba..55f3f80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,33 +5,18 @@ os: - linux - windows +# linux distro dist: xenial -#compiler: -# - clang -# - gcc - addons: apt: packages: ["libminizip-dev"] homebrew: packages: ["minizip"] -#matrix: -# include: -# - os: linux -# addons: -# apt: -# update: true -# packages: ["minizip"] -# - os: osx -# addons: -# homebrew: -# packages: ["minizip"] -# - -#before_install: -# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -y install libbsd-dev libssl-dev; fi +matrix: + allow_failures: + - os: windows #before_script: # - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip @@ -45,7 +30,6 @@ addons: script: - - which choco && choco install minizip - cmake . - cmake --build . - ./example_xlsxio_write From 426d61f942c2c29c6a449db25e3c818933697a9d Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Fri, 15 Mar 2019 19:48:46 +0800 Subject: [PATCH 13/22] Update Makefile: make XLSXIOREAD_LDFLAGS and XLSXIOREADW_LDFLAGS configurable --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c60eedd..80463c7 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,8 @@ SHARED_CFLAGS = -DBUILD_XLSXIO_DLL LIBS = LDFLAGS = ifeq ($(OS),Darwin) -CFLAGS += -I/opt/local/include -I/opt/local/lib/libzip/include -LDFLAGS += -L/opt/local/lib +#CFLAGS += -I/opt/local/include -I/opt/local/lib/libzip/include +#LDFLAGS += -L/opt/local/lib #CFLAGS += -arch i386 -arch x86_64 #LDFLAGS += -arch i386 -arch x86_64 STRIPFLAG = @@ -66,8 +66,8 @@ CFLAGS += -DUSE_MINIZIP endif XLSXIOREAD_OBJ = lib/xlsxio_read.o lib/xlsxio_read_sharedstrings.o -XLSXIOREAD_LDFLAGS = $(ZIPLIB_LDFLAGS) -lexpat -XLSXIOREADW_LDFLAGS = $(ZIPLIB_LDFLAGS) -lexpatw +XLSXIOREAD_LDFLAGS += $(ZIPLIB_LDFLAGS) -lexpat +XLSXIOREADW_LDFLAGS += $(ZIPLIB_LDFLAGS) -lexpatw XLSXIOREAD_SHARED_LDFLAGS = XLSXIOWRITE_OBJ = lib/xlsxio_write.o XLSXIOWRITE_LDFLAGS = $(ZIPLIB_LDFLAGS) From 909fafce2cf0a48a37ea4e37c2b913a07067dc76 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sat, 16 Mar 2019 21:35:19 +0800 Subject: [PATCH 14/22] Try to fix windows build --- .travis.yml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55f3f80..8afb28a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,19 +18,22 @@ matrix: allow_failures: - os: windows -#before_script: -# - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip -# - unzip 2.8.4.zip -# - cd minizip-2.8.4 -# - mkdir build -# - cd build -# - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release -# - sudo cmake --build . --config Release -# - cd ../.. - +jobs: + include: + - stage: install_local_minizip + os: windows + script: + - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip + - unzip 2.8.4.zip + - cd minizip-2.8.4 + - mkdir build + - cd build + - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release + - sudo cmake --build . --config Release + - export XLSXIO_CMAKE_ARGS="-DMINIZIP_DIR:PATH=$(pwd)" script: - - cmake . + - cmake . $XLSXIO_CMAKE_ARGS - cmake --build . - ./example_xlsxio_write - ls -lah example.xlsx From 68f19c53104d7506548860c9f91fe4fbe6872a75 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sat, 16 Mar 2019 21:36:06 +0800 Subject: [PATCH 15/22] Revert "Remove files generated by cmake" This reverts commit b708ea6a009e583e3089b6d18aa4b4fe36501698. --- build/example_xlsxio_read.cbp | 106 ++++++++++++++++++ build/example_xlsxio_read_advanced.cbp | 105 ++++++++++++++++++ build/example_xlsxio_read_cpp.cbp | 102 ++++++++++++++++++ build/example_xlsxio_read_wchar.cbp | 106 ++++++++++++++++++ build/example_xlsxio_write.cbp | 104 ++++++++++++++++++ build/example_xlsxio_write_cpp.cbp | 102 ++++++++++++++++++ build/example_xlsxio_write_getversion.cbp | 104 ++++++++++++++++++ build/libxlsxio_read.cbp | 116 ++++++++++++++++++++ build/libxlsxio_read_minizip_shared.cbp | 125 ++++++++++++++++++++++ build/libxlsxio_read_shared.cbp | 122 +++++++++++++++++++++ build/libxlsxio_read_wchar.cbp | 117 ++++++++++++++++++++ build/libxlsxio_read_wchar_shared.cbp | 123 +++++++++++++++++++++ build/libxlsxio_write.cbp | 122 +++++++++++++++++++++ build/libxlsxio_write_minizip_shared.cbp | 118 ++++++++++++++++++++ build/libxlsxio_write_shared.cbp | 117 ++++++++++++++++++++ build/libxlsxio_write_wchar.cbp | 123 +++++++++++++++++++++ build/libxlsxio_write_wchar_shared.cbp | 118 ++++++++++++++++++++ build/xlsxio.workspace | 36 +++++++ build/xlsxio_csv2xlsx.cbp | 110 +++++++++++++++++++ build/xlsxio_read.cbp | 103 ++++++++++++++++++ build/xlsxio_write.cbp | 102 ++++++++++++++++++ build/xlsxio_xlsx2csv.cbp | 110 +++++++++++++++++++ 22 files changed, 2391 insertions(+) create mode 100644 build/example_xlsxio_read.cbp create mode 100644 build/example_xlsxio_read_advanced.cbp create mode 100644 build/example_xlsxio_read_cpp.cbp create mode 100644 build/example_xlsxio_read_wchar.cbp create mode 100644 build/example_xlsxio_write.cbp create mode 100644 build/example_xlsxio_write_cpp.cbp create mode 100644 build/example_xlsxio_write_getversion.cbp create mode 100644 build/libxlsxio_read.cbp create mode 100644 build/libxlsxio_read_minizip_shared.cbp create mode 100644 build/libxlsxio_read_shared.cbp create mode 100644 build/libxlsxio_read_wchar.cbp create mode 100644 build/libxlsxio_read_wchar_shared.cbp create mode 100644 build/libxlsxio_write.cbp create mode 100644 build/libxlsxio_write_minizip_shared.cbp create mode 100644 build/libxlsxio_write_shared.cbp create mode 100644 build/libxlsxio_write_wchar.cbp create mode 100644 build/libxlsxio_write_wchar_shared.cbp create mode 100644 build/xlsxio.workspace create mode 100644 build/xlsxio_csv2xlsx.cbp create mode 100644 build/xlsxio_read.cbp create mode 100644 build/xlsxio_write.cbp create mode 100644 build/xlsxio_xlsx2csv.cbp diff --git a/build/example_xlsxio_read.cbp b/build/example_xlsxio_read.cbp new file mode 100644 index 0000000..a372bd7 --- /dev/null +++ b/build/example_xlsxio_read.cbp @@ -0,0 +1,106 @@ + + + + + + diff --git a/build/example_xlsxio_read_advanced.cbp b/build/example_xlsxio_read_advanced.cbp new file mode 100644 index 0000000..d6225ec --- /dev/null +++ b/build/example_xlsxio_read_advanced.cbp @@ -0,0 +1,105 @@ + + + + + + diff --git a/build/example_xlsxio_read_cpp.cbp b/build/example_xlsxio_read_cpp.cbp new file mode 100644 index 0000000..27ab82d --- /dev/null +++ b/build/example_xlsxio_read_cpp.cbp @@ -0,0 +1,102 @@ + + + + + + diff --git a/build/example_xlsxio_read_wchar.cbp b/build/example_xlsxio_read_wchar.cbp new file mode 100644 index 0000000..77faf8a --- /dev/null +++ b/build/example_xlsxio_read_wchar.cbp @@ -0,0 +1,106 @@ + + + + + + diff --git a/build/example_xlsxio_write.cbp b/build/example_xlsxio_write.cbp new file mode 100644 index 0000000..1ba85d8 --- /dev/null +++ b/build/example_xlsxio_write.cbp @@ -0,0 +1,104 @@ + + + + + + diff --git a/build/example_xlsxio_write_cpp.cbp b/build/example_xlsxio_write_cpp.cbp new file mode 100644 index 0000000..975d435 --- /dev/null +++ b/build/example_xlsxio_write_cpp.cbp @@ -0,0 +1,102 @@ + + + + + + diff --git a/build/example_xlsxio_write_getversion.cbp b/build/example_xlsxio_write_getversion.cbp new file mode 100644 index 0000000..6909018 --- /dev/null +++ b/build/example_xlsxio_write_getversion.cbp @@ -0,0 +1,104 @@ + + + + + + diff --git a/build/libxlsxio_read.cbp b/build/libxlsxio_read.cbp new file mode 100644 index 0000000..44933da --- /dev/null +++ b/build/libxlsxio_read.cbp @@ -0,0 +1,116 @@ + + + + + + diff --git a/build/libxlsxio_read_minizip_shared.cbp b/build/libxlsxio_read_minizip_shared.cbp new file mode 100644 index 0000000..4fa26a3 --- /dev/null +++ b/build/libxlsxio_read_minizip_shared.cbp @@ -0,0 +1,125 @@ + + + + + + diff --git a/build/libxlsxio_read_shared.cbp b/build/libxlsxio_read_shared.cbp new file mode 100644 index 0000000..bcea4dc --- /dev/null +++ b/build/libxlsxio_read_shared.cbp @@ -0,0 +1,122 @@ + + + + + + diff --git a/build/libxlsxio_read_wchar.cbp b/build/libxlsxio_read_wchar.cbp new file mode 100644 index 0000000..453910c --- /dev/null +++ b/build/libxlsxio_read_wchar.cbp @@ -0,0 +1,117 @@ + + + + + + diff --git a/build/libxlsxio_read_wchar_shared.cbp b/build/libxlsxio_read_wchar_shared.cbp new file mode 100644 index 0000000..a5d8c68 --- /dev/null +++ b/build/libxlsxio_read_wchar_shared.cbp @@ -0,0 +1,123 @@ + + + + + + diff --git a/build/libxlsxio_write.cbp b/build/libxlsxio_write.cbp new file mode 100644 index 0000000..f816dc2 --- /dev/null +++ b/build/libxlsxio_write.cbp @@ -0,0 +1,122 @@ + + + + + + diff --git a/build/libxlsxio_write_minizip_shared.cbp b/build/libxlsxio_write_minizip_shared.cbp new file mode 100644 index 0000000..fd73009 --- /dev/null +++ b/build/libxlsxio_write_minizip_shared.cbp @@ -0,0 +1,118 @@ + + + + + + diff --git a/build/libxlsxio_write_shared.cbp b/build/libxlsxio_write_shared.cbp new file mode 100644 index 0000000..c563b9c --- /dev/null +++ b/build/libxlsxio_write_shared.cbp @@ -0,0 +1,117 @@ + + + + + + diff --git a/build/libxlsxio_write_wchar.cbp b/build/libxlsxio_write_wchar.cbp new file mode 100644 index 0000000..b84fd4f --- /dev/null +++ b/build/libxlsxio_write_wchar.cbp @@ -0,0 +1,123 @@ + + + + + + diff --git a/build/libxlsxio_write_wchar_shared.cbp b/build/libxlsxio_write_wchar_shared.cbp new file mode 100644 index 0000000..381c962 --- /dev/null +++ b/build/libxlsxio_write_wchar_shared.cbp @@ -0,0 +1,118 @@ + + + + + + diff --git a/build/xlsxio.workspace b/build/xlsxio.workspace new file mode 100644 index 0000000..6fcd3bc --- /dev/null +++ b/build/xlsxio.workspace @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/xlsxio_csv2xlsx.cbp b/build/xlsxio_csv2xlsx.cbp new file mode 100644 index 0000000..72c3c3e --- /dev/null +++ b/build/xlsxio_csv2xlsx.cbp @@ -0,0 +1,110 @@ + + + + + + diff --git a/build/xlsxio_read.cbp b/build/xlsxio_read.cbp new file mode 100644 index 0000000..8778c35 --- /dev/null +++ b/build/xlsxio_read.cbp @@ -0,0 +1,103 @@ + + + + + + diff --git a/build/xlsxio_write.cbp b/build/xlsxio_write.cbp new file mode 100644 index 0000000..9924032 --- /dev/null +++ b/build/xlsxio_write.cbp @@ -0,0 +1,102 @@ + + + + + + diff --git a/build/xlsxio_xlsx2csv.cbp b/build/xlsxio_xlsx2csv.cbp new file mode 100644 index 0000000..9aa245c --- /dev/null +++ b/build/xlsxio_xlsx2csv.cbp @@ -0,0 +1,110 @@ + + + + + + From f99971a0dc4df2be5c01601c1c742debcaecebf1 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sat, 16 Mar 2019 21:41:37 +0800 Subject: [PATCH 16/22] Try to fix windows build on travis --- .travis.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8afb28a..63cd27c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: jobs: include: - - stage: install_local_minizip + - stage: prepare os: windows script: - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip @@ -29,11 +29,12 @@ jobs: - mkdir build - cd build - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release - - sudo cmake --build . --config Release + - cmake --build . --config Release - export XLSXIO_CMAKE_ARGS="-DMINIZIP_DIR:PATH=$(pwd)" -script: - - cmake . $XLSXIO_CMAKE_ARGS - - cmake --build . - - ./example_xlsxio_write - - ls -lah example.xlsx + - stage: test + script: + - cmake . $XLSXIO_CMAKE_ARGS + - cmake --build . + - ./example_xlsxio_write + - ls -lah example.xlsx From 5618ee0a5112c9586640d6c7f7496d01cd9a93a0 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sat, 16 Mar 2019 22:02:32 +0800 Subject: [PATCH 17/22] Try to fix windows build on travis --- .travis.yml | 24 ++++++++++++++++++------ lib/xlsxio_read.c | 2 +- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 63cd27c..396b0a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,15 +14,13 @@ addons: homebrew: packages: ["minizip"] -matrix: +jobs: allow_failures: - os: windows - -jobs: include: - - stage: prepare + - stage: test os: windows - script: + before_script: - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip - unzip 2.8.4.zip - cd minizip-2.8.4 @@ -30,9 +28,23 @@ jobs: - cd build - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release - cmake --build . --config Release - - export XLSXIO_CMAKE_ARGS="-DMINIZIP_DIR:PATH=$(pwd)" + - cd ../.. + script: + - cmake . -DMINIZIP_DIR:PATH=$(pwd)/minizip-2.8.4/build + - cmake --build . + - ./example_xlsxio_write + - ls -lah example.xlsx + + - stage: test + os: linux + script: + - cmake . $XLSXIO_CMAKE_ARGS + - cmake --build . + - ./example_xlsxio_write + - ls -lah example.xlsx - stage: test + os: osx script: - cmake . $XLSXIO_CMAKE_ARGS - cmake --build . diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index e887396..99b4e6e 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -575,7 +575,7 @@ struct data_sheet_callback_data { cell_type_enum cell_type; XML_Char* number_fmt; unsigned int flags; - XML_Char* skiptag; //tag to skip + XML_Char* skiptag; //tag to skip size_t skiptagcount; //nesting level for current tag to skip XML_StartElementHandler skip_start; //start handler to set after skipping XML_EndElementHandler skip_end; //end handler to set after skipping From 05f4b9b53cff2e3b0617a0a51b19bc314f501b47 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sun, 17 Mar 2019 00:10:13 +0800 Subject: [PATCH 18/22] Remove utarray and uthash, use arrays if structs --- include/utarray.h | 238 --------- include/uthash.h | 1227 --------------------------------------------- lib/xlsxio_read.c | 191 ++++--- 3 files changed, 115 insertions(+), 1541 deletions(-) delete mode 100755 include/utarray.h delete mode 100755 include/uthash.h diff --git a/include/utarray.h b/include/utarray.h deleted file mode 100755 index 6ed0dce..0000000 --- a/include/utarray.h +++ /dev/null @@ -1,238 +0,0 @@ -/* -Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -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. -*/ - -/* a dynamic array implementation using macros - */ -#ifndef UTARRAY_H -#define UTARRAY_H - -#define UTARRAY_VERSION 2.1.0 - -#include /* size_t */ -#include /* memset, etc */ -#include /* exit */ - -#ifdef __GNUC__ -#define UTARRAY_UNUSED __attribute__((__unused__)) -#else -#define UTARRAY_UNUSED -#endif - -#ifndef oom -#define oom() exit(-1) -#endif - -typedef void (ctor_f)(void *dst, const void *src); -typedef void (dtor_f)(void *elt); -typedef void (init_f)(void *elt); -typedef struct { - size_t sz; - init_f *init; - ctor_f *copy; - dtor_f *dtor; -} UT_icd; - -typedef struct { - unsigned i,n;/* i: index of next available slot, n: num slots */ - UT_icd icd; /* initializer, copy and destructor functions */ - char *d; /* n slots of size icd->sz*/ -} UT_array; - -#define utarray_init(a,_icd) do { \ - memset(a,0,sizeof(UT_array)); \ - (a)->icd = *(_icd); \ -} while(0) - -#define utarray_done(a) do { \ - if ((a)->n) { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ - } \ - } \ - free((a)->d); \ - } \ - (a)->n=0; \ -} while(0) - -#define utarray_new(a,_icd) do { \ - (a) = (UT_array*)malloc(sizeof(UT_array)); \ - if ((a) == NULL) oom(); \ - utarray_init(a,_icd); \ -} while(0) - -#define utarray_free(a) do { \ - utarray_done(a); \ - free(a); \ -} while(0) - -#define utarray_reserve(a,by) do { \ - if (((a)->i+(by)) > (a)->n) { \ - char *utarray_tmp; \ - while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ - utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ - if (utarray_tmp == NULL) oom(); \ - (a)->d=utarray_tmp; \ - } \ -} while(0) - -#define utarray_push_back(a,p) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ - else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ -} while(0) - -#define utarray_pop_back(a) do { \ - if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ - else { (a)->i--; } \ -} while(0) - -#define utarray_extend_back(a) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ - else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ - (a)->i++; \ -} while(0) - -#define utarray_len(a) ((a)->i) - -#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) -#define _utarray_eltptr(a,j) ((a)->d + ((a)->icd.sz * (j))) - -#define utarray_insert(a,p,j) do { \ - if ((j) > (a)->i) utarray_resize(a,j); \ - utarray_reserve(a,1); \ - if ((j) < (a)->i) { \ - memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ - else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ - (a)->i++; \ -} while(0) - -#define utarray_inserta(a,w,j) do { \ - if (utarray_len(w) == 0) break; \ - if ((j) > (a)->i) utarray_resize(a,j); \ - utarray_reserve(a,utarray_len(w)); \ - if ((j) < (a)->i) { \ - memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ - _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { \ - unsigned _ut_i; \ - for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ - (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ - } \ - } else { \ - memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ - utarray_len(w)*((a)->icd.sz)); \ - } \ - (a)->i += utarray_len(w); \ -} while(0) - -#define utarray_resize(dst,num) do { \ - unsigned _ut_i; \ - if ((dst)->i > (unsigned)(num)) { \ - if ((dst)->icd.dtor) { \ - for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ - (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ - } \ - } \ - } else if ((dst)->i < (unsigned)(num)) { \ - utarray_reserve(dst, (num) - (dst)->i); \ - if ((dst)->icd.init) { \ - for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ - (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ - } \ - } else { \ - memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ - } \ - } \ - (dst)->i = (num); \ -} while(0) - -#define utarray_concat(dst,src) do { \ - utarray_inserta(dst, src, utarray_len(dst)); \ -} while(0) - -#define utarray_erase(a,pos,len) do { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ - } \ - } \ - if ((a)->i > ((pos) + (len))) { \ - memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ - ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ - } \ - (a)->i -= (len); \ -} while(0) - -#define utarray_renew(a,u) do { \ - if (a) utarray_clear(a); \ - else utarray_new(a, u); \ -} while(0) - -#define utarray_clear(a) do { \ - if ((a)->i > 0) { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ - } \ - } \ - (a)->i = 0; \ - } \ -} while(0) - -#define utarray_sort(a,cmp) do { \ - qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ -} while(0) - -#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) - -#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) -#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) -#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) -#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) -#define utarray_eltidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) - -/* last we pre-define a few icd for common utarrays of ints and strings */ -static void utarray_str_cpy(void *dst, const void *src) { - char **_src = (char**)src, **_dst = (char**)dst; - *_dst = (*_src == NULL) ? NULL : strdup(*_src); -} -static void utarray_str_dtor(void *elt) { - char **eltc = (char**)elt; - if (*eltc != NULL) free(*eltc); -} -static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; -static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; -static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; - - -#endif /* UTARRAY_H */ diff --git a/include/uthash.h b/include/uthash.h deleted file mode 100755 index 76bdca6..0000000 --- a/include/uthash.h +++ /dev/null @@ -1,1227 +0,0 @@ -/* -Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -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. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#define UTHASH_VERSION 2.1.0 - -#include /* memcmp, memset, strlen */ -#include /* ptrdiff_t */ -#include /* exit */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) -#if defined(_MSC_VER) /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#endif -#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) -#define NO_DECLTYPE -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE(x) -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while (0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while (0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ -#if defined(_WIN32) -#if defined(_MSC_VER) && _MSC_VER >= 1600 -#include -#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif -#elif defined(__GNUC__) && !defined(__VXWORKS__) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif - -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif -#ifndef uthash_bzero -#define uthash_bzero(a,n) memset(a,'\0',n) -#endif -#ifndef uthash_strlen -#define uthash_strlen(s) strlen(s) -#endif - -#ifdef uthash_memcmp -/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */ -#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead" -#else -#define uthash_memcmp(a,b,n) memcmp(a,b,n) -#endif - -#ifndef HASH_KEYCMP -#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n) -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -#ifndef HASH_NONFATAL_OOM -#define HASH_NONFATAL_OOM 0 -#endif - -#if HASH_NONFATAL_OOM -/* malloc failures can be recovered from */ - -#ifndef uthash_nonfatal_oom -#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ -#endif - -#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) -#define IF_HASH_NONFATAL_OOM(x) x - -#else -/* malloc failures result in lost memory, hash tables are unusable */ - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ -#endif - -#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") -#define IF_HASH_NONFATAL_OOM(x) - -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhp */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) -/* calculate the hash handle from element address elp */ -#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) - -#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ -do { \ - struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ - unsigned _hd_bkt; \ - HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - (head)->hh.tbl->buckets[_hd_bkt].count++; \ - _hd_hh_item->hh_next = NULL; \ - _hd_hh_item->hh_prev = NULL; \ -} while (0) - -#define HASH_VALUE(keyptr,keylen,hashv) \ -do { \ - HASH_FCN(keyptr, keylen, hashv); \ -} while (0) - -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ -do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_bkt; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ - } \ - } \ -} while (0) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - unsigned _hf_hashv; \ - HASH_VALUE(keyptr, keylen, _hf_hashv); \ - HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl,oomed) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!(tbl)->bloom_bv) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ - } \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) - -#else -#define HASH_BLOOM_MAKE(tbl,oomed) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#define HASH_BLOOM_BYTELEN 0U -#endif - -#define HASH_MAKE_TABLE(hh,head,oomed) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ - if (!(head)->hh.tbl) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ - if (!(head)->hh.tbl->buckets) { \ - HASH_RECORD_OOM(oomed); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - } else { \ - uthash_bzero((head)->hh.tbl->buckets, \ - HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (oomed) { \ - uthash_free((head)->hh.tbl->buckets, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - } \ - ) \ - } \ - } \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -} while (0) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -} while (0) - -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -} while (0) - -#define HASH_APPEND_LIST(hh, head, add) \ -do { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail->next = (add); \ - (head)->hh.tbl->tail = &((add)->hh); \ -} while (0) - -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - do { \ - if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ - break; \ - } \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) - -#ifdef NO_DECLTYPE -#undef HASH_AKBI_INNER_LOOP -#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ -do { \ - char *_hs_saved_head = (char*)(head); \ - do { \ - DECLTYPE_ASSIGN(head, _hs_iter); \ - if (cmpfcn(head, add) > 0) { \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - break; \ - } \ - DECLTYPE_ASSIGN(head, _hs_saved_head); \ - } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ -} while (0) -#endif - -#if HASH_NONFATAL_OOM - -#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ -do { \ - if (!(oomed)) { \ - unsigned _ha_bkt; \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ - if (oomed) { \ - HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ - HASH_DELETE_HH(hh, head, &(add)->hh); \ - (add)->hh.tbl = NULL; \ - uthash_nonfatal_oom(add); \ - } else { \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ - } \ - } else { \ - (add)->hh.tbl = NULL; \ - uthash_nonfatal_oom(add); \ - } \ -} while (0) - -#else - -#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ -do { \ - unsigned _ha_bkt; \ - (head)->hh.tbl->num_items++; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ - HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ - HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ -} while (0) - -#endif - - -#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ -do { \ - IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh, add, _ha_oomed); \ - IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ - (head) = (add); \ - IF_HASH_NONFATAL_OOM( } ) \ - } else { \ - void *_hs_iter = (head); \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ - if (_hs_iter) { \ - (add)->hh.next = _hs_iter; \ - if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ - HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ - } else { \ - (head) = (add); \ - } \ - HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ - } else { \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - } \ - HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ - HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ -} while (0) - -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -do { \ - unsigned _hs_hashv; \ - HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) - -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ - HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) - -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ -do { \ - IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (char*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh, add, _ha_oomed); \ - IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ - (head) = (add); \ - IF_HASH_NONFATAL_OOM( } ) \ - } else { \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ - HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ -} while (0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_hashv; \ - HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ -} while (0) - -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) - -#define HASH_TO_BKT(hashv,num_bkts,bkt) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while (0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ - HASH_DELETE_HH(hh, head, &(delptr)->hh) - -#define HASH_DELETE_HH(hh,head,delptrhh) \ -do { \ - struct UT_hash_handle *_hd_hh_del = (delptrhh); \ - if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head) = NULL; \ - } else { \ - unsigned _hd_bkt; \ - if (_hd_hh_del == (head)->hh.tbl->tail) { \ - (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ - } \ - if (_hd_hh_del->prev != NULL) { \ - HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ - } else { \ - DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ - } \ - HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ -} while (0) - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ -do { \ - unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ - HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ -} while (0) -#define HASH_ADD_STR(head,strfield,add) \ -do { \ - unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ - HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ -} while (0) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ -do { \ - unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ - HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ -} while (0) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head,where) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count = 0; \ - char *_prev; \ - for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ - (where), (void*)_thh->hh_prev, (void*)_prev); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ - (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ - (where), (head)->hh.tbl->num_items, _count); \ - } \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev != (char*)_thh->prev) { \ - HASH_OOPS("%s: invalid prev %p, actual %p\n", \ - (where), (void*)_thh->prev, (void*)_prev); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ - (where), (head)->hh.tbl->num_items, _count); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head,where) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,hashv) \ -do { \ - unsigned _hb_keylen = (unsigned)keylen; \ - const unsigned char *_hb_key = (const unsigned char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ - } \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,hashv) \ -do { \ - unsigned _sx_i; \ - const unsigned char *_hs_key = (const unsigned char*)(key); \ - hashv = 0; \ - for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - } \ -} while (0) -/* FNV-1a variation */ -#define HASH_FNV(key,keylen,hashv) \ -do { \ - unsigned _fn_i; \ - const unsigned char *_hf_key = (const unsigned char*)(key); \ - (hashv) = 2166136261U; \ - for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ - } \ -} while (0) - -#define HASH_OAT(key,keylen,hashv) \ -do { \ - unsigned _ho_i; \ - const unsigned char *_ho_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ -} while (0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,hashv) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned const char *_hj_key=(unsigned const char*)(key); \ - hashv = 0xfeedbeefu; \ - _hj_i = _hj_j = 0x9e3779b9u; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12U) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12U; \ - } \ - hashv += (unsigned)(keylen); \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -} while (0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,hashv) \ -do { \ - unsigned const char *_sfh_key=(unsigned const char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ - \ - unsigned _sfh_rem = _sfh_len & 3U; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabeu; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0U; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2U*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ -} while (0) - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -#define MUR_GETBLOCK(p,i) p[i] -#else /* non intel */ -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -#else /* assume little endian non-intel */ -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -#endif -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ - (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ - (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ - MUR_ONE_THREE(p)))) -#endif -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -#define MUR_FMIX(_h) \ -do { \ - _h ^= _h >> 16; \ - _h *= 0x85ebca6bu; \ - _h ^= _h >> 13; \ - _h *= 0xc2b2ae35u; \ - _h ^= _h >> 16; \ -} while (0) - -#define HASH_MUR(key,keylen,hashv) \ -do { \ - const uint8_t *_mur_data = (const uint8_t*)(key); \ - const int _mur_nblocks = (int)(keylen) / 4; \ - uint32_t _mur_h1 = 0xf88D5353u; \ - uint32_t _mur_c1 = 0xcc9e2d51u; \ - uint32_t _mur_c2 = 0x1b873593u; \ - uint32_t _mur_k1 = 0; \ - const uint8_t *_mur_tail; \ - const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ - int _mur_i; \ - for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ - _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - \ - _mur_h1 ^= _mur_k1; \ - _mur_h1 = MUR_ROTL32(_mur_h1,13); \ - _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ - } \ - _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ - _mur_k1=0; \ - switch ((keylen) & 3U) { \ - case 0: break; \ - case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ - case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ - case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - _mur_h1 ^= _mur_k1; \ - } \ - _mur_h1 ^= (uint32_t)(keylen); \ - MUR_FMIX(_mur_h1); \ - hashv = _mur_h1; \ -} while (0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ -do { \ - if ((head).hh_head != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ - } else { \ - (out) = NULL; \ - } \ - while ((out) != NULL) { \ - if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ - if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ - break; \ - } \ - } \ - if ((out)->hh.hh_next != NULL) { \ - DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ - } else { \ - (out) = NULL; \ - } \ - } \ -} while (0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ -do { \ - UT_hash_bucket *_ha_head = &(head); \ - _ha_head->count++; \ - (addhh)->hh_next = _ha_head->hh_head; \ - (addhh)->hh_prev = NULL; \ - if (_ha_head->hh_head != NULL) { \ - _ha_head->hh_head->hh_prev = (addhh); \ - } \ - _ha_head->hh_head = (addhh); \ - if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ - && !(addhh)->tbl->noexpand) { \ - HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (oomed) { \ - HASH_DEL_IN_BKT(head,addhh); \ - } \ - ) \ - } \ -} while (0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(head,delhh) \ -do { \ - UT_hash_bucket *_hd_head = &(head); \ - _hd_head->count--; \ - if (_hd_head->hh_head == (delhh)) { \ - _hd_head->hh_head = (delhh)->hh_next; \ - } \ - if ((delhh)->hh_prev) { \ - (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ - } \ - if ((delhh)->hh_next) { \ - (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ - } \ -} while (0) - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { \ - HASH_RECORD_OOM(oomed); \ - } else { \ - uthash_bzero(_he_new_buckets, \ - 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ - (tbl)->ideal_chain_maxlen = \ - ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ - ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - (tbl)->nonideal_items = 0; \ - for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ - _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[_he_bkt]); \ - if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ - (tbl)->nonideal_items++; \ - if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ - _he_newbkt->expand_mult++; \ - } \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { \ - _he_newbkt->hh_head->hh_prev = _he_thh; \ - } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ - (tbl)->num_buckets *= 2U; \ - (tbl)->log2_num_buckets++; \ - (tbl)->buckets = _he_new_buckets; \ - (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ - ((tbl)->ineff_expands+1U) : 0U; \ - if ((tbl)->ineff_expands > 1U) { \ - (tbl)->noexpand = 1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ - } \ -} while (0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ - _hs_psize++; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - if (_hs_q == NULL) { \ - break; \ - } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - _hs_qsize--; \ - } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL) { \ - _hs_p = ((_hs_p->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ - } \ - _hs_psize--; \ - } else if ((cmpfcn( \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ - )) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL) { \ - _hs_p = ((_hs_p->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = ((_hs_q->next != NULL) ? \ - HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail != NULL) { \ - _hs_tail->next = NULL; \ - } \ - if (_hs_nmerges <= 1U) { \ - _hs_looping = 0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2U; \ - } \ - HASH_FSCK(hh, head, "HASH_SRT"); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt = NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if ((src) != NULL) { \ - for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { \ - _last_elt_hh->next = _elt; \ - } \ - if ((dst) == NULL) { \ - DECLTYPE_ASSIGN(dst, _elt); \ - HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ - IF_HASH_NONFATAL_OOM( \ - if (_hs_oomed) { \ - uthash_nonfatal_oom(_elt); \ - (dst) = NULL; \ - continue; \ - } \ - ) \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ - (dst)->hh_dst.tbl->num_items++; \ - IF_HASH_NONFATAL_OOM( \ - if (_hs_oomed) { \ - HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ - HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ - _dst_hh->tbl = NULL; \ - uthash_nonfatal_oom(_elt); \ - continue; \ - } \ - ) \ - HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if ((head) != NULL) { \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head) = NULL; \ - } \ -} while (0) - -#define HASH_OVERHEAD(hh,head) \ - (((head) != NULL) ? ( \ - (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - sizeof(UT_hash_table) + \ - (HASH_BLOOM_BYTELEN))) : 0U) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1u -#define HASH_BLOOM_SIGNATURE 0xb12220f2u - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - uint8_t bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 99b4e6e..8fdf1d2 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -6,8 +6,6 @@ #include #include #include -#include "uthash.h" -#include "utarray.h" #ifdef USE_MINIZIP # include @@ -531,19 +529,29 @@ struct main_sheet_get_rels_callback_data { XML_Char* stylesfile; }; -struct date_struct { - int id; /* key */ +struct xlsxio_number_format { int isDate; + int numFmtId; XML_Char* fmt; - UT_hash_handle hh; /* makes this structure hashable */ +}; + +struct xlsxio_cell_style { + int numFmtId; + struct xlsxio_number_format* numberFormat; }; // aka xlsxioreader struct xlsxio_read_struct { ZIPFILETYPE* zip; const char* filename; - struct date_struct *date_formats; - UT_array *number_formats; + + struct xlsxio_cell_style **styles; + int stylesCount; + int currentStyleIdx; + + struct xlsxio_number_format **numberFormats; + int numberFormatsCount; + int currentNumberFormatIdx; }; struct styles_callback_data { @@ -657,8 +665,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open (const char* filename) } result->filename = filename; - result->number_formats = NULL; - result->date_formats = NULL; + result->numberFormats = NULL; get_styles(result); @@ -696,8 +703,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_filehandle (int filehandle) } result->filename = "*filehandle*"; - result->number_formats = NULL; - result->date_formats = NULL; + result->numberFormats = NULL; get_styles(result); @@ -745,8 +751,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data #endif result->filename = "*memory*"; - result->number_formats = NULL; - result->date_formats = NULL; + result->numberFormats = NULL; get_styles(result); @@ -758,7 +763,7 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) if (handle) { // if we call "free()" more then once then it will crash with "pointer being freed was not allocated" // so we can call xlsxioread_close multiple times - int needFreeItself = handle->zip != NULL || handle->date_formats != NULL || handle->number_formats != NULL; + int needFreeItself = handle->zip != NULL || handle->styles != NULL || handle->numberFormats != NULL; //note: no need to call zip_source_free() after successful use in zip_open_from_source() if (handle->zip) { @@ -770,14 +775,22 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) handle->zip = NULL; } - if (handle->date_formats) { - HASH_CLEAR(hh, handle->date_formats); - handle->date_formats = NULL; + if (handle->styles) { + for(int i = 0; i < handle->stylesCount; i++) { + free(handle->styles[i]); + } + free(handle->styles); + handle->stylesCount = 0; + handle->styles = NULL; } - if (handle->number_formats) { - utarray_free(handle->number_formats); - handle->number_formats = NULL; + if (handle->numberFormats) { + for(int i = 0; i < handle->numberFormatsCount; i++) { + free(handle->numberFormats[i]); + } + free(handle->numberFormats); + handle->numberFormatsCount = 0; + handle->numberFormats = NULL; } if (needFreeItself) { @@ -941,36 +954,50 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const if (strcmp(name, "cellXfs") == 0) { pdata->lookForStyles = 1; + // allocate pdata->handle->styles according to "count" attribute + int stylesCount = atoi(get_expat_attr_by_name(atts, "count")); + pdata->handle->stylesCount = stylesCount; + if (stylesCount > 0) { + pdata->handle->styles = malloc(sizeof(struct xlsxio_cell_style) * stylesCount); + } } else if (strcmp(name, "numFmts") == 0) { pdata->lookForDates = 1; + // allocate pdata->handle->numberFormatsCount according to "count" attribute + int formatsCount = atoi(get_expat_attr_by_name(atts, "count")); + pdata->handle->numberFormatsCount = formatsCount; + if (formatsCount > 0) { + pdata->handle->numberFormats = malloc(sizeof(struct xlsxio_number_format) * formatsCount); + } } else if (pdata->lookForStyles && strcmp(name, "xf") == 0) { - const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); - int iVal = atoi(numFmtId); - utarray_push_back(pdata->handle->number_formats, &iVal); + // check if we have more tags then allocated + if (pdata->handle->currentStyleIdx < pdata->handle->stylesCount) { + const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + struct xlsxio_cell_style *cell_style; + cell_style = malloc(sizeof(struct xlsxio_cell_style)); + cell_style->numFmtId = atoi(numFmtId); + cell_style->numberFormat = NULL; + + pdata->handle->styles[pdata->handle->currentStyleIdx] = cell_style; + pdata->handle->currentStyleIdx++; + } + } else if (strcmp(name, "numFmt") == 0) { - const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); - struct date_struct *dateStruct; - dateStruct = malloc(sizeof(struct date_struct)); - numFmtId = get_expat_attr_by_name(atts, "numFmtId"); - dateStruct->id = atoi(numFmtId); - - const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); - - dateStruct->isDate = is_date_format(formatCode); - dateStruct->fmt = XML_Char_dupchar(formatCode); - HASH_ADD_INT(pdata->handle->date_formats, id, dateStruct); /* id: name of key field */ - } else if (pdata->lookForDates && strcmp(name, "xf") == 0) { - const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); - struct date_struct *dateStruct; - dateStruct = malloc(sizeof(struct date_struct)); - numFmtId = get_expat_attr_by_name(atts, "numFmtId"); - dateStruct->id = atoi(numFmtId); - - const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); - - dateStruct->isDate = is_date_format(formatCode); - dateStruct->fmt = XML_Char_dupchar(formatCode); - HASH_ADD_INT(pdata->handle->date_formats, id, dateStruct); /* id: name of key field */ + // check if we have more tags then allocated + if (pdata->handle->currentNumberFormatIdx < pdata->handle->numberFormatsCount) { + const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); + + struct xlsxio_number_format *number_format; + number_format = malloc(sizeof(struct xlsxio_number_format)); + number_format->numFmtId = atoi(numFmtId); + + const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); + + number_format->fmt = XML_Char_dupchar(formatCode); + number_format->isDate = is_date_format(formatCode); + + pdata->handle->numberFormats[pdata->handle->currentNumberFormatIdx] = number_format; + pdata->handle->currentNumberFormatIdx++; + } } } @@ -1333,25 +1360,23 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch if (s == NULL) { data->cell_type = cell_type_value; } else { - int formatIndex = atoi(s); - int formatNum = *(int*)utarray_eltptr(data->handle->number_formats, formatIndex); - - struct date_struct *dateStruct = NULL; + int styleNum = atoi(s); + if (styleNum < data->handle->currentNumberFormatIdx) { + struct xlsxio_cell_style *cell_style = data->handle->styles[styleNum]; - HASH_FIND_INT(data->handle->date_formats, &formatNum, dateStruct); + int isDateTime = cell_style->numberFormat ? cell_style->numberFormat->isDate : 0; - int isDateTime = dateStruct ? dateStruct->isDate : 0; - - // builtin date formats are 14 - 22 - if ((formatNum > 13 && formatNum < 23) || isDateTime) { - data->cell_type = cell_type_date; - } else { - data->cell_type = cell_type_value; - } + // builtin date formats are 14 - 22 + if ((cell_style->numFmtId > 13 && cell_style->numFmtId < 23) || isDateTime) { + data->cell_type = cell_type_date; + } else { + data->cell_type = cell_type_value; + } - // apply for dates and numbers - if (dateStruct) { - data->number_fmt = dateStruct->fmt; + // apply for dates and numbers + if (cell_style->numberFormat) { + data->number_fmt = cell_style->numberFormat->fmt; + } } } } @@ -1463,36 +1488,51 @@ void data_sheet_expat_callback_value_data (void* callbackdata, const XML_Char* b } // extract number and date formats from styles.xml -// save it in handle->number_formats and handle->date_formats +// save it in handle->numberFormats and handle->date_formats void get_styles(xlsxioreader handle) { struct styles_callback_data stylesCallbackData; stylesCallbackData.lookForStyles = 0; stylesCallbackData.lookForDates = 0; stylesCallbackData.handle = handle; - utarray_new(handle->number_formats, &ut_int_icd); - HASH_CLEAR(hh, handle->date_formats); + handle->currentNumberFormatIdx = 0; + handle->currentStyleIdx = 0; + + //utarray_new(handle->numberFormats, &ut_int_icd); + //HASH_CLEAR(hh, handle->date_formats); expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylesCallbackData, NULL); + + // attach number_format struct to style struct + for (int si = 0; si < handle->currentStyleIdx; si++) { + if (handle->styles[si]->numFmtId) { + for(int fi = 0; fi < handle->currentNumberFormatIdx; fi++) { + if (handle->numberFormats[fi]->numFmtId == handle->styles[si]->numFmtId) { + handle->styles[si]->numberFormat = handle->numberFormats[fi]; + } + } + } + } } DLL_EXPORT_XLSXIO void xlsxioread_debug_internals(xlsxioreader handle) { printf("\n"); printf("XLSXIO_READ DEBUG - %s\n", handle->filename); - printf("number_formats:\n"); - int *p, i = 0; - for (p = (int*)utarray_front(handle->number_formats); p != NULL; p = (int*)utarray_next(handle->number_formats, p)) { - printf(" number_format %i - %d\n", i, *p); - i += 1; + printf("number_formats:\n"); + for(int fi = 0; fi < handle->currentNumberFormatIdx; fi++) { + printf(" numFmtId = %d\tisDate = %d\tnumber_format = %s\n", + handle->numberFormats[fi]->numFmtId, handle->numberFormats[fi]->isDate, handle->numberFormats[fi]->fmt); } - printf("date_formats:\n"); - - struct date_struct *dateFmt = NULL; - for (dateFmt = handle->date_formats; dateFmt != NULL; dateFmt = (struct date_struct*)(dateFmt->hh.next)) { - printf(" date_format id = %d\t isDate = %d\t fmt = %s\n", dateFmt->id, dateFmt->isDate, dateFmt->fmt); + printf("styles:\n"); + struct xlsxio_number_format *numberFormat; + for (int si = 0; si < handle->currentStyleIdx; si++) { + numberFormat = handle->styles[si]->numberFormat; + printf(" id = %d\tnumFmtId = %d\tisDate = %d\tnumber_format = %s\n", + si, handle->styles[si]->numFmtId, numberFormat ? numberFormat->isDate : 0, numberFormat ? numberFormat->fmt : NULL); } + printf("\n"); } @@ -1683,7 +1723,6 @@ DLL_EXPORT_XLSXIO int xlsxioread_sheet_next_row (xlsxioreadersheet sheethandle) DLL_EXPORT_XLSXIO struct data_sheet_cell_data* xlsxioread_sheet_next_cell_struct (xlsxioreadersheet sheethandle) { - XML_Char* result; if (!sheethandle) { return NULL; } From 574af78eb5a2dd17a9789a4f591bb36c96152d10 Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sun, 17 Mar 2019 01:11:53 +0800 Subject: [PATCH 19/22] Try to fix build on windows --- .travis.yml | 44 ++++++++-------------------------------- build/ci_windows_prepare | 20 ++++++++++++++++++ 2 files changed, 28 insertions(+), 36 deletions(-) create mode 100755 build/ci_windows_prepare diff --git a/.travis.yml b/.travis.yml index 396b0a0..152d05b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,39 +14,11 @@ addons: homebrew: packages: ["minizip"] -jobs: - allow_failures: - - os: windows - include: - - stage: test - os: windows - before_script: - - wget https://github.com/nmoinvaz/minizip/archive/2.8.4.zip - - unzip 2.8.4.zip - - cd minizip-2.8.4 - - mkdir build - - cd build - - cmake .. -DMZ_BUILD_TEST=ON -DMZ_BUILD_UNIT_TEST=ON -DCMAKE_BUILD_TYPE=Release - - cmake --build . --config Release - - cd ../.. - script: - - cmake . -DMINIZIP_DIR:PATH=$(pwd)/minizip-2.8.4/build - - cmake --build . - - ./example_xlsxio_write - - ls -lah example.xlsx - - - stage: test - os: linux - script: - - cmake . $XLSXIO_CMAKE_ARGS - - cmake --build . - - ./example_xlsxio_write - - ls -lah example.xlsx - - - stage: test - os: osx - script: - - cmake . $XLSXIO_CMAKE_ARGS - - cmake --build . - - ./example_xlsxio_write - - ls -lah example.xlsx +script: + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then ./build/ci_windows_prepare; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then export XLSXIO_CMAKE_ARGS="-DMINIZIP_DIR:PATH=$(pwd)/minizip-2.8.4/build"; fi + - cmake . $XLSXIO_CMAKE_ARGS + - cmake --build . + - ./example_xlsxio_write + - ls -lah example.xlsx + - ./example_xlsxio_read diff --git a/build/ci_windows_prepare b/build/ci_windows_prepare new file mode 100755 index 0000000..83b7ebe --- /dev/null +++ b/build/ci_windows_prepare @@ -0,0 +1,20 @@ +#!/bin/bash + +set -v +set -e + +wget https://zlib.net/zlib-1.2.11.tar.gz +tar -xzf zlib-1.2.11.tar.gz +rm zlib-1.2.11.tar.gz + +cd zlib-1.2.11 + +./configure +make +make install + +cd contrib/minizip +make +make install + +ls -lah From 99162beef020117ab98da34be69fa8d108ec7b4c Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Sun, 17 Mar 2019 01:31:02 +0800 Subject: [PATCH 20/22] Convert camelCase to snake_case --- lib/xlsxio_read.c | 133 ++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 8fdf1d2..354fa5a 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -530,14 +530,14 @@ struct main_sheet_get_rels_callback_data { }; struct xlsxio_number_format { - int isDate; + int is_date; int numFmtId; XML_Char* fmt; }; struct xlsxio_cell_style { int numFmtId; - struct xlsxio_number_format* numberFormat; + struct xlsxio_number_format* numberformat; }; // aka xlsxioreader @@ -546,18 +546,18 @@ struct xlsxio_read_struct { const char* filename; struct xlsxio_cell_style **styles; - int stylesCount; - int currentStyleIdx; + int styles_count; + int current_style_idx; - struct xlsxio_number_format **numberFormats; - int numberFormatsCount; - int currentNumberFormatIdx; + struct xlsxio_number_format **numberformats; + int numberformats_count; + int current_numberformat_idx; }; struct styles_callback_data { XML_Parser xmlparser; - int lookForStyles; - int lookForDates; + int look_for_styles; + int look_for_dates; struct xlsxio_read_struct* handle; }; @@ -665,7 +665,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open (const char* filename) } result->filename = filename; - result->numberFormats = NULL; + result->numberformats = NULL; get_styles(result); @@ -703,7 +703,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_filehandle (int filehandle) } result->filename = "*filehandle*"; - result->numberFormats = NULL; + result->numberformats = NULL; get_styles(result); @@ -751,7 +751,7 @@ DLL_EXPORT_XLSXIO xlsxioreader xlsxioread_open_memory (void* data, uint64_t data #endif result->filename = "*memory*"; - result->numberFormats = NULL; + result->numberformats = NULL; get_styles(result); @@ -763,7 +763,7 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) if (handle) { // if we call "free()" more then once then it will crash with "pointer being freed was not allocated" // so we can call xlsxioread_close multiple times - int needFreeItself = handle->zip != NULL || handle->styles != NULL || handle->numberFormats != NULL; + int needFreeItself = handle->zip != NULL || handle->styles != NULL || handle->numberformats != NULL; //note: no need to call zip_source_free() after successful use in zip_open_from_source() if (handle->zip) { @@ -776,21 +776,21 @@ DLL_EXPORT_XLSXIO void xlsxioread_close (xlsxioreader handle) } if (handle->styles) { - for(int i = 0; i < handle->stylesCount; i++) { + for(int i = 0; i < handle->styles_count; i++) { free(handle->styles[i]); } free(handle->styles); - handle->stylesCount = 0; + handle->styles_count = 0; handle->styles = NULL; } - if (handle->numberFormats) { - for(int i = 0; i < handle->numberFormatsCount; i++) { - free(handle->numberFormats[i]); + if (handle->numberformats) { + for(int i = 0; i < handle->numberformats_count; i++) { + free(handle->numberformats[i]); } - free(handle->numberFormats); - handle->numberFormatsCount = 0; - handle->numberFormats = NULL; + free(handle->numberformats); + handle->numberformats_count = 0; + handle->numberformats = NULL; } if (needFreeItself) { @@ -953,37 +953,37 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; if (strcmp(name, "cellXfs") == 0) { - pdata->lookForStyles = 1; + pdata->look_for_styles = 1; // allocate pdata->handle->styles according to "count" attribute - int stylesCount = atoi(get_expat_attr_by_name(atts, "count")); - pdata->handle->stylesCount = stylesCount; - if (stylesCount > 0) { - pdata->handle->styles = malloc(sizeof(struct xlsxio_cell_style) * stylesCount); + int styles_count = atoi(get_expat_attr_by_name(atts, "count")); + pdata->handle->styles_count = styles_count; + if (styles_count > 0) { + pdata->handle->styles = malloc(sizeof(struct xlsxio_cell_style) * styles_count); } } else if (strcmp(name, "numFmts") == 0) { - pdata->lookForDates = 1; - // allocate pdata->handle->numberFormatsCount according to "count" attribute - int formatsCount = atoi(get_expat_attr_by_name(atts, "count")); - pdata->handle->numberFormatsCount = formatsCount; - if (formatsCount > 0) { - pdata->handle->numberFormats = malloc(sizeof(struct xlsxio_number_format) * formatsCount); + pdata->look_for_dates = 1; + // allocate pdata->handle->numberformats_count according to "count" attribute + int formats_count = atoi(get_expat_attr_by_name(atts, "count")); + pdata->handle->numberformats_count = formats_count; + if (formats_count > 0) { + pdata->handle->numberformats = malloc(sizeof(struct xlsxio_number_format) * formats_count); } - } else if (pdata->lookForStyles && strcmp(name, "xf") == 0) { + } else if (pdata->look_for_styles && strcmp(name, "xf") == 0) { // check if we have more tags then allocated - if (pdata->handle->currentStyleIdx < pdata->handle->stylesCount) { + if (pdata->handle->current_style_idx < pdata->handle->styles_count) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); struct xlsxio_cell_style *cell_style; cell_style = malloc(sizeof(struct xlsxio_cell_style)); cell_style->numFmtId = atoi(numFmtId); - cell_style->numberFormat = NULL; + cell_style->numberformat = NULL; - pdata->handle->styles[pdata->handle->currentStyleIdx] = cell_style; - pdata->handle->currentStyleIdx++; + pdata->handle->styles[pdata->handle->current_style_idx] = cell_style; + pdata->handle->current_style_idx++; } } else if (strcmp(name, "numFmt") == 0) { // check if we have more tags then allocated - if (pdata->handle->currentNumberFormatIdx < pdata->handle->numberFormatsCount) { + if (pdata->handle->current_numberformat_idx < pdata->handle->numberformats_count) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); struct xlsxio_number_format *number_format; @@ -993,10 +993,10 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const const XML_Char* formatCode = get_expat_attr_by_name(atts, "formatCode"); number_format->fmt = XML_Char_dupchar(formatCode); - number_format->isDate = is_date_format(formatCode); + number_format->is_date = is_date_format(formatCode); - pdata->handle->numberFormats[pdata->handle->currentNumberFormatIdx] = number_format; - pdata->handle->currentNumberFormatIdx++; + pdata->handle->numberformats[pdata->handle->current_numberformat_idx] = number_format; + pdata->handle->current_numberformat_idx++; } } } @@ -1004,10 +1004,10 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const void format_types_end_callback(void* callbackdata, const XML_Char* name) { if (strcmp(name, "cellXfs") == 0) { struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; - pdata->lookForStyles = 0; + pdata->look_for_styles = 0; } else if (strcmp(name, "numFmts") == 0) { struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; - pdata->lookForDates = 0; + pdata->look_for_dates = 0; } } @@ -1361,21 +1361,21 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch data->cell_type = cell_type_value; } else { int styleNum = atoi(s); - if (styleNum < data->handle->currentNumberFormatIdx) { + if (styleNum < data->handle->current_numberformat_idx) { struct xlsxio_cell_style *cell_style = data->handle->styles[styleNum]; - int isDateTime = cell_style->numberFormat ? cell_style->numberFormat->isDate : 0; + int is_dateTime = cell_style->numberformat ? cell_style->numberformat->is_date : 0; // builtin date formats are 14 - 22 - if ((cell_style->numFmtId > 13 && cell_style->numFmtId < 23) || isDateTime) { + if ((cell_style->numFmtId > 13 && cell_style->numFmtId < 23) || is_dateTime) { data->cell_type = cell_type_date; } else { data->cell_type = cell_type_value; } // apply for dates and numbers - if (cell_style->numberFormat) { - data->number_fmt = cell_style->numberFormat->fmt; + if (cell_style->numberformat) { + data->number_fmt = cell_style->numberformat->fmt; } } } @@ -1488,27 +1488,24 @@ void data_sheet_expat_callback_value_data (void* callbackdata, const XML_Char* b } // extract number and date formats from styles.xml -// save it in handle->numberFormats and handle->date_formats +// save it in handle->numberformats and handle->date_formats void get_styles(xlsxioreader handle) { struct styles_callback_data stylesCallbackData; - stylesCallbackData.lookForStyles = 0; - stylesCallbackData.lookForDates = 0; + stylesCallbackData.look_for_styles = 0; + stylesCallbackData.look_for_dates = 0; stylesCallbackData.handle = handle; - handle->currentNumberFormatIdx = 0; - handle->currentStyleIdx = 0; - - //utarray_new(handle->numberFormats, &ut_int_icd); - //HASH_CLEAR(hh, handle->date_formats); + handle->current_numberformat_idx = 0; + handle->current_style_idx = 0; expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylesCallbackData, NULL); // attach number_format struct to style struct - for (int si = 0; si < handle->currentStyleIdx; si++) { + for (int si = 0; si < handle->current_style_idx; si++) { if (handle->styles[si]->numFmtId) { - for(int fi = 0; fi < handle->currentNumberFormatIdx; fi++) { - if (handle->numberFormats[fi]->numFmtId == handle->styles[si]->numFmtId) { - handle->styles[si]->numberFormat = handle->numberFormats[fi]; + for(int fi = 0; fi < handle->current_numberformat_idx; fi++) { + if (handle->numberformats[fi]->numFmtId == handle->styles[si]->numFmtId) { + handle->styles[si]->numberformat = handle->numberformats[fi]; } } } @@ -1520,17 +1517,17 @@ DLL_EXPORT_XLSXIO void xlsxioread_debug_internals(xlsxioreader handle) { printf("XLSXIO_READ DEBUG - %s\n", handle->filename); printf("number_formats:\n"); - for(int fi = 0; fi < handle->currentNumberFormatIdx; fi++) { - printf(" numFmtId = %d\tisDate = %d\tnumber_format = %s\n", - handle->numberFormats[fi]->numFmtId, handle->numberFormats[fi]->isDate, handle->numberFormats[fi]->fmt); + for(int fi = 0; fi < handle->current_numberformat_idx; fi++) { + printf(" numFmtId = %d\tis_date = %d\tnumber_format = %s\n", + handle->numberformats[fi]->numFmtId, handle->numberformats[fi]->is_date, handle->numberformats[fi]->fmt); } printf("styles:\n"); - struct xlsxio_number_format *numberFormat; - for (int si = 0; si < handle->currentStyleIdx; si++) { - numberFormat = handle->styles[si]->numberFormat; - printf(" id = %d\tnumFmtId = %d\tisDate = %d\tnumber_format = %s\n", - si, handle->styles[si]->numFmtId, numberFormat ? numberFormat->isDate : 0, numberFormat ? numberFormat->fmt : NULL); + struct xlsxio_number_format *numberformat; + for (int si = 0; si < handle->current_style_idx; si++) { + numberformat = handle->styles[si]->numberformat; + printf(" id = %d\tnumFmtId = %d\tis_date = %d\tnumber_format = %s\n", + si, handle->styles[si]->numFmtId, numberformat ? numberformat->is_date : 0, numberformat ? numberformat->fmt : NULL); } printf("\n"); From 43402c545b714a7bf4fb06dd3d464f2d22b8b6ed Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Wed, 27 Mar 2019 02:03:35 +0800 Subject: [PATCH 21/22] Fix detecting format --- lib/xlsxio_read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 354fa5a..773dd44 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -1361,7 +1361,7 @@ void data_sheet_expat_callback_find_cell_start (void* callbackdata, const XML_Ch data->cell_type = cell_type_value; } else { int styleNum = atoi(s); - if (styleNum < data->handle->current_numberformat_idx) { + if (styleNum < data->handle->current_style_idx) { struct xlsxio_cell_style *cell_style = data->handle->styles[styleNum]; int is_dateTime = cell_style->numberformat ? cell_style->numberformat->is_date : 0; From 74a030b75550d53d2eb8992d58711250e9beb5fe Mon Sep 17 00:00:00 2001 From: Pavel Evstigneev Date: Wed, 27 Mar 2019 17:24:42 +0800 Subject: [PATCH 22/22] Fix processing --- lib/xlsxio_read.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/xlsxio_read.c b/lib/xlsxio_read.c index 773dd44..3c6ebcb 100644 --- a/lib/xlsxio_read.c +++ b/lib/xlsxio_read.c @@ -557,7 +557,7 @@ struct xlsxio_read_struct { struct styles_callback_data { XML_Parser xmlparser; int look_for_styles; - int look_for_dates; + int look_for_numfmt; struct xlsxio_read_struct* handle; }; @@ -961,7 +961,7 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const pdata->handle->styles = malloc(sizeof(struct xlsxio_cell_style) * styles_count); } } else if (strcmp(name, "numFmts") == 0) { - pdata->look_for_dates = 1; + pdata->look_for_numfmt = 1; // allocate pdata->handle->numberformats_count according to "count" attribute int formats_count = atoi(get_expat_attr_by_name(atts, "count")); pdata->handle->numberformats_count = formats_count; @@ -981,8 +981,9 @@ void format_types_start_callback(void* callbackdata, const XML_Char* name, const pdata->handle->current_style_idx++; } - } else if (strcmp(name, "numFmt") == 0) { + } else if (pdata->look_for_numfmt && strcmp(name, "numFmt") == 0) { // check if we have more tags then allocated + if (pdata->handle->current_numberformat_idx < pdata->handle->numberformats_count) { const XML_Char* numFmtId = get_expat_attr_by_name(atts, "numFmtId"); @@ -1007,7 +1008,7 @@ void format_types_end_callback(void* callbackdata, const XML_Char* name) { pdata->look_for_styles = 0; } else if (strcmp(name, "numFmts") == 0) { struct styles_callback_data* pdata = (struct styles_callback_data*)callbackdata; - pdata->look_for_dates = 0; + pdata->look_for_numfmt = 0; } } @@ -1492,11 +1493,13 @@ void data_sheet_expat_callback_value_data (void* callbackdata, const XML_Char* b void get_styles(xlsxioreader handle) { struct styles_callback_data stylesCallbackData; stylesCallbackData.look_for_styles = 0; - stylesCallbackData.look_for_dates = 0; + stylesCallbackData.look_for_numfmt = 0; stylesCallbackData.handle = handle; handle->current_numberformat_idx = 0; handle->current_style_idx = 0; + handle->numberformats_count = 0; + handle->styles_count = 0; expat_process_zip_file(handle->zip, "xl/styles.xml", format_types_start_callback, format_types_end_callback, NULL, &stylesCallbackData, NULL);