/src/netcdf-c/libdispatch/daux.c
Line | Count | Source |
1 | | /* |
2 | | Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata |
3 | | See COPYRIGHT for license information. |
4 | | */ |
5 | | |
6 | | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
7 | | * Copyright by The HDF Group. * |
8 | | * Copyright by the Board of Trustees of the University of Illinois. * |
9 | | * All rights reserved. * |
10 | | * * |
11 | | * This file is part of HDF5. The full HDF5 copyright notice, including * |
12 | | * terms governing use, modification, and redistribution, is contained in * |
13 | | * the files COPYING and Copyright.html. COPYING can be found at the root * |
14 | | * of the source code distribution tree; Copyright.html can be found at the * |
15 | | * root level of an installed copy of the electronic HDF5 document set and * |
16 | | * is linked from the top-level documents page. It can also be found at * |
17 | | * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * |
18 | | * access to either file, you may request a copy from help@hdfgroup.org. * |
19 | | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
20 | | |
21 | | #include "config.h" |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <assert.h> |
25 | | #include "config.h" |
26 | | #include "netcdf.h" |
27 | | #include "netcdf_aux.h" |
28 | | #include "nc4internal.h" |
29 | | #include "ncoffsets.h" |
30 | | #include "nclog.h" |
31 | | #include "ncrc.h" |
32 | | #include "netcdf_filter.h" |
33 | | #include "ncpathmgr.h" |
34 | | #include "nclist.h" |
35 | | #include "ncutil.h" |
36 | | |
37 | | struct NCAUX_FIELD { |
38 | | char* name; |
39 | | nc_type fieldtype; |
40 | | size_t ndims; |
41 | | int dimsizes[NC_MAX_VAR_DIMS]; |
42 | | size_t size; |
43 | | size_t offset; |
44 | | size_t alignment; |
45 | | }; |
46 | | |
47 | | struct NCAUX_CMPD { |
48 | | int ncid; |
49 | | int mode; |
50 | | char* name; |
51 | | size_t nfields; |
52 | | struct NCAUX_FIELD* fields; |
53 | | size_t size; |
54 | | size_t offset; /* cumulative as fields are added */ |
55 | | size_t alignment; |
56 | | }; |
57 | | |
58 | | static int computefieldinfo(struct NCAUX_CMPD* cmpd); |
59 | | |
60 | | static int filterspec_cvt(const char* txt, size_t* nparamsp, unsigned int* params); |
61 | | |
62 | | EXTERNL int nc_dump_data(int ncid, nc_type xtype, void* memory, size_t count, char** bufp); |
63 | | EXTERNL int nc_parse_plugin_pathlist(const char* path0, NClist* dirlist); |
64 | | |
65 | | /**************************************************/ |
66 | | /* |
67 | | This code is a variant of the H5detect.c code from HDF5. |
68 | | Author: D. Heimbigner 10/7/2008 |
69 | | */ |
70 | | |
71 | | EXTERNL int |
72 | | ncaux_begin_compound(int ncid, const char *name, int alignmode, void** tagp) |
73 | 0 | { |
74 | 0 | #ifdef USE_NETCDF4 |
75 | 0 | int status = NC_NOERR; |
76 | 0 | struct NCAUX_CMPD* cmpd = NULL; |
77 | |
|
78 | 0 | if(tagp) *tagp = NULL; |
79 | |
|
80 | 0 | cmpd = (struct NCAUX_CMPD*)calloc(1,sizeof(struct NCAUX_CMPD)); |
81 | 0 | if(cmpd == NULL) {status = NC_ENOMEM; goto fail;} |
82 | 0 | cmpd->ncid = ncid; |
83 | 0 | cmpd->mode = alignmode; |
84 | 0 | cmpd->nfields = 0; |
85 | 0 | cmpd->name = strdup(name); |
86 | 0 | if(cmpd->name == NULL) {status = NC_ENOMEM; goto fail;} |
87 | | |
88 | 0 | if(tagp) { |
89 | 0 | *tagp = (void*)cmpd; |
90 | 0 | } else { /* Error, free cmpd to avoid memory leak. */ |
91 | 0 | free(cmpd); |
92 | 0 | } |
93 | 0 | return status; |
94 | | |
95 | 0 | fail: |
96 | 0 | ncaux_abort_compound((void*)cmpd); |
97 | 0 | return status; |
98 | | #else |
99 | | return NC_ENOTBUILT; |
100 | | #endif |
101 | 0 | } |
102 | | |
103 | | EXTERNL int |
104 | | ncaux_abort_compound(void* tag) |
105 | 0 | { |
106 | 0 | #ifdef USE_NETCDF4 |
107 | 0 | size_t i; |
108 | 0 | struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag; |
109 | 0 | if(cmpd == NULL) goto done; |
110 | 0 | if(cmpd->name) free(cmpd->name); |
111 | 0 | for(i=0;i<cmpd->nfields;i++) { |
112 | 0 | struct NCAUX_FIELD* field = &cmpd->fields[i]; |
113 | 0 | if(field->name) free(field->name); |
114 | 0 | } |
115 | 0 | if(cmpd->fields) free(cmpd->fields); |
116 | 0 | free(cmpd); |
117 | |
|
118 | 0 | done: |
119 | 0 | return NC_NOERR; |
120 | | #else |
121 | | return NC_ENOTBUILT; |
122 | | #endif |
123 | 0 | } |
124 | | |
125 | | EXTERNL int |
126 | | ncaux_add_field(void* tag, const char *name, nc_type field_type, |
127 | | int ndims, const int* dimsizes) |
128 | 0 | { |
129 | 0 | #ifdef USE_NETCDF4 |
130 | 0 | int i; |
131 | 0 | int status = NC_NOERR; |
132 | 0 | struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag; |
133 | 0 | struct NCAUX_FIELD* newfields = NULL; |
134 | 0 | struct NCAUX_FIELD* field = NULL; |
135 | |
|
136 | 0 | if(cmpd == NULL) goto done; |
137 | 0 | if(ndims < 0) {status = NC_EINVAL; goto done;} |
138 | 0 | for(i=0;i<ndims;i++) { |
139 | 0 | if(dimsizes[i] <= 0) {status = NC_EINVAL; goto done;} |
140 | 0 | } |
141 | 0 | if(cmpd->fields == NULL) { |
142 | 0 | newfields = (struct NCAUX_FIELD*)calloc(1,sizeof(struct NCAUX_FIELD)); |
143 | 0 | } else { |
144 | 0 | newfields = (struct NCAUX_FIELD*)realloc(cmpd->fields,cmpd->nfields+1*sizeof(struct NCAUX_FIELD)); |
145 | 0 | } |
146 | 0 | if(cmpd->fields == NULL) {status = NC_ENOMEM; goto done;} |
147 | 0 | cmpd->fields = newfields; |
148 | 0 | field = &cmpd->fields[cmpd->nfields+1]; |
149 | 0 | field->name = strdup(name); |
150 | 0 | field->fieldtype = field_type; |
151 | 0 | if(field->name == NULL) {status = NC_ENOMEM; goto done;} |
152 | 0 | field->ndims = (size_t)ndims; |
153 | 0 | memcpy(field->dimsizes,dimsizes,sizeof(int)*field->ndims); |
154 | 0 | cmpd->nfields++; |
155 | |
|
156 | 0 | done: |
157 | 0 | if(newfields) |
158 | 0 | free(newfields); |
159 | 0 | return status; |
160 | | #else |
161 | | return NC_ENOTBUILT; |
162 | | #endif |
163 | 0 | } |
164 | | |
165 | | EXTERNL int |
166 | | ncaux_end_compound(void* tag, nc_type* idp) |
167 | 0 | { |
168 | 0 | #ifdef USE_NETCDF4 |
169 | 0 | size_t i; |
170 | 0 | int status = NC_NOERR; |
171 | 0 | struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag; |
172 | |
|
173 | 0 | if(cmpd == NULL) {status = NC_EINVAL; goto done;} |
174 | | |
175 | | /* Compute field and compound info */ |
176 | 0 | status = computefieldinfo(cmpd); |
177 | 0 | if(status != NC_NOERR) goto done; |
178 | | |
179 | 0 | status = nc_def_compound(cmpd->ncid, cmpd->size, cmpd->name, idp); |
180 | 0 | if(status != NC_NOERR) goto done; |
181 | | |
182 | 0 | for(i=0;i<cmpd->nfields;i++) { |
183 | 0 | struct NCAUX_FIELD* field = &cmpd->fields[i]; |
184 | 0 | if(field->ndims > 0) { |
185 | 0 | status = nc_insert_compound(cmpd->ncid, *idp, field->name, |
186 | 0 | field->offset, field->fieldtype); |
187 | 0 | } else { |
188 | 0 | status = nc_insert_array_compound(cmpd->ncid, *idp, field->name, |
189 | 0 | field->offset, field->fieldtype, |
190 | 0 | (int)field->ndims,field->dimsizes); |
191 | 0 | } |
192 | 0 | if(status != NC_NOERR) goto done; |
193 | 0 | } |
194 | | |
195 | 0 | done: |
196 | 0 | return status; |
197 | | #else |
198 | | return NC_ENOTBUILT; |
199 | | #endif |
200 | 0 | } |
201 | | |
202 | | /**************************************************/ |
203 | | |
204 | | /** |
205 | | @param ncclass - type class for which alignment is requested; excludes ENUM|COMPOUND |
206 | | */ |
207 | | |
208 | | int |
209 | | ncaux_class_alignment(int ncclass, size_t* alignp) |
210 | 0 | { |
211 | 0 | int stat = NC_NOERR; |
212 | 0 | size_t align = 0; |
213 | 0 | if(ncclass <= NC_MAX_ATOMIC_TYPE || ncclass == NC_VLEN || ncclass == NC_OPAQUE) { |
214 | 0 | stat = NC_class_alignment(ncclass,&align); |
215 | 0 | } else { |
216 | 0 | nclog(NCLOGERR,"ncaux_class_alignment: class %d; alignment cannot be determermined",ncclass); |
217 | 0 | } |
218 | 0 | if(alignp) *alignp = align; |
219 | 0 | if(align == 0) stat = NC_EINVAL; |
220 | 0 | return stat; |
221 | 0 | } |
222 | | |
223 | | |
224 | | #ifdef USE_NETCDF4 |
225 | | /* Find first primitive field of a possibly nested sequence of compounds */ |
226 | | static nc_type |
227 | | findfirstfield(int ncid, nc_type xtype) |
228 | 0 | { |
229 | 0 | int status = NC_NOERR; |
230 | 0 | nc_type fieldtype = xtype; |
231 | 0 | if(xtype <= NC_MAX_ATOMIC_TYPE) goto done; |
232 | | |
233 | 0 | status = nc_inq_compound_fieldtype(ncid, xtype, 0, &fieldtype); |
234 | 0 | if(status != NC_NOERR) goto done; |
235 | 0 | fieldtype = findfirstfield(ncid,fieldtype); |
236 | |
|
237 | 0 | done: |
238 | 0 | return (status == NC_NOERR?fieldtype:NC_NAT); |
239 | 0 | } |
240 | | |
241 | | static size_t |
242 | | getpadding(size_t offset, size_t alignment) |
243 | 0 | { |
244 | 0 | size_t rem = (alignment==0?0:(offset % alignment)); |
245 | 0 | size_t pad = (rem==0?0:(alignment - rem)); |
246 | 0 | return pad; |
247 | 0 | } |
248 | | |
249 | | static size_t |
250 | | dimproduct(size_t ndims, int* dimsizes) |
251 | 0 | { |
252 | 0 | size_t i; |
253 | 0 | size_t product = 1; |
254 | 0 | for(i=0;i<ndims;i++) product *= (size_t)dimsizes[i]; |
255 | 0 | return product; |
256 | 0 | } |
257 | | |
258 | | static int |
259 | | computefieldinfo(struct NCAUX_CMPD* cmpd) |
260 | 0 | { |
261 | 0 | size_t i; |
262 | 0 | int status = NC_NOERR; |
263 | 0 | size_t offset = 0; |
264 | 0 | size_t totaldimsize; |
265 | | |
266 | | /* Assign the sizes for the fields */ |
267 | 0 | for(i=0;i<cmpd->nfields;i++) { |
268 | 0 | struct NCAUX_FIELD* field = &cmpd->fields[i]; |
269 | 0 | status = nc_inq_type(cmpd->ncid,field->fieldtype,NULL,&field->size); |
270 | 0 | if(status != NC_NOERR) goto done; |
271 | 0 | totaldimsize = dimproduct(field->ndims,field->dimsizes); |
272 | 0 | field->size *= totaldimsize; |
273 | 0 | } |
274 | | |
275 | 0 | for(offset=0,i=0;i<cmpd->nfields;i++) { |
276 | 0 | struct NCAUX_FIELD* field = &cmpd->fields[i]; |
277 | 0 | size_t alignment = 0; |
278 | 0 | nc_type firsttype = findfirstfield(cmpd->ncid,field->fieldtype); |
279 | | |
280 | | /* only support 'c' alignment for now*/ |
281 | 0 | switch (field->fieldtype) { |
282 | 0 | case NC_OPAQUE: |
283 | 0 | field->alignment = 1; |
284 | 0 | break; |
285 | 0 | case NC_ENUM: |
286 | 0 | status = ncaux_type_alignment(firsttype,cmpd->ncid,&field->alignment); |
287 | 0 | break; |
288 | 0 | case NC_VLEN: /*fall thru*/ |
289 | 0 | case NC_COMPOUND: |
290 | 0 | status = ncaux_type_alignment(firsttype,cmpd->ncid,&field->alignment); |
291 | 0 | break; |
292 | 0 | default: |
293 | 0 | status = ncaux_type_alignment(field->fieldtype,cmpd->ncid,&field->alignment); |
294 | 0 | break; |
295 | |
|
296 | 0 | } |
297 | 0 | offset += getpadding(offset,alignment); |
298 | 0 | field->offset = offset; |
299 | 0 | offset += field->size; |
300 | 0 | } |
301 | 0 | cmpd->size = offset; |
302 | 0 | cmpd->alignment = cmpd->fields[0].alignment; |
303 | |
|
304 | 0 | done: |
305 | 0 | return status; |
306 | 0 | } |
307 | | |
308 | | #endif /*USE_NETCDF4*/ |
309 | | |
310 | | |
311 | | /**************************************************/ |
312 | | /* Forward */ |
313 | | |
314 | | #define NUMCHAR "0123456789" |
315 | | #define LPAREN '(' |
316 | | #define RPAREN ')' |
317 | 0 | #define LBRACK '[' |
318 | 0 | #define RBRACK ']' |
319 | | |
320 | | /* Look at q0 and q1) to determine type */ |
321 | | static int |
322 | | gettype(const char q0, const char q1, int* isunsignedp) |
323 | 0 | { |
324 | 0 | int type = 0; |
325 | 0 | int isunsigned = 0; |
326 | 0 | char typechar; |
327 | | |
328 | 0 | isunsigned = (q0 == 'u' || q0 == 'U'); |
329 | 0 | if(q1 == '\0') |
330 | 0 | typechar = q0; /* we were given only a single char */ |
331 | 0 | else if(isunsigned) |
332 | 0 | typechar = q1; /* we have something like Ux as the tag */ |
333 | 0 | else |
334 | 0 | typechar = q1; /* look at last char for tag */ |
335 | 0 | switch (typechar) { |
336 | 0 | case 'f': case 'F': case '.': type = 'f'; break; /* float */ |
337 | 0 | case 'd': case 'D': type = 'd'; break; /* double */ |
338 | 0 | case 'b': case 'B': type = 'b'; break; /* byte */ |
339 | 0 | case 's': case 'S': type = 's'; break; /* short */ |
340 | 0 | case 'l': case 'L': type = 'l'; break; /* long long */ |
341 | 0 | case '0': case '1': case '2': case '3': case '4': |
342 | 0 | case '5': case '6': case '7': case '8': case '9': type = 'i'; break; |
343 | 0 | case 'u': case 'U': type = 'i'; isunsigned = 1; break; /* unsigned int */ |
344 | 0 | case '\0': type = 'i'; break; |
345 | 0 | default: break; |
346 | 0 | } |
347 | 0 | if(isunsignedp) *isunsignedp = isunsigned; |
348 | 0 | return type; |
349 | 0 | } |
350 | | |
351 | | #ifdef WORDS_BIGENDIAN |
352 | | /* Byte swap an 8-byte integer in place */ |
353 | | static void |
354 | | byteswap8(unsigned char* mem) |
355 | | { |
356 | | unsigned char c; |
357 | | c = mem[0]; |
358 | | mem[0] = mem[7]; |
359 | | mem[7] = c; |
360 | | c = mem[1]; |
361 | | mem[1] = mem[6]; |
362 | | mem[6] = c; |
363 | | c = mem[2]; |
364 | | mem[2] = mem[5]; |
365 | | mem[5] = c; |
366 | | c = mem[3]; |
367 | | mem[3] = mem[4]; |
368 | | mem[4] = c; |
369 | | } |
370 | | |
371 | | /* Byte swap an 8-byte integer in place */ |
372 | | static void |
373 | | byteswap4(unsigned char* mem) |
374 | | { |
375 | | unsigned char c; |
376 | | c = mem[0]; |
377 | | mem[0] = mem[3]; |
378 | | mem[3] = c; |
379 | | c = mem[1]; |
380 | | mem[1] = mem[2]; |
381 | | mem[2] = c; |
382 | | } |
383 | | #endif |
384 | | |
385 | | /**************************************************/ |
386 | | /* Moved here from netcdf_filter.h */ |
387 | | |
388 | | /* |
389 | | This function implements the 8-byte conversion algorithms for HDF5 |
390 | | Before calling *nc_def_var_filter* (unless *NC_parsefilterspec* was used), |
391 | | the client must call this function with the decode argument set to 0. |
392 | | Inside the filter code, this function should be called with the decode |
393 | | argument set to 1. |
394 | | |
395 | | * @params mem8 is a pointer to the 8-byte value either to fix. |
396 | | * @params decode is 1 if the function should apply the 8-byte decoding algorithm |
397 | | else apply the encoding algorithm. |
398 | | */ |
399 | | |
400 | | void |
401 | | ncaux_h5filterspec_fix8(unsigned char* mem8, int decode) |
402 | 0 | { |
403 | | #ifdef WORDS_BIGENDIAN |
404 | | if(decode) { /* Apply inverse of the encode case */ |
405 | | byteswap4(mem8); /* step 1: byte-swap each piece */ |
406 | | byteswap4(mem8+4); |
407 | | byteswap8(mem8); /* step 2: convert to little endian format */ |
408 | | } else { /* encode */ |
409 | | byteswap8(mem8); /* step 1: convert to little endian format */ |
410 | | byteswap4(mem8); /* step 2: byte-swap each piece */ |
411 | | byteswap4(mem8+4); |
412 | | } |
413 | | #else /* Little endian */ |
414 | | /* No action is necessary */ |
415 | 0 | #endif |
416 | 0 | } |
417 | | |
418 | | /* |
419 | | Parse a filter spec string into a NC_FILTER_SPEC* |
420 | | Note that this differs from the usual case in that the |
421 | | function is called once to get both the number of parameters |
422 | | and the parameters themselves (hence the unsigned int** paramsp). |
423 | | |
424 | | @param txt - a string containing the spec as a sequence of |
425 | | constants separated by commas, where first constant |
426 | | is the filter id and the rest are parameters. |
427 | | @param idp - store the parsed filter id here |
428 | | @param nparamsp - store the number of parameters here |
429 | | @param paramsp - store the vector of parameters here; caller frees. |
430 | | @return NC_NOERR if parse succeeded |
431 | | @return NC_EINVAL otherwise |
432 | | */ |
433 | | |
434 | | EXTERNL int |
435 | | ncaux_h5filterspec_parse(const char* txt, unsigned int* idp, size_t* nparamsp, unsigned int** paramsp) |
436 | 0 | { |
437 | 0 | int stat = NC_NOERR; |
438 | 0 | size_t i; |
439 | 0 | char* p; |
440 | 0 | char* sdata0 = NULL; /* what to free */ |
441 | 0 | char* sdata = NULL; /* sdata0 with leading prefix skipped */ |
442 | 0 | size_t nparams; /* no. of comma delimited params */ |
443 | 0 | size_t nactual; /* actual number of unsigned int's */ |
444 | 0 | const char* sid = NULL; |
445 | 0 | unsigned int filterid = 0; |
446 | 0 | unsigned int* params = NULL; |
447 | 0 | size_t len; |
448 | | |
449 | 0 | if(txt == NULL) |
450 | 0 | {stat = NC_EINVAL; goto done;} |
451 | 0 | len = strlen(txt); |
452 | 0 | if(len == 0) |
453 | 0 | {stat = NC_EINVAL; goto done;} |
454 | | |
455 | 0 | if((sdata0 = (char*)calloc(1,len+1+1))==NULL) |
456 | 0 | {stat = NC_ENOMEM; goto done;} |
457 | 0 | memcpy(sdata0,txt,len); |
458 | 0 | sdata = sdata0; |
459 | | |
460 | | /* Count number of parameters + id and delimit */ |
461 | 0 | p=sdata; |
462 | 0 | for(nparams=0;;nparams++) { |
463 | 0 | char* q = strchr(p,','); |
464 | 0 | if(q == NULL) break; |
465 | 0 | *q++ = '\0'; |
466 | 0 | p = q; |
467 | 0 | } |
468 | 0 | nparams++; /* for final piece */ |
469 | |
|
470 | 0 | if(nparams == 0) |
471 | 0 | {stat = NC_EINVAL; goto done;} /* no id and no parameters */ |
472 | | |
473 | 0 | p = sdata; |
474 | | |
475 | | /* Extract the filter id */ |
476 | 0 | sid = p; |
477 | 0 | if((sscanf(sid,"%u",&filterid)) != 1) {stat = NC_EINVAL; goto done;} |
478 | 0 | nparams--; |
479 | | |
480 | | /* skip past the filter id */ |
481 | 0 | p = p + strlen(p) + 1; |
482 | | |
483 | | /* Allocate the max needed space (assume all params are 64 bit) */ |
484 | 0 | if((params = (unsigned int*)calloc(sizeof(unsigned int),(nparams)*2))==NULL) |
485 | 0 | {stat = NC_ENOMEM; goto done;} |
486 | | |
487 | | /* walk and capture */ |
488 | 0 | for(nactual=0,i=0;i<nparams;i++) { /* step thru param strings */ |
489 | 0 | size_t count = 0; |
490 | 0 | len = strlen(p); |
491 | | /* skip leading white space */ |
492 | 0 | while(strchr(" ",*p) != NULL) {p++; len--;} |
493 | 0 | if((stat = filterspec_cvt(p,&count,params+nactual))) goto done; |
494 | 0 | nactual += count; |
495 | 0 | p = p + strlen(p) + 1; /* move to next param string */ |
496 | 0 | } |
497 | | /* Now return results */ |
498 | 0 | if(idp) *idp = filterid; |
499 | 0 | if(nparamsp) *nparamsp = nactual; |
500 | 0 | if(paramsp) {*paramsp = params; params = NULL;} |
501 | 0 | done: |
502 | 0 | nullfree(params); |
503 | 0 | nullfree(sdata0); |
504 | 0 | return stat; |
505 | 0 | } |
506 | | |
507 | | /* |
508 | | Parse a filter parameter string into a sequence of unsigned ints. |
509 | | |
510 | | @param txt - a string containing the parameter string. |
511 | | @param nuiparamsp - store the number of unsigned ints here |
512 | | @param uiparamsp - store the vector of unsigned ints here; caller frees. |
513 | | @return NC_NOERR if parse succeeded |
514 | | @return NC_EINVAL otherwise |
515 | | */ |
516 | | |
517 | | EXTERNL int |
518 | | ncaux_h5filterspec_parse_parameter(const char* txt, size_t* nuiparamsp, unsigned int* uiparams) |
519 | 0 | { |
520 | 0 | int stat = NC_NOERR; |
521 | 0 | char* p; |
522 | 0 | char* sdata0 = NULL; /* what to free */ |
523 | 0 | char* sdata = NULL; /* sdata0 with leading prefix skipped */ |
524 | 0 | size_t nuiparams = 0; |
525 | 0 | size_t len; |
526 | | |
527 | 0 | if(txt == NULL) |
528 | 0 | {stat = NC_EINVAL; goto done;} |
529 | 0 | len = strlen(txt); |
530 | 0 | if(len == 0) |
531 | 0 | {stat = NC_EINVAL; goto done;} |
532 | | |
533 | 0 | if((sdata0 = (char*)calloc(1,len+1+1))==NULL) |
534 | 0 | {stat = NC_ENOMEM; goto done;} |
535 | 0 | memcpy(sdata0,txt,len); |
536 | 0 | sdata = sdata0; |
537 | |
|
538 | 0 | p = sdata; |
539 | |
|
540 | 0 | nuiparams = 0; |
541 | 0 | len = strlen(p); |
542 | | /* skip leading white space */ |
543 | 0 | while(strchr(" ",*p) != NULL) {p++; len--;} |
544 | 0 | if((stat = filterspec_cvt(p,&nuiparams,uiparams))) goto done; |
545 | | /* Now return results */ |
546 | 0 | if(nuiparamsp) *nuiparamsp = nuiparams; |
547 | 0 | done: |
548 | 0 | nullfree(sdata0); |
549 | 0 | return stat; |
550 | 0 | } |
551 | | |
552 | | /* |
553 | | Parse a string containing multiple '|' separated filter specs. |
554 | | Use a vector of NC_Filterspec structs to return results. |
555 | | @param txt0 - a string containing the list of filter specs. |
556 | | @param formatp - store any leading format integer here |
557 | | @param nspecsp - # of parsed specs |
558 | | @param specsp - pointer to hold vector of parsed specs. Caller frees |
559 | | @return NC_NOERR if parse succeeded |
560 | | @return NC_EINVAL if bad parameters or parse failed |
561 | | */ |
562 | | |
563 | | EXTERNL int |
564 | | ncaux_h5filterspec_parselist(const char* txt0, int* formatp, size_t* nspecsp, NC_H5_Filterspec*** vectorp) |
565 | 0 | { |
566 | 0 | int stat = NC_NOERR; |
567 | 0 | int format = 0; |
568 | 0 | size_t len = 0; |
569 | 0 | size_t nspecs = 0; |
570 | 0 | NC_H5_Filterspec** vector = NULL; |
571 | 0 | char* spec = NULL; /* without prefix */ |
572 | 0 | char* p = NULL; |
573 | 0 | char* q = NULL; |
574 | |
|
575 | 0 | if(txt0 == NULL) return NC_EINVAL; |
576 | | /* Duplicate txt0 so we can modify it */ |
577 | 0 | len = strlen(txt0); |
578 | 0 | if((spec = calloc(1,len+1+1)) == NULL) {stat = NC_ENOMEM; goto done;} |
579 | 0 | memcpy(spec,txt0,len); /* Note double ending nul */ |
580 | | |
581 | | /* See if there is a prefix '[format]' tag */ |
582 | 0 | if(spec[0] == LBRACK) { |
583 | 0 | p = spec + 1; |
584 | 0 | q = strchr(p,RBRACK); |
585 | 0 | if(q == NULL) {stat = NC_EINVAL; goto done;} |
586 | 0 | *q++ = '\0'; /* delimit tag */ |
587 | 0 | if(sscanf(p,"%d",&format) != 1) {stat = NC_EINVAL; goto done;} |
588 | 0 | spec = q; /* skip tag wrt later processing */ |
589 | 0 | } |
590 | | |
591 | | /* pass 1: count number of specs */ |
592 | 0 | p = spec; |
593 | 0 | nspecs = 0; |
594 | 0 | while(*p) { |
595 | 0 | q = strchr(p,'|'); |
596 | 0 | if(q == NULL) q = p + strlen(p); /* fake it */ |
597 | 0 | nspecs++; |
598 | 0 | p = q + 1; |
599 | 0 | } |
600 | 0 | if(nspecs > 0) { |
601 | 0 | size_t count = 0; |
602 | 0 | if((vector = (NC_H5_Filterspec**)calloc(sizeof(NC_H5_Filterspec*),nspecs)) == NULL) |
603 | 0 | {stat = NC_ENOMEM; goto done;} |
604 | | /* pass 2: parse */ |
605 | 0 | p = spec; |
606 | 0 | for(count=0;count<nspecs;count++) { |
607 | 0 | NC_H5_Filterspec* spec = (NC_H5_Filterspec*)calloc(1,sizeof(NC_H5_Filterspec)); |
608 | 0 | if(spec == NULL) {stat = NC_ENOMEM; goto done;} |
609 | 0 | vector[count] = spec; |
610 | 0 | q = strchr(p,'|'); |
611 | 0 | if(q == NULL) q = p + strlen(p); /* fake it */ |
612 | 0 | *q = '\0'; |
613 | 0 | if((stat=ncaux_h5filterspec_parse(p,&spec->filterid,&spec->nparams,&spec->params))) goto done; |
614 | 0 | p = q+1; /* ok because of double nul */ |
615 | 0 | } |
616 | 0 | } |
617 | 0 | if(formatp) *formatp = format; |
618 | 0 | if(nspecsp) *nspecsp = nspecs; |
619 | 0 | if(vectorp) {*vectorp = vector; vector = NULL;} |
620 | 0 | done: |
621 | 0 | nullfree(spec); |
622 | 0 | if(vector) { |
623 | 0 | size_t i; |
624 | 0 | for(i=0;i<nspecs;i++) |
625 | 0 | ncaux_h5filterspec_free(vector[i]); |
626 | 0 | nullfree(vector); |
627 | 0 | } |
628 | 0 | return stat; |
629 | 0 | } |
630 | | |
631 | | /* |
632 | | Parse a string containing multiple '|' separated filter specs. |
633 | | Use a vector of NC_Filterspec structs to return results. |
634 | | @param txt0 - a string containing the list of filter specs. |
635 | | @param formatp - store any leading format integer here |
636 | | @param nspecsp - # of parsed specs |
637 | | @param specsp - pointer to hold vector of parsed specs. Caller frees |
638 | | @return NC_NOERR if parse succeeded |
639 | | @return NC_EINVAL if bad parameters or parse failed |
640 | | */ |
641 | | |
642 | | EXTERNL void |
643 | | ncaux_h5filterspec_free(NC_H5_Filterspec* f) |
644 | 0 | { |
645 | 0 | if(f) nullfree(f->params); |
646 | 0 | nullfree(f); |
647 | 0 | } |
648 | | |
649 | | |
650 | | /* |
651 | | Convert a parameter string to one or two unsigned ints/ |
652 | | @param txt - (in) string constant |
653 | | @param nparamsp - (out) # of unsigned ints produced |
654 | | @param params - (out) produced unsigned ints |
655 | | @return NC_NOERR if parse succeeded |
656 | | @return NC_EINVAL if bad parameters or parse failed |
657 | | */ |
658 | | |
659 | | static int |
660 | | filterspec_cvt(const char* txt, size_t* nparamsp, unsigned int* params) |
661 | 0 | { |
662 | 0 | int stat = NC_NOERR; |
663 | 0 | size_t nparams = 0; /*actual count*/ |
664 | 0 | unsigned long long val64u; |
665 | 0 | unsigned int val32u; |
666 | 0 | double vald; |
667 | 0 | float valf; |
668 | 0 | unsigned int *vector; |
669 | 0 | unsigned char mem[8]; |
670 | 0 | int isunsigned = 0; |
671 | 0 | int isnegative = 0; |
672 | 0 | int type = 0; |
673 | 0 | const char* q; |
674 | 0 | const char* p = txt; |
675 | 0 | size_t len = strlen(p); |
676 | 0 | int sstat; |
677 | | |
678 | | /* skip leading white space */ |
679 | 0 | while(strchr(" ",*p) != NULL) {p++; len--;} |
680 | | /* Get leading sign character, if any */ |
681 | 0 | if(*p == '-') isnegative = 1; |
682 | | /* Get trailing type tag characters */ |
683 | 0 | switch (len) { |
684 | 0 | case 0: stat = NC_EINVAL; goto done; /* empty parameter */ |
685 | 0 | case 1: case 2: |
686 | 0 | q = (p + len) - 1; /* point to last char */ |
687 | 0 | type = gettype(*q,'\0',&isunsigned); |
688 | 0 | break; |
689 | 0 | default: /* > 2 => we might have a two letter tag */ |
690 | 0 | q = (p + len) - 2; |
691 | 0 | type = gettype(*q,*(q+1),&isunsigned); |
692 | 0 | break; |
693 | 0 | } |
694 | | /* Now parse */ |
695 | 0 | switch (type) { |
696 | 0 | case 'b': case 's': case 'i': |
697 | | /* special case for a positive integer;for back compatibility.*/ |
698 | 0 | if(!isnegative) |
699 | 0 | sstat = sscanf(p,"%u",&val32u); |
700 | 0 | else |
701 | 0 | sstat = sscanf(p,"%d",(int*)&val32u); |
702 | 0 | if(sstat != 1) {stat = NC_EINVAL; goto done;} |
703 | 0 | switch(type) { |
704 | 0 | case 'b': val32u = (val32u & 0xFF); break; |
705 | 0 | case 's': val32u = (val32u & 0xFFFF); break; |
706 | 0 | } |
707 | 0 | params[nparams++] = val32u; |
708 | 0 | break; |
709 | 0 | case 'f': |
710 | 0 | sstat = sscanf(p,"%lf",&vald); |
711 | 0 | if(sstat != 1) {stat = NC_EINVAL; goto done;} |
712 | 0 | valf = (float)vald; |
713 | | /* avoid type punning */ |
714 | 0 | memcpy(¶ms[nparams++], &valf, sizeof(unsigned int)); |
715 | 0 | break; |
716 | | /* The following are 8-byte values, so we must swap pieces if this |
717 | | is a little endian machine */ |
718 | 0 | case 'd': |
719 | 0 | sstat = sscanf(p,"%lf",&vald); |
720 | 0 | if(sstat != 1) {stat = NC_EINVAL; goto done;}; |
721 | 0 | memcpy(mem,&vald,sizeof(mem)); |
722 | 0 | ncaux_h5filterspec_fix8(mem,0); |
723 | 0 | vector = (unsigned int*)mem; |
724 | 0 | params[nparams++] = vector[0]; |
725 | 0 | params[nparams++] = vector[1]; |
726 | 0 | break; |
727 | 0 | case 'l': /* long long */ |
728 | 0 | if(isunsigned) |
729 | 0 | sstat = sscanf(p,"%llu",&val64u); |
730 | 0 | else |
731 | 0 | sstat = sscanf(p,"%lld",(long long*)&val64u); |
732 | 0 | if(sstat != 1) {stat = NC_EINVAL; goto done;}; |
733 | 0 | memcpy(mem,&val64u,sizeof(mem)); |
734 | 0 | ncaux_h5filterspec_fix8(mem,0); |
735 | 0 | vector = (unsigned int*)&mem; |
736 | 0 | params[nparams++] = vector[0]; |
737 | 0 | params[nparams++] = vector[1]; |
738 | 0 | break; |
739 | 0 | default: |
740 | 0 | {stat = NC_EINVAL; goto done;}; |
741 | 0 | } |
742 | 0 | *nparamsp = nparams; |
743 | |
|
744 | 0 | done: |
745 | 0 | return stat; |
746 | 0 | } |
747 | | |
748 | | #if 0 |
749 | | /* |
750 | | Parse a filter spec string into a NC_H5_Filterspec* |
751 | | @param txt - a string containing the spec as a sequence of |
752 | | constants separated by commas. |
753 | | @param specp - store the parsed filter here -- caller frees |
754 | | @return NC_NOERR if parse succeeded |
755 | | @return NC_EINVAL otherwise |
756 | | */ |
757 | | |
758 | | EXTERNL int |
759 | | ncaux_filter_parsespec(const char* txt, NC_H5_Filterspec** h5specp) |
760 | | { |
761 | | int stat = NC_NOERR; |
762 | | NC_Filterspec* spec = NULL; |
763 | | NC_H5_Filterspec* h5spec = NULL; |
764 | | size_t len; |
765 | | |
766 | | if(txt == NULL) |
767 | | {stat = NC_EINVAL; goto done;} |
768 | | len = strlen(txt); |
769 | | if(len == 0) {stat = NC_EINVAL; goto done;} |
770 | | |
771 | | /* Parse as strings */ |
772 | | if((stat = ncaux_filterspec_parse(txt,&spec))) goto done; |
773 | | /* walk and convert */ |
774 | | if((stat = ncaux_filterspec_cvt(spec,&h5spec))) goto done; |
775 | | /* Now return results */ |
776 | | if(h5specp != NULL) {*h5specp = h5spec; h5spec = NULL;} |
777 | | |
778 | | done: |
779 | | ncaux_filterspec_free(spec); |
780 | | if(h5spec) nullfree(h5spec->params); |
781 | | nullfree(h5spec); |
782 | | return stat; |
783 | | } |
784 | | |
785 | | /* |
786 | | Parse a string containing multiple '|' separated filter specs. |
787 | | |
788 | | @param spec0 - a string containing the list of filter specs. |
789 | | @param nspecsp - # of parsed specs |
790 | | @param specsp - pointer to hold vector of parsed specs. Caller frees |
791 | | @return NC_NOERR if parse succeeded |
792 | | @return NC_EINVAL if bad parameters or parse failed |
793 | | */ |
794 | | |
795 | | EXTERNL int |
796 | | ncaux_filter_parselist(const char* txt0, size_t* nspecsp, NC_H5_Filterspec*** vectorp) |
797 | | { |
798 | | int stat = NC_NOERR; |
799 | | size_t len = 0; |
800 | | size_t nspecs = 0; |
801 | | NC_H5_Filterspec** vector = NULL; |
802 | | char* spec0 = NULL; /* with prefix */ |
803 | | char* spec = NULL; /* without prefix */ |
804 | | char* p = NULL; |
805 | | char* q = NULL; |
806 | | |
807 | | if(txt0 == NULL) return NC_EINVAL; |
808 | | /* Duplicate txt0 so we can modify it */ |
809 | | len = strlen(txt0); |
810 | | if((spec = calloc(1,len+1+1)) == NULL) return NC_ENOMEM; |
811 | | memcpy(spec,txt0,len); /* Note double ending nul */ |
812 | | spec0 = spec; /* Save for later free */ |
813 | | |
814 | | /* See if there is a prefix '[format]' tag; ignore it */ |
815 | | if(spec[0] == LBRACK) { |
816 | | spec = q; /* skip tag wrt later processing */ |
817 | | } |
818 | | /* pass 1: count number of specs */ |
819 | | p = spec; |
820 | | nspecs = 0; |
821 | | while(*p) { |
822 | | q = strchr(p,'|'); |
823 | | if(q == NULL) q = p + strlen(p); /* fake it */ |
824 | | nspecs++; |
825 | | p = q + 1; |
826 | | } |
827 | | if(nspecs > 0) { |
828 | | int count = 0; |
829 | | if((vector = (NC_H5_Filterspec**)malloc(sizeof(NC_H5_Filterspec*)*nspecs)) == NULL) |
830 | | {stat = NC_ENOMEM; goto done;} |
831 | | /* pass 2: parse */ |
832 | | p = spec; |
833 | | for(count=0;count<nspecs;count++) { |
834 | | NC_H5_Filterspec* aspec = NULL; |
835 | | q = strchr(p,'|'); |
836 | | if(q == NULL) q = p + strlen(p); /* fake it */ |
837 | | *q = '\0'; |
838 | | if(ncaux_filter_parsespec(p,&aspec)) |
839 | | {stat = NC_EINVAL; goto done;} |
840 | | vector[count] = aspec; aspec = NULL; |
841 | | p = q+1; /* ok because of double nul */ |
842 | | } |
843 | | } |
844 | | if(nspecsp) *nspecsp = nspecs; |
845 | | if(vectorp) *vectorp = (nspecs == 0 ? NULL : vector); |
846 | | vector = NULL; |
847 | | done: |
848 | | nullfree(spec0); |
849 | | if(vector != NULL) { |
850 | | int k; |
851 | | for(k=0;k<nspecs;k++) { |
852 | | NC_H5_Filterspec* nfs = vector[k]; |
853 | | if(nfs->params) free(nfs->params); |
854 | | nullfree(nfs); |
855 | | } |
856 | | free(vector); |
857 | | } |
858 | | return stat; |
859 | | } |
860 | | #endif |
861 | | |
862 | | /**************************************************/ |
863 | | /* Wrappers to export selected functions from libnetcdf */ |
864 | | |
865 | | EXTERNL int |
866 | | ncaux_readfile(const char* filename, size_t* sizep, void** datap) |
867 | 0 | { |
868 | 0 | int stat = NC_NOERR; |
869 | 0 | NCbytes* content = ncbytesnew(); |
870 | 0 | stat = NC_readfile(filename,content); |
871 | 0 | if(stat == NC_NOERR && sizep) |
872 | 0 | *sizep = ncbyteslength(content); |
873 | 0 | if(stat == NC_NOERR && datap) |
874 | 0 | *datap = ncbytesextract(content); |
875 | 0 | ncbytesfree(content); |
876 | 0 | return stat; |
877 | 0 | } |
878 | | |
879 | | EXTERNL int |
880 | | ncaux_writefile(const char* filename, size_t size, void* content) |
881 | 0 | { |
882 | 0 | return NC_writefile(filename,size,content); |
883 | 0 | } |
884 | | |
885 | | /**************************************************/ |
886 | | /** |
887 | | Reclaim the output tree of data from a call |
888 | | to e.g. nc_get_vara or the input to e.g. nc_put_vara. |
889 | | This recursively walks the top-level instances to |
890 | | reclaim any nested data such as vlen or strings or such. |
891 | | |
892 | | This function is just a wrapper around nc_reclaim_data. |
893 | | |
894 | | @param ncid file ncid |
895 | | @param xtype type id |
896 | | @param memory to reclaim |
897 | | @param count number of instances of the type in memory |
898 | | @return error code |
899 | | */ |
900 | | |
901 | | EXTERNL int |
902 | | ncaux_reclaim_data(int ncid, int xtype, void* memory, size_t count) |
903 | 0 | { |
904 | | /* Defer to the internal version */ |
905 | 0 | return nc_reclaim_data(ncid, xtype, memory, count); |
906 | 0 | } |
907 | | |
908 | | /* |
909 | | This function is just a wrapper around nc_reclaim_data_all. |
910 | | @param ncid file ncid |
911 | | @param xtype type id |
912 | | @param memory to reclaim |
913 | | @param count number of instances of the type in memory |
914 | | @return error code |
915 | | */ |
916 | | |
917 | | EXTERNL int |
918 | | ncaux_reclaim_data_all(int ncid, int xtype, void* memory, size_t count) |
919 | 0 | { |
920 | | /* Defer to the internal version */ |
921 | 0 | return nc_reclaim_data_all(ncid, xtype, memory, count); |
922 | 0 | } |
923 | | |
924 | | EXTERNL int NC_inq_any_type(int ncid, nc_type typeid, char *name, size_t *size, nc_type *basetypep, size_t *nfieldsp, int *classp); |
925 | | |
926 | | EXTERNL int |
927 | | ncaux_inq_any_type(int ncid, nc_type typeid, char *name, size_t *sizep, nc_type *basetypep, size_t *nfieldsp, int *classp) |
928 | 0 | { |
929 | 0 | return NC_inq_any_type(ncid, typeid, name, sizep, basetypep, nfieldsp, classp); |
930 | 0 | } |
931 | | |
932 | | #ifdef USE_NETCDF4 |
933 | | /** |
934 | | @param ncid - only needed for a compound type |
935 | | @param xtype - type for which alignment is requested |
936 | | */ |
937 | | int |
938 | | ncaux_type_alignment(int xtype, int ncid, size_t* alignp) |
939 | 0 | { |
940 | | /* Defer to the internal version */ |
941 | 0 | return NC_type_alignment(ncid, xtype, alignp); |
942 | 0 | } |
943 | | #endif |
944 | | |
945 | | /** |
946 | | Dump the output tree of data from a call |
947 | | to e.g. nc_get_vara or the input to e.g. nc_put_vara. |
948 | | This function is just a wrapper around nc_dump__data. |
949 | | |
950 | | @param ncid file ncid |
951 | | @param xtype type id |
952 | | @param memory to print |
953 | | @param count number of instances of the type in memory |
954 | | @return error code |
955 | | */ |
956 | | |
957 | | EXTERNL int nc_dump_data(int ncid, nc_type xtype, void* memory, size_t count, char** bufp); |
958 | | |
959 | | EXTERNL int |
960 | | ncaux_dump_data(int ncid, int xtype, void* memory, size_t count, char** bufp) |
961 | 0 | { |
962 | 0 | return nc_dump_data(ncid, xtype, memory, count, bufp); |
963 | 0 | } |
964 | | |
965 | | /**************************************************/ |
966 | | /* Path List Utilities */ |
967 | | |
968 | | /* Path-list Parser: |
969 | | @param pathlen length of the pathlist0 arg |
970 | | @param pathlist0 the string to parse |
971 | | @param dirs return the parsed directories -- see note below. |
972 | | @param sep the separator parsing: one of ';' | ':' | '\0', where zero means use platform default. |
973 | | @return NC_NOERR || NC_EXXX |
974 | | |
975 | | Note: If dirs->dirs is not NULL, then this function |
976 | | will allocate the space for the vector of directory path. |
977 | | The user is then responsible for free'ing that vector |
978 | | (or call ncaux_plugin_path_reclaim). |
979 | | */ |
980 | | EXTERNL int |
981 | | ncaux_plugin_path_parsen(size_t pathlen, const char* pathlist0, char sep, NCPluginList* dirs) |
982 | 1 | { |
983 | 1 | int stat = NC_NOERR; |
984 | 1 | size_t i; |
985 | 1 | char* path = NULL; |
986 | 1 | char* p; |
987 | 1 | size_t count; |
988 | 1 | char seps[2] = "\0\0"; /* will contain all allowable separators */ |
989 | | |
990 | 1 | if(dirs == NULL) {stat = NC_EINVAL; goto done;} |
991 | | |
992 | 1 | if(pathlen == 0 || pathlist0 == NULL) {dirs->ndirs = 0; goto done;} |
993 | | |
994 | | /* If a separator is specified, use it, otherwise search for ';' or ':' */ |
995 | 1 | seps[0] = sep; |
996 | 1 | if(sep == 0) { |
997 | 1 | if(NCgetlocalpathkind() == NCPD_WIN |
998 | 1 | || NCgetlocalpathkind() == NCPD_MSYS) |
999 | 0 | seps[0] = ';'; |
1000 | 1 | else |
1001 | 1 | seps[0] = ':'; |
1002 | 1 | } |
1003 | 1 | if((path = malloc(pathlen+1+1))==NULL) {stat = NC_ENOMEM; goto done;} |
1004 | 1 | memcpy(path,pathlist0,pathlen); |
1005 | 1 | path[pathlen] = '\0'; path[pathlen+1] = '\0'; /* double null term */ |
1006 | | |
1007 | 54 | for(count=0,p=path;*p;p++) { |
1008 | 53 | if(strchr(seps,*p) == NULL) |
1009 | 52 | continue; /* non-separator */ |
1010 | 1 | else { |
1011 | 1 | *p = '\0'; |
1012 | 1 | count++; |
1013 | 1 | } |
1014 | 53 | } |
1015 | 1 | count++; /* count last piece */ |
1016 | | |
1017 | | /* Save and allocate */ |
1018 | 1 | dirs->ndirs = count; |
1019 | 1 | if(dirs->dirs == NULL) { |
1020 | 1 | if((dirs->dirs = (char**)calloc(count,sizeof(char*)))==NULL) |
1021 | 0 | {stat = NC_ENOMEM; goto done;} |
1022 | 1 | } |
1023 | | |
1024 | | /* capture the parsed pieces */ |
1025 | 3 | for(p=path,i=0;i<count;i++) { |
1026 | 2 | size_t len = strlen(p); |
1027 | 2 | dirs->dirs[i] = strdup(p); |
1028 | 2 | p = p+len+1; /* point to next piece */ |
1029 | 2 | } |
1030 | | |
1031 | 1 | done: |
1032 | 1 | nullfree(path); |
1033 | 1 | return stat; |
1034 | 1 | } |
1035 | | |
1036 | | /* Wrapper around ncaux_plugin_path_parsen |
1037 | | to allow passing a nul-terminated string to parse. |
1038 | | @param pathlist0 the nul-termiated string to parse |
1039 | | @param dirs return the parsed directories -- see note below. |
1040 | | @param sep the separator parsing: one of ';' | ':' | '\0', where zero means use platform default. |
1041 | | @return NC_NOERR || NC_EXXX |
1042 | | See also the comments for ncaux_plugin_path_parsen. |
1043 | | */ |
1044 | | EXTERNL int |
1045 | | ncaux_plugin_path_parse(const char* pathlist0, char sep, NCPluginList* dirs) |
1046 | 1 | { |
1047 | 1 | return ncaux_plugin_path_parsen(nulllen(pathlist0),pathlist0,sep,dirs); |
1048 | 1 | } |
1049 | | |
1050 | | /* |
1051 | | Path-list concatenator where given a vector of directories, |
1052 | | concatenate all dirs with specified separator. |
1053 | | If the separator is 0, then use the default platform separator. |
1054 | | |
1055 | | @param dirs the counted directory vector to concatenate |
1056 | | @param sep one of ';', ':', or '\0' |
1057 | | @param catp return the concatenation; WARNING: caller frees. |
1058 | | @return ::NC_NOERR |
1059 | | @return ::NC_EINVAL for illegal arguments |
1060 | | */ |
1061 | | EXTERNL int |
1062 | | ncaux_plugin_path_tostring(const NCPluginList* dirs, char sep, char** catp) |
1063 | 0 | { |
1064 | 0 | int stat = NC_NOERR; |
1065 | 0 | NCbytes* buf = ncbytesnew(); |
1066 | 0 | size_t i; |
1067 | |
|
1068 | 0 | if(dirs == NULL) {stat = NC_EINVAL; goto done;} |
1069 | 0 | if(dirs->ndirs > 0 && dirs->dirs == NULL) {stat = NC_EINVAL; goto done;} |
1070 | | |
1071 | 0 | if(sep == '\0') |
1072 | | #ifdef _WIN32 |
1073 | | sep = ';'; |
1074 | | #else |
1075 | 0 | sep = ':'; |
1076 | 0 | #endif |
1077 | 0 | if(dirs->ndirs > 0) { |
1078 | 0 | for(i=0;i<dirs->ndirs;i++) { |
1079 | 0 | if(i>0) ncbytesappend(buf,sep); |
1080 | 0 | if(dirs->dirs[i] != NULL) ncbytescat(buf,dirs->dirs[i]); |
1081 | 0 | } |
1082 | 0 | } |
1083 | 0 | ncbytesnull(buf); |
1084 | 0 | if(catp) *catp = ncbytesextract(buf); |
1085 | 0 | done: |
1086 | 0 | ncbytesfree(buf); |
1087 | 0 | return stat; |
1088 | 0 | } |
1089 | | |
1090 | | /* |
1091 | | Clear an NCPluginList object possibly produced by ncaux_plugin_parse function. |
1092 | | @param dirs the object to clear |
1093 | | @return ::NC_NOERR |
1094 | | @return ::NC_EINVAL for illegal arguments |
1095 | | */ |
1096 | | EXTERNL int |
1097 | | ncaux_plugin_path_clear(NCPluginList* dirs) |
1098 | 1 | { |
1099 | 1 | int stat = NC_NOERR; |
1100 | 1 | size_t i; |
1101 | 1 | if(dirs == NULL || dirs->ndirs == 0 || dirs->dirs == NULL) goto done; |
1102 | 3 | for(i=0;i<dirs->ndirs;i++) { |
1103 | 2 | if(dirs->dirs[i] != NULL) free(dirs->dirs[i]); |
1104 | 2 | dirs->dirs[i] = NULL; |
1105 | 2 | } |
1106 | 1 | free(dirs->dirs); |
1107 | 1 | dirs->dirs = NULL; |
1108 | 1 | dirs->ndirs = 0; |
1109 | 1 | done: |
1110 | 1 | return stat; |
1111 | 1 | } |
1112 | | |
1113 | | /* |
1114 | | Reclaim an NCPluginList object. |
1115 | | @param dir the object to reclaim |
1116 | | @return ::NC_NOERR |
1117 | | @return ::NC_EINVAL for illegal arguments |
1118 | | */ |
1119 | | EXTERNL int |
1120 | | ncaux_plugin_path_reclaim(NCPluginList* dirs) |
1121 | 0 | { |
1122 | 0 | int stat = NC_NOERR; |
1123 | 0 | if((stat = ncaux_plugin_path_clear(dirs))) goto done; |
1124 | 0 | nullfree(dirs); |
1125 | 0 | done: |
1126 | 0 | return stat; |
1127 | 0 | } |
1128 | | |
1129 | | /* |
1130 | | Modify a plugin path set to append a new directory to the end. |
1131 | | @param dirs a pointer to an NCPluginPath object giving the number and vector of directories to which 'dir' argument is appended. |
1132 | | @return ::NC_NOERR |
1133 | | @return ::NC_EINVAL for illegal arguments |
1134 | | |
1135 | | WARNING: dirs->dirs may be reallocated. |
1136 | | |
1137 | | Author: Dennis Heimbigner |
1138 | | */ |
1139 | | |
1140 | | EXTERNL int |
1141 | | ncaux_plugin_path_append(NCPluginList* dirs, const char* dir) |
1142 | 0 | { |
1143 | 0 | int stat = NC_NOERR; |
1144 | 0 | char** newdirs = NULL; |
1145 | 0 | char** olddirs = NULL; |
1146 | 0 | if(dirs == NULL || dir == NULL) {stat = NC_EINVAL; goto done;} |
1147 | 0 | olddirs = dirs->dirs; dirs->dirs = NULL; |
1148 | 0 | if((newdirs = (char**)calloc(dirs->ndirs+1,sizeof(char*)))==NULL) |
1149 | 0 | {stat = NC_ENOMEM; goto done;} |
1150 | 0 | if(dirs->ndirs > 0) |
1151 | 0 | memcpy(newdirs,olddirs,sizeof(char*)*dirs->ndirs); |
1152 | 0 | nullfree(olddirs); |
1153 | 0 | dirs->dirs = newdirs; newdirs = NULL; |
1154 | 0 | dirs->dirs[dirs->ndirs] = nulldup(dir); |
1155 | 0 | dirs->ndirs++; |
1156 | 0 | done: |
1157 | 0 | return stat; |
1158 | 0 | } |
1159 | | |
1160 | | /* |
1161 | | Modify a plugin path set to prepend a new directory to the front. |
1162 | | @param dirs a pointer to an NCPluginList object giving the number and vector of directories to which 'dir' argument is appended. |
1163 | | @return ::NC_NOERR |
1164 | | @return ::NC_EINVAL for illegal arguments |
1165 | | |
1166 | | WARNING: dirs->dirs may be reallocated. |
1167 | | |
1168 | | Author: Dennis Heimbigner |
1169 | | */ |
1170 | | EXTERNL int |
1171 | | ncaux_plugin_path_prepend(struct NCPluginList* dirs, const char* dir) |
1172 | 0 | { |
1173 | 0 | int stat = NC_NOERR; |
1174 | 0 | char** newdirs = NULL; |
1175 | 0 | char** olddirs = NULL; |
1176 | 0 | if(dirs == NULL || dir == NULL) {stat = NC_EINVAL; goto done;} |
1177 | 0 | olddirs = dirs->dirs; dirs->dirs = NULL; |
1178 | 0 | if((newdirs = (char**)calloc(dirs->ndirs+1,sizeof(char*)))==NULL) |
1179 | 0 | {stat = NC_ENOMEM; goto done;} |
1180 | 0 | if(dirs->ndirs > 0) |
1181 | 0 | memcpy(&newdirs[1],olddirs,sizeof(char*)*dirs->ndirs); |
1182 | 0 | nullfree(olddirs); |
1183 | 0 | dirs->dirs = newdirs; newdirs = NULL; |
1184 | 0 | dirs->dirs[0] = nulldup(dir); |
1185 | 0 | dirs->ndirs++; |
1186 | 0 | done: |
1187 | 0 | return stat; |
1188 | 0 | } |
1189 | | |
1190 | | /* FORTRAN is not good at manipulating C char** vectors, |
1191 | | so provide some wrappers for use by netcdf-fortran |
1192 | | that read/write plugin path as a single string. |
1193 | | For simplicity, the path separator is always semi-colon. |
1194 | | */ |
1195 | | |
1196 | | /** |
1197 | | * Return the length (as in strlen) of the current plugin path directories encoded as a string. |
1198 | | * @return length of the string encoded plugin path or -1 if failed. |
1199 | | * @author Dennis Heimbigner |
1200 | | * |
1201 | | * @author: Dennis Heimbigner |
1202 | | */ |
1203 | | int |
1204 | | ncaux_plugin_path_stringlen(void) |
1205 | 0 | { |
1206 | 0 | int len = 0; |
1207 | 0 | int stat = NC_NOERR; |
1208 | 0 | struct NCPluginList npl = {0,NULL}; |
1209 | 0 | char* buf = NULL; |
1210 | | |
1211 | | /* Get the list of dirs */ |
1212 | 0 | if((stat = nc_plugin_path_get(&npl))) goto done; |
1213 | | /* Convert to a string path separated by ';' */ |
1214 | 0 | if((stat = ncaux_plugin_path_tostring(&npl,';',&buf))) goto done; |
1215 | 0 | len = (int)nulllen(buf); |
1216 | |
|
1217 | 0 | done: |
1218 | 0 | if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} |
1219 | 0 | nullfree(buf); |
1220 | 0 | if(stat) return -1; else return len; |
1221 | 0 | } |
1222 | | |
1223 | | /** |
1224 | | * Return the current sequence of directories in the internal global |
1225 | | * plugin path list encoded as a string path using ';' as a path separator. |
1226 | | * @param pathlen the length of the path argument. |
1227 | | * @param path a string into which the current plugin paths are encodeded. |
1228 | | * @return NC_NOERR | NC_EXXX |
1229 | | * @author Dennis Heimbigner |
1230 | | * |
1231 | | * @author: Dennis Heimbigner |
1232 | | */ |
1233 | | int |
1234 | | ncaux_plugin_path_stringget(int pathlen, char* path) |
1235 | 0 | { |
1236 | 0 | int stat = NC_NOERR; |
1237 | 0 | struct NCPluginList npl = {0,NULL}; |
1238 | 0 | char* buf = NULL; |
1239 | |
|
1240 | 0 | if(pathlen == 0 || path == NULL) {stat = NC_EINVAL; goto done;} |
1241 | | |
1242 | | /* Get the list of dirs */ |
1243 | 0 | if((stat = nc_plugin_path_get(&npl))) goto done; |
1244 | | /* Convert to a string path separated by ';' */ |
1245 | 0 | if((stat = ncaux_plugin_path_tostring(&npl,';',&buf))) goto done; |
1246 | 0 | strncpy(path,buf,(size_t)pathlen); |
1247 | |
|
1248 | 0 | done: |
1249 | 0 | nullfree(buf); |
1250 | 0 | if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} |
1251 | 0 | return stat; |
1252 | 0 | } |
1253 | | |
1254 | | /** |
1255 | | * Set the current sequence of directories in the internal global |
1256 | | * plugin path list to the sequence of directories encoded as a |
1257 | | * string path using ';' as a path separator. |
1258 | | * @param pathlen the length of the path argument. |
1259 | | * @param path a string encoding the sequence of directories and using ';' to separate them. |
1260 | | * @return NC_NOERR | NC_EXXX |
1261 | | * @author Dennis Heimbigner |
1262 | | * |
1263 | | * @author: Dennis Heimbigner |
1264 | | */ |
1265 | | int |
1266 | | ncaux_plugin_path_stringset(int pathlen, const char* path) |
1267 | 0 | { |
1268 | 0 | int stat = NC_NOERR; |
1269 | 0 | struct NCPluginList npl = {0,NULL}; |
1270 | |
|
1271 | 0 | if(pathlen == 0 || path == NULL) {stat = NC_EINVAL; goto done;} |
1272 | | /* Parse the incoming path */ |
1273 | 0 | if((stat = ncaux_plugin_path_parsen((size_t)pathlen,path,';',&npl))) goto done; |
1274 | | /* set the list of dirs */ |
1275 | 0 | if((stat = nc_plugin_path_set(&npl))) goto done; |
1276 | | |
1277 | 0 | done: |
1278 | 0 | if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} |
1279 | 0 | return stat; |
1280 | 0 | } |
1281 | | |
1282 | | /**************************************************/ |
1283 | | |
1284 | | /* De-escape a string */ |
1285 | | static char* |
1286 | | deescape(const char* s) |
1287 | 0 | { |
1288 | 0 | char* des = strdup(s); |
1289 | 0 | char* p = NULL; |
1290 | 0 | char* q = NULL; |
1291 | 0 | if(s == NULL) return NULL; |
1292 | 0 | for(p=des,q=des;*p;) { |
1293 | 0 | switch (*p) { |
1294 | 0 | case '\\': |
1295 | 0 | p++; |
1296 | 0 | if(*p == '\0') {*q++ = '\\';} break; /* edge case */ |
1297 | | /* fall thru */ |
1298 | 0 | default: |
1299 | 0 | *q++ = *p++; |
1300 | 0 | break; |
1301 | 0 | } |
1302 | 0 | } |
1303 | 0 | *q = '\0'; |
1304 | 0 | return des; |
1305 | 0 | } |
1306 | | |
1307 | | /** |
1308 | | * @internal |
1309 | | * |
1310 | | * Construct the parsed provenance information |
1311 | | * Provide a parser for _NCProperties attribute. |
1312 | | * @param ncprop the contents of the _NCProperties attribute. |
1313 | | * @param pairsp allocate and return a pointer to a NULL terminated vector of (key,value) pairs. |
1314 | | * @return NC_NOERR | NC_EXXX |
1315 | | */ |
1316 | | int |
1317 | | ncaux_parse_provenance(const char* ncprop0, char*** pairsp) |
1318 | 0 | { |
1319 | 0 | int stat = NC_NOERR; |
1320 | 0 | NClist* pairs = NULL; |
1321 | 0 | char* ncprop = NULL; |
1322 | 0 | size_t ncproplen = 0; |
1323 | 0 | char* thispair = NULL; |
1324 | 0 | char* p = NULL; |
1325 | 0 | int i,count = 0; |
1326 | 0 | int endinner; |
1327 | | |
1328 | 0 | if(pairsp == NULL) goto done; |
1329 | 0 | *pairsp = NULL; |
1330 | 0 | ncproplen = nulllen(ncprop0); |
1331 | |
|
1332 | 0 | if(ncproplen == 0) goto done; |
1333 | | |
1334 | 0 | ncprop = (char*)malloc(ncproplen+1+1); /* double nul term */ |
1335 | 0 | strcpy(ncprop,ncprop0); /* Make modifiable copy */ |
1336 | 0 | ncprop[ncproplen] = '\0'; /* double nul term */ |
1337 | 0 | ncprop[ncproplen+1] = '\0'; /* double nul term */ |
1338 | 0 | pairs = nclistnew(); |
1339 | | |
1340 | | /* delimit the key,value pairs */ |
1341 | 0 | thispair = ncprop; |
1342 | 0 | count = 0; |
1343 | 0 | p = thispair; |
1344 | 0 | endinner = 0; |
1345 | 0 | do { |
1346 | 0 | switch (*p) { |
1347 | 0 | case '\0': |
1348 | 0 | if(strlen(thispair)==0) {stat = NC_EINVAL; goto done;} /* Has to be a non-null key */ |
1349 | 0 | endinner = 1; /* terminate loop */ |
1350 | 0 | break; |
1351 | 0 | case ',': case '|': /* '|' is version one pair separator */ |
1352 | 0 | *p++ = '\0'; /* terminate this pair */ |
1353 | 0 | if(strlen(thispair)==0) {stat = NC_EINVAL; goto done;} /* Has to be a non-null key */ |
1354 | 0 | thispair = p; |
1355 | 0 | count++; |
1356 | 0 | break; |
1357 | 0 | case '\\': |
1358 | 0 | p++; /* skip the escape and escaped char */ |
1359 | | /* fall thru */ |
1360 | 0 | default: |
1361 | 0 | p++; |
1362 | 0 | break; |
1363 | 0 | } |
1364 | 0 | } while(!endinner); |
1365 | 0 | count++; |
1366 | | /* Split and store the pairs */ |
1367 | 0 | thispair = ncprop; |
1368 | 0 | for(i=0;i<count;i++) { |
1369 | 0 | char* key = thispair; |
1370 | 0 | char* value = NULL; |
1371 | 0 | char* nextpair = (thispair + strlen(thispair) + 1); |
1372 | | /* Find the '=' separator for each pair */ |
1373 | 0 | p = thispair; |
1374 | 0 | endinner = 0; |
1375 | 0 | do { |
1376 | 0 | switch (*p) { |
1377 | 0 | case '\0': /* Key has no value */ |
1378 | 0 | value = p; |
1379 | 0 | endinner = 1; /* => leave loop */ |
1380 | 0 | break; |
1381 | 0 | case '=': |
1382 | 0 | *p++ = '\0'; /* split this pair */ |
1383 | 0 | value = p; |
1384 | 0 | endinner = 1; |
1385 | 0 | break; |
1386 | 0 | case '\\': |
1387 | 0 | p++; /* skip the escape + escaped char */ |
1388 | | /* fall thru */ |
1389 | 0 | default: |
1390 | 0 | p++; |
1391 | 0 | break; |
1392 | 0 | } |
1393 | 0 | } while(!endinner); |
1394 | | /* setup next iteration */ |
1395 | 0 | nclistpush(pairs,deescape(key)); |
1396 | 0 | nclistpush(pairs,deescape(value)); |
1397 | 0 | thispair = nextpair; |
1398 | 0 | } |
1399 | | /* terminate the list with (NULL,NULL) key value pair*/ |
1400 | 0 | nclistpush(pairs,NULL); nclistpush(pairs,NULL); |
1401 | 0 | *pairsp = (char**)nclistextract(pairs); |
1402 | 0 | done: |
1403 | | nullfree(ncprop); |
1404 | 0 | nclistfreeall(pairs); |
1405 | 0 | return stat; |
1406 | 0 | } |