/src/netcdf-c/libnczarr/zprov.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * @internal Add provenance info for netcdf-4 files. |
4 | | * |
5 | | * Copyright 2018, UCAR/Unidata See netcdf/COPYRIGHT file for copying |
6 | | * and redistribution conditions. |
7 | | * @author Dennis Heimbigner |
8 | | */ |
9 | | |
10 | | #include "zincludes.h" |
11 | | #include "nc_provenance.h" |
12 | | |
13 | | /* Provide a hack to suppress the writing of _NCProperties attribute. |
14 | | This is for creating a file without _NCProperties for testing purposes. |
15 | | */ |
16 | | #undef SUPPRESSNCPROPS |
17 | | |
18 | | /* Various Constants */ |
19 | | #define NCPROPS_MAX_NAME 1024 /* max key name size */ |
20 | | #define NCPROPS_MAX_VALUE 1024 /* max value size */ |
21 | | #define NCZ_MAX_NAME 1024 /**< ZARR max name. */ |
22 | | |
23 | | #define ESCAPECHARS "\\=|," |
24 | | |
25 | 1 | #define NCPNCZLIB "nczarr" |
26 | | |
27 | | /** @internal Check NetCDF return code. */ |
28 | | #define NCHECK(expr) {if((expr)!=NC_NOERR) {goto done;}} |
29 | | |
30 | | /** @internal Check ZARR return code. */ |
31 | | #define HCHECK(expr) {if((expr)<0) {ncstat = NC_EHDFERR; goto done;}} |
32 | | |
33 | | static int NCZ_read_ncproperties(NC_FILE_INFO_T* h5, const char* value, char** propstring); |
34 | | static int NCZ_write_ncproperties(NC_FILE_INFO_T* h5); |
35 | | |
36 | | static int globalpropinitialized = 0; |
37 | | static NC4_Provenance globalprovenance; |
38 | | |
39 | | /** |
40 | | * @internal Initialize default provenance info |
41 | | * This will only be used for newly created files |
42 | | * or for opened files that do not contain an _NCProperties |
43 | | * attribute. |
44 | | * |
45 | | * @return ::NC_NOERR No error. |
46 | | * @author Dennis Heimbigner |
47 | | */ |
48 | | int |
49 | | NCZ_provenance_init(void) |
50 | 1 | { |
51 | 1 | int stat = NC_NOERR; |
52 | 1 | char* name = NULL; |
53 | 1 | char* value = NULL; |
54 | 1 | unsigned long major, minor, release; |
55 | 1 | NCbytes* buffer = NULL; /* for constructing the global _NCProperties */ |
56 | 1 | char printbuf[1024]; |
57 | | |
58 | 1 | if(globalpropinitialized) |
59 | 0 | return stat; |
60 | | |
61 | | /* Build _NCProperties info */ |
62 | | |
63 | | /* Initialize globalpropinfo */ |
64 | 1 | memset((void*)&globalprovenance,0,sizeof(NC4_Provenance)); |
65 | 1 | globalprovenance.version = NCPROPS_VERSION; |
66 | | |
67 | 1 | buffer = ncbytesnew(); |
68 | | |
69 | | /* Insert version as first entry */ |
70 | 1 | ncbytescat(buffer,NCPVERSION); |
71 | 1 | ncbytescat(buffer,"="); |
72 | | |
73 | 1 | snprintf(printbuf,sizeof(printbuf),"%d",globalprovenance.version); |
74 | 1 | ncbytescat(buffer,printbuf); |
75 | | |
76 | | /* Insert the netcdf version */ |
77 | 1 | ncbytesappend(buffer,NCPROPSSEP2); |
78 | 1 | ncbytescat(buffer,NCPNCLIB2); |
79 | 1 | ncbytescat(buffer,"="); |
80 | 1 | ncbytescat(buffer,PACKAGE_VERSION); |
81 | | |
82 | | /* This should be redundant since netcdf version => zarr format */ |
83 | | /* Insert the ZARR as underlying storage format library */ |
84 | 1 | ncbytesappend(buffer,NCPROPSSEP2); |
85 | 1 | ncbytescat(buffer,NCPNCZLIB); |
86 | 1 | ncbytescat(buffer,"="); |
87 | 1 | if((stat = NCZ_get_libversion(&major,&minor,&release))) return stat; |
88 | 1 | snprintf(printbuf,sizeof(printbuf),"%lu.%lu.%lu",major,minor,release); |
89 | 1 | ncbytescat(buffer,printbuf); |
90 | | |
91 | | #ifdef NCPROPERTIES_EXTRA |
92 | | if(NCPROPERTIES_EXTRA != NULL && strlen(NCPROPERTIES_EXTRA) > 0) |
93 | | { |
94 | | /* Add any extra fields */ |
95 | | const char* p = NCPROPERTIES_EXTRA; |
96 | | if(p != NULL && strlen(p) > 0) { |
97 | | if(p[0] == NCPROPSSEP2) p++; /* If leading separator */ |
98 | | ncbytesappend(buffer,NCPROPSSEP2); |
99 | | ncbytescat(buffer,p); |
100 | | } |
101 | | } |
102 | | #endif |
103 | 1 | ncbytesnull(buffer); |
104 | 1 | globalprovenance.ncproperties = ncbytesextract(buffer); |
105 | | |
106 | 1 | ncbytesfree(buffer); |
107 | 1 | if(name != NULL) free(name); |
108 | 1 | if(value != NULL) free(value); |
109 | 1 | if(stat == NC_NOERR) |
110 | 1 | globalpropinitialized = 1; /* avoid repeating it */ |
111 | 1 | return stat; |
112 | 1 | } |
113 | | |
114 | | /** |
115 | | * @internal finalize default provenance info |
116 | | * |
117 | | * @return ::NC_NOERR No error. |
118 | | * @author Dennis Heimbigner |
119 | | */ |
120 | | int |
121 | | NCZ_provenance_finalize(void) |
122 | 1 | { |
123 | 1 | return NCZ_clear_provenance(&globalprovenance); |
124 | 1 | } |
125 | | |
126 | | /** |
127 | | * @internal |
128 | | * |
129 | | * Construct the provenance information for a newly created file. |
130 | | * Note that creation of the _NCProperties attribute is deferred |
131 | | * to the sync_netcdf4_file function. |
132 | | * |
133 | | * @param file Pointer to file object. |
134 | | * |
135 | | * @return ::NC_NOERR No error. |
136 | | * [Note: other errors are reported via LOG()] |
137 | | * @author Dennis Heimbigner |
138 | | */ |
139 | | int |
140 | | NCZ_new_provenance(NC_FILE_INFO_T* file) |
141 | 0 | { |
142 | 0 | int stat = NC_NOERR; |
143 | 0 | NC4_Provenance* provenance = NULL; |
144 | 0 | int superblock; |
145 | |
|
146 | 0 | LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id)); |
147 | |
|
148 | 0 | assert(file->provenance.ncproperties == NULL); /* not yet defined */ |
149 | | |
150 | 0 | provenance = &file->provenance; |
151 | 0 | memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */ |
152 | | |
153 | | /* Set the version */ |
154 | 0 | provenance->version = globalprovenance.version; |
155 | | |
156 | | /* Set the superblock number */ |
157 | 0 | if((stat = NCZ_get_superblock(file,&superblock))) goto done; |
158 | 0 | provenance->superblockversion = superblock; |
159 | |
|
160 | 0 | if(globalprovenance.ncproperties != NULL) { |
161 | 0 | if((provenance->ncproperties = strdup(globalprovenance.ncproperties)) == NULL) |
162 | 0 | {stat = NC_ENOMEM; goto done;} |
163 | 0 | } |
164 | | |
165 | 0 | done: |
166 | 0 | if(stat) { |
167 | 0 | LOG((0,"Could not create _NCProperties attribute")); |
168 | 0 | } |
169 | 0 | return NC_NOERR; |
170 | 0 | } |
171 | | |
172 | | /** |
173 | | * @internal |
174 | | * |
175 | | * Construct the provenance information for an existing file. |
176 | | * |
177 | | * @param file Pointer to file object. |
178 | | * |
179 | | * @return ::NC_NOERR No error. |
180 | | * [Note: other errors are reported via LOG()] |
181 | | * @author Dennis Heimbigner |
182 | | */ |
183 | | int |
184 | | NCZ_read_provenance(NC_FILE_INFO_T* file, const char* name, const char* value) |
185 | 0 | { |
186 | 0 | int stat = NC_NOERR; |
187 | 0 | NC4_Provenance* provenance = NULL; |
188 | 0 | char* propstring = NULL; |
189 | |
|
190 | 0 | LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id)); |
191 | |
|
192 | 0 | assert(file->provenance.version == 0); /* not yet defined */ |
193 | | |
194 | 0 | provenance = &file->provenance; |
195 | 0 | memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */ |
196 | | |
197 | | /* Set the superblock number */ |
198 | 0 | int superblock = -1; |
199 | 0 | if((stat = NCZ_get_superblock(file,&superblock))) goto done; |
200 | 0 | provenance->superblockversion = superblock; |
201 | | |
202 | | /* Process the _NCProperties value */ |
203 | 0 | if(strcmp(name,NCPROPS)==0) { |
204 | 0 | if((stat = NCZ_read_ncproperties(file,value,&propstring))) goto done; |
205 | 0 | provenance->ncproperties = propstring; |
206 | 0 | propstring = NULL; |
207 | 0 | } |
208 | | |
209 | 0 | done: |
210 | 0 | nullfree(propstring); |
211 | 0 | if(stat) { |
212 | 0 | LOG((0,"Could not create _NCProperties attribute")); |
213 | 0 | } |
214 | 0 | return NC_NOERR; |
215 | 0 | } |
216 | | |
217 | | /** |
218 | | * @internal |
219 | | * |
220 | | * Add the provenance information to a newly created file. |
221 | | * |
222 | | * @param file Pointer to file object. |
223 | | * |
224 | | * @return ::NC_NOERR No error. |
225 | | * [Note: other errors are reported via LOG()] |
226 | | * @author Dennis Heimbigner |
227 | | */ |
228 | | int |
229 | | NCZ_write_provenance(NC_FILE_INFO_T* file) |
230 | 0 | { |
231 | 0 | int stat = NC_NOERR; |
232 | 0 | if((stat = NCZ_write_ncproperties(file))) |
233 | 0 | goto done; |
234 | 0 | done: |
235 | 0 | return stat; |
236 | 0 | } |
237 | | |
238 | | /* ZARR Specific attribute read/write of _NCProperties */ |
239 | | static int |
240 | | NCZ_read_ncproperties(NC_FILE_INFO_T* h5, const char* value, char** propstring) |
241 | 0 | { |
242 | 0 | int stat = NC_NOERR; |
243 | 0 | char* text = NULL; |
244 | 0 | size_t len; |
245 | |
|
246 | 0 | LOG((5, "%s", __func__)); |
247 | | |
248 | | /* NCPROPS Attribute exists, make sure it is legitimate */ |
249 | 0 | if(value == NULL || strlen(value) == 0) |
250 | 0 | {stat = NC_EINVAL; goto done;} |
251 | 0 | len = strlen(value); |
252 | 0 | text = (char*)malloc(1+len); |
253 | 0 | if(text == NULL) {stat = NC_ENOMEM; goto done;} |
254 | 0 | memcpy(text,value,len); |
255 | | /* Make sure its null terminated */ |
256 | 0 | text[len] = '\0'; |
257 | 0 | if(propstring) {*propstring = text; text = NULL;} |
258 | |
|
259 | 0 | done: |
260 | 0 | if(text != NULL) free(text); |
261 | | /* For certain errors, actually fail, else log that attribute was invalid and ignore */ |
262 | 0 | if(stat != NC_NOERR) { |
263 | 0 | if(stat != NC_ENOMEM && stat != NC_EHDFERR) { |
264 | 0 | LOG((0,"Invalid _NCProperties attribute: ignored")); |
265 | 0 | stat = NC_NOERR; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | return stat; |
269 | 0 | } |
270 | | |
271 | | static int |
272 | | NCZ_write_ncproperties(NC_FILE_INFO_T* h5) |
273 | 0 | { |
274 | | #ifdef SUPPRESSNCPROPERTY |
275 | | return NC_NOERR; |
276 | | #else /*!SUPPRESSNCPROPERTY*/ |
277 | 0 | int i,stat = NC_NOERR; |
278 | 0 | NC4_Provenance* prov = &h5->provenance; |
279 | 0 | NC_ATT_INFO_T* ncprops = NULL; |
280 | 0 | NCindex* attlist = NULL; |
281 | |
|
282 | 0 | LOG((5, "%s", __func__)); |
283 | | |
284 | | /* If the file is read-only, return an error. */ |
285 | 0 | if (h5->no_write) |
286 | 0 | {stat = NC_EPERM; goto done;} |
287 | | |
288 | | /* See if it already exists */ |
289 | | /* Load the root group attributes */ |
290 | 0 | if((stat = ncz_getattlist(h5->root_grp,NC_GLOBAL,NULL,&attlist))) |
291 | 0 | goto done; |
292 | | |
293 | | /* Look for _NCProperties */ |
294 | 0 | for(i=0; i<ncindexsize(attlist); i++) { |
295 | 0 | NC_ATT_INFO_T* att = (NC_ATT_INFO_T*)ncindexith(attlist,i); |
296 | 0 | if(strcmp(NCPROPS,att->hdr.name)==0) { |
297 | 0 | ncprops = att; |
298 | 0 | break; |
299 | 0 | } |
300 | 0 | } |
301 | 0 | if(ncprops != NULL) goto done; /* Already exists, no overwrite */ |
302 | | |
303 | | /* Build the property if we have legit value */ |
304 | 0 | if(prov->ncproperties != NULL) { |
305 | 0 | if((stat=nc4_att_list_add(attlist,NCPROPS,&ncprops))) |
306 | 0 | goto done; |
307 | 0 | ncprops->nc_typeid = NC_CHAR; |
308 | 0 | ncprops->len = strlen(prov->ncproperties); |
309 | 0 | if((ncprops->data = strdup(prov->ncproperties)) == NULL) |
310 | 0 | {stat = NC_ENOMEM; goto done;} |
311 | 0 | ncprops->dirty = 1; |
312 | 0 | if((ncprops->format_att_info = calloc(1,sizeof(NCZ_ATT_INFO_T)))==NULL) |
313 | 0 | {stat = NC_ENOMEM; goto done;} |
314 | 0 | ((NCZ_ATT_INFO_T*)ncprops->format_att_info)->common.file = h5; |
315 | 0 | } |
316 | | |
317 | 0 | done: |
318 | | /* For certain errors, actually fail, else log that attribute was invalid and ignore */ |
319 | 0 | switch (stat) { |
320 | 0 | case NC_ENOMEM: |
321 | 0 | case NC_EHDFERR: |
322 | 0 | case NC_EPERM: |
323 | 0 | case NC_EFILEMETA: |
324 | 0 | case NC_NOERR: |
325 | 0 | break; |
326 | 0 | default: |
327 | 0 | LOG((0,"Invalid _NCProperties attribute")); |
328 | 0 | stat = NC_NOERR; |
329 | 0 | break; |
330 | 0 | } |
331 | 0 | return stat; |
332 | 0 | #endif /*!SUPPRESSNCPROPERTY*/ |
333 | 0 | } |
334 | | |
335 | | /**************************************************/ |
336 | | /* Utilities */ |
337 | | |
338 | | /* Debugging */ |
339 | | |
340 | | void |
341 | | nczprintprovenance(NC4_Provenance* info) |
342 | 0 | { |
343 | 0 | fprintf(stderr,"[%p] version=%d superblockversion=%d ncproperties=|%s|\n", |
344 | 0 | info, |
345 | 0 | info->version, |
346 | 0 | info->superblockversion, |
347 | 0 | (info->ncproperties==NULL?"":info->ncproperties)); |
348 | 0 | } |
349 | | |
350 | | /** |
351 | | * @internal |
352 | | * |
353 | | * Clear the NCPROVENANCE object; do not free it |
354 | | * @param prov Pointer to provenance object |
355 | | * |
356 | | * @return ::NC_NOERR No error. |
357 | | * @author Dennis Heimbigner |
358 | | */ |
359 | | int |
360 | | NCZ_clear_provenance(NC4_Provenance* prov) |
361 | 1 | { |
362 | 1 | LOG((5, "%s", __func__)); |
363 | | |
364 | 1 | if(prov == NULL) return NC_NOERR; |
365 | 1 | nullfree(prov->ncproperties); |
366 | 1 | memset(prov,0,sizeof(NC4_Provenance)); |
367 | 1 | return NC_NOERR; |
368 | 1 | } |
369 | | |
370 | | #if 0 |
371 | | /* Unused functions */ |
372 | | |
373 | | /** |
374 | | * @internal Parse file properties. |
375 | | * |
376 | | * @param text0 Text properties. |
377 | | * @param pairs list of parsed (key,value) pairs |
378 | | * |
379 | | * @return ::NC_NOERR No error. |
380 | | * @author Dennis Heimbigner |
381 | | */ |
382 | | static int |
383 | | properties_parse(const char* text0, NClist* pairs) |
384 | | { |
385 | | int ret = NC_NOERR; |
386 | | char* p; |
387 | | char* q; |
388 | | char* text = NULL; |
389 | | |
390 | | if(text0 == NULL || strlen(text0) == 0) |
391 | | goto done; |
392 | | |
393 | | text = strdup(text0); |
394 | | if(text == NULL) return NC_ENOMEM; |
395 | | |
396 | | /* For back compatibility with version 1, translate '|' -> ',' */ |
397 | | for(p=text;*p;p++) { |
398 | | if(*p == NCPROPSSEP1) |
399 | | *p = NCPROPSSEP2; |
400 | | } |
401 | | |
402 | | /* Walk and fill in ncinfo */ |
403 | | p = text; |
404 | | while(*p) { |
405 | | char* name = p; |
406 | | char* value = NULL; |
407 | | char* next = NULL; |
408 | | |
409 | | /* Delimit whole (key,value) pair */ |
410 | | q = locate(p,NCPROPSSEP2); |
411 | | if(*q != '\0') /* Never go beyond the final nul term */ |
412 | | *q++ = '\0'; |
413 | | next = q; |
414 | | /* split key and value */ |
415 | | q = locate(p,'='); |
416 | | name = p; |
417 | | *q++ = '\0'; |
418 | | value = q; |
419 | | /* Set up p for next iteration */ |
420 | | p = next; |
421 | | nclistpush(pairs,strdup(name)); |
422 | | nclistpush(pairs,strdup(value)); |
423 | | } |
424 | | done: |
425 | | if(text) free(text); |
426 | | return ret; |
427 | | } |
428 | | |
429 | | /* Locate a specific character and return its pointer |
430 | | or EOS if not found |
431 | | take \ escapes into account */ |
432 | | static char* |
433 | | locate(char* p, char tag) |
434 | | { |
435 | | char* next; |
436 | | int c; |
437 | | assert(p != NULL); |
438 | | for(next = p;(c = *next);next++) { |
439 | | if(c == tag) |
440 | | return next; |
441 | | else if(c == '\\' && next[1] != '\0') |
442 | | next++; /* skip escaped char */ |
443 | | } |
444 | | return next; /* not found */ |
445 | | } |
446 | | |
447 | | /* Utility to transfer a string to a buffer with escaping */ |
448 | | static void |
449 | | escapify(NCbytes* buffer, const char* s) |
450 | | { |
451 | | const char* p; |
452 | | for(p=s;*p;p++) { |
453 | | if(strchr(ESCAPECHARS,*p) != NULL) |
454 | | ncbytesappend(buffer,'\\'); |
455 | | ncbytesappend(buffer,*p); |
456 | | } |
457 | | } |
458 | | |
459 | | /** |
460 | | * @internal |
461 | | * |
462 | | * Clear and Free the NC4_Provenance object |
463 | | * @param prov Pointer to provenance object |
464 | | * |
465 | | * @return ::NC_NOERR No error. |
466 | | * @author Dennis Heimbigner |
467 | | */ |
468 | | static int |
469 | | NCZ_free_provenance(NC4_Provenance* prov) |
470 | | { |
471 | | LOG((5, "%s", __func__)); |
472 | | |
473 | | if(prov == NULL) return NC_NOERR; |
474 | | NCZ_clear_provenance(prov); |
475 | | free(prov); |
476 | | return NC_NOERR; |
477 | | } |
478 | | |
479 | | /** |
480 | | * @internal Build _NCProperties attribute value. |
481 | | * |
482 | | * Convert a NCPROPINFO instance to a single string. |
483 | | * Will always convert to current format |
484 | | * |
485 | | * @param version |
486 | | * @param list Properties list |
487 | | * @param spropp Pointer that gets properties string. |
488 | | * @return ::NC_NOERR No error. |
489 | | * @return ::NC_EINVAL failed. |
490 | | * @author Dennis Heimbigner |
491 | | */ |
492 | | static int |
493 | | build_propstring(int version, NClist* list, char** spropp) |
494 | | { |
495 | | int stat = NC_NOERR; |
496 | | int i; |
497 | | NCbytes* buffer = NULL; |
498 | | char sversion[64]; |
499 | | |
500 | | LOG((5, "%s version=%d", __func__, version)); |
501 | | |
502 | | if(spropp != NULL) *spropp = NULL; |
503 | | |
504 | | if(version == 0 || version > NCPROPS_VERSION) /* unknown case */ |
505 | | goto done; |
506 | | if(list == NULL) |
507 | | {stat = NC_EINVAL; goto done;} |
508 | | |
509 | | if((buffer = ncbytesnew()) == NULL) |
510 | | {stat = NC_ENOMEM; goto done;} |
511 | | |
512 | | /* start with version */ |
513 | | ncbytescat(buffer,NCPVERSION); |
514 | | ncbytesappend(buffer,'='); |
515 | | /* Use current version */ |
516 | | snprintf(sversion,sizeof(sversion),"%d",version); |
517 | | ncbytescat(buffer,sversion); |
518 | | |
519 | | for(i=0;i<nclistlength(list);i+=2) { |
520 | | char* value, *name; |
521 | | name = nclistget(list,i); |
522 | | if(name == NULL) continue; |
523 | | value = nclistget(list,i+1); |
524 | | ncbytesappend(buffer,NCPROPSSEP2); /* terminate last entry */ |
525 | | escapify(buffer,name); |
526 | | ncbytesappend(buffer,'='); |
527 | | escapify(buffer,value); |
528 | | } |
529 | | /* Force null termination */ |
530 | | ncbytesnull(buffer); |
531 | | if(spropp) *spropp = ncbytesextract(buffer); |
532 | | |
533 | | done: |
534 | | if(buffer != NULL) ncbytesfree(buffer); |
535 | | return stat; |
536 | | } |
537 | | |
538 | | static int |
539 | | properties_getversion(const char* propstring, int* versionp) |
540 | | { |
541 | | int stat = NC_NOERR; |
542 | | int version = 0; |
543 | | /* propstring should begin with "version=dddd" */ |
544 | | if(propstring == NULL || strlen(propstring) < strlen("version=") + strlen("1")) |
545 | | {stat = NC_EINVAL; goto done;} /* illegal version */ |
546 | | if(memcmp(propstring,"version=",strlen("version=")) != 0) |
547 | | {stat = NC_EINVAL; goto done;} /* illegal version */ |
548 | | propstring += strlen("version="); |
549 | | /* get version */ |
550 | | version = atoi(propstring); |
551 | | if(version < 0) |
552 | | {stat = NC_EINVAL; goto done;} /* illegal version */ |
553 | | if(versionp) *versionp = version; |
554 | | done: |
555 | | return stat; |
556 | | } |
557 | | |
558 | | /** |
559 | | * @internal |
560 | | * |
561 | | * Construct the parsed provenance information |
562 | | * |
563 | | * @param prov Pointer to provenance object |
564 | | * |
565 | | * @return ::NC_NOERR No error. |
566 | | * @return ::NC_ENOMEM |
567 | | * @return ::NC_EINVAL |
568 | | * @author Dennis Heimbigner |
569 | | */ |
570 | | static int |
571 | | parse_provenance(NC4_Provenance* prov) |
572 | | { |
573 | | int stat = NC_NOERR; |
574 | | char *name = NULL; |
575 | | char *value = NULL; |
576 | | int version = 0; |
577 | | NClist* list = NULL; |
578 | | |
579 | | LOG((5, "%s: prov 0x%x", __func__, prov)); |
580 | | |
581 | | if(prov->ncproperty == NULL || strlen(prov->ncproperty) < strlen("version=")) |
582 | | {stat = NC_EINVAL; goto done;} |
583 | | if((list = nclistnew()) == NULL) |
584 | | {stat = NC_ENOMEM; goto done;} |
585 | | |
586 | | /* Do we understand the version? */ |
587 | | if(prov->version > 0 && prov->version <= NCPROPS_VERSION) {/* recognized version */ |
588 | | if((stat=properties_parse(prov->ncproperty,list))) |
589 | | goto done; |
590 | | /* Remove version pair from properties list*/ |
591 | | if(nclistlength(list) < 2) |
592 | | {stat = NC_EINVAL; goto done;} /* bad _NCProperties attribute */ |
593 | | /* Throw away the purported version=... */ |
594 | | nclistremove(list,0); /* version key */ |
595 | | nclistremove(list,0); /* version value */ |
596 | | |
597 | | /* Now, rebuild to the latest version */ |
598 | | switch (version) { |
599 | | default: break; /* do nothing */ |
600 | | case 1: { |
601 | | int i; |
602 | | for(i=0;i<nclistlength(list);i+=2) { |
603 | | char* newname = NULL; |
604 | | name = nclistget(list,i); |
605 | | if(name == NULL) continue; /* ignore */ |
606 | | if(strcmp(name,NCPNCLIB1) == 0) |
607 | | newname = NCPNCLIB2; /* change name */ |
608 | | else if(strcmp(name,NCPNCZLIB1) == 0) |
609 | | newname = NCPNCZLIB2; |
610 | | else continue; /* ignore */ |
611 | | /* Do any rename */ |
612 | | nclistset(list,i,strdup(newname)); |
613 | | if(name) {free(name); name = NULL;} |
614 | | } |
615 | | } break; |
616 | | } /*switch*/ |
617 | | } |
618 | | prov->properties = list; |
619 | | list = NULL; |
620 | | |
621 | | done: |
622 | | nclistfreeall(list); |
623 | | if(name != NULL) free(name); |
624 | | if(value != NULL) free(value); |
625 | | return stat; |
626 | | } |
627 | | |
628 | | /* Utility to copy contents of the dfalt into an NCPROPINFO object */ |
629 | | static int |
630 | | propinfo_default(NCZ_Properties* dst, const NCZ_Properties* dfalt) |
631 | | { |
632 | | int i; |
633 | | if(dst->properties == NULL) { |
634 | | dst->properties = nclistnew(); |
635 | | if(dst->properties == NULL) return NC_ENOMEM; |
636 | | } |
637 | | dst->version = dfalt->version; |
638 | | for(i=0;i<nclistlength(dfalt->properties);i++) { |
639 | | char* s = nclistget(dfalt->properties,i); |
640 | | s = strdup(s); |
641 | | if(s == NULL) return NC_ENOMEM; |
642 | | nclistpush(dst->properties,s); |
643 | | } |
644 | | return NC_NOERR; |
645 | | } |
646 | | |
647 | | #endif /*0*/ |