/src/netcdf-c/libnczarr/zmap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018, University Corporation for Atmospheric Research |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | */ |
5 | | |
6 | | #include "zincludes.h" |
7 | | #include <stdarg.h> |
8 | | #include "ncpathmgr.h" |
9 | | |
10 | | /**************************************************/ |
11 | | /* Import the current implementations */ |
12 | | |
13 | | |
14 | | /**************************************************/ |
15 | | |
16 | | NCZM_FEATURES |
17 | | nczmap_features(NCZM_IMPL impl) |
18 | 0 | { |
19 | 0 | switch (impl) { |
20 | 0 | case NCZM_FILE: return zmap_file.features; |
21 | | #ifdef ENABLE_NCZARR_ZIP |
22 | | case NCZM_ZIP: return zmap_zip.features; |
23 | | #endif |
24 | | #ifdef ENABLE_S3 |
25 | | case NCZM_S3: return zmap_s3sdk.features; |
26 | | #endif |
27 | 0 | default: break; |
28 | 0 | } |
29 | 0 | return NCZM_UNIMPLEMENTED; |
30 | 0 | } |
31 | | |
32 | | int |
33 | | nczmap_create(NCZM_IMPL impl, const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp) |
34 | 0 | { |
35 | 0 | int stat = NC_NOERR; |
36 | 0 | NCZMAP* map = NULL; |
37 | 0 | NCURI* uri = NULL; |
38 | | |
39 | 0 | if(path == NULL || strlen(path) == 0) |
40 | 0 | {stat = NC_EINVAL; goto done;} |
41 | | |
42 | 0 | if(mapp) *mapp = NULL; |
43 | |
|
44 | 0 | switch (impl) { |
45 | 0 | case NCZM_FILE: |
46 | 0 | stat = zmap_file.create(path, mode, flags, parameters, &map); |
47 | 0 | if(stat) goto done; |
48 | 0 | break; |
49 | | #ifdef ENABLE_NCZARR_ZIP |
50 | | case NCZM_ZIP: |
51 | | stat = zmap_zip.create(path, mode, flags, parameters, &map); |
52 | | if(stat) goto done; |
53 | | break; |
54 | | #endif |
55 | | #ifdef ENABLE_S3 |
56 | | case NCZM_S3: |
57 | | stat = zmap_s3sdk.create(path, mode, flags, parameters, &map); |
58 | | if(stat) goto done; |
59 | | break; |
60 | | #endif |
61 | 0 | default: |
62 | 0 | {stat = REPORT(NC_ENOTBUILT,"nczmap_create"); goto done;} |
63 | 0 | } |
64 | 0 | if(mapp) *mapp = map; |
65 | 0 | done: |
66 | 0 | ncurifree(uri); |
67 | 0 | return THROW(stat); |
68 | 0 | } |
69 | | |
70 | | int |
71 | | nczmap_open(NCZM_IMPL impl, const char *path, int mode, size64_t flags, void* parameters, NCZMAP** mapp) |
72 | 0 | { |
73 | 0 | int stat = NC_NOERR; |
74 | 0 | NCZMAP* map = NULL; |
75 | 0 | NCURI* uri = NULL; |
76 | |
|
77 | 0 | if(path == NULL || strlen(path) == 0) |
78 | 0 | {stat = NC_EINVAL; goto done;} |
79 | | |
80 | 0 | if(mapp) *mapp = NULL; |
81 | |
|
82 | 0 | switch (impl) { |
83 | 0 | case NCZM_FILE: |
84 | 0 | stat = zmap_file.open(path, mode, flags, parameters, &map); |
85 | 0 | if(stat) goto done; |
86 | 0 | break; |
87 | | #ifdef ENABLE_NCZARR_ZIP |
88 | | case NCZM_ZIP: |
89 | | stat = zmap_zip.open(path, mode, flags, parameters, &map); |
90 | | if(stat) goto done; |
91 | | break; |
92 | | #endif |
93 | | #ifdef ENABLE_S3 |
94 | | case NCZM_S3: |
95 | | stat = zmap_s3sdk.open(path, mode, flags, parameters, &map); |
96 | | if(stat) goto done; |
97 | | break; |
98 | | #endif |
99 | 0 | default: |
100 | 0 | {stat = REPORT(NC_ENOTBUILT,"nczmap_open"); goto done;} |
101 | 0 | } |
102 | | |
103 | 0 | done: |
104 | 0 | ncurifree(uri); |
105 | 0 | if(!stat) { |
106 | 0 | if(mapp) *mapp = map; |
107 | 0 | } |
108 | 0 | return THROW(stat); |
109 | 0 | } |
110 | | |
111 | | /**************************************************/ |
112 | | /* API Wrapper */ |
113 | | |
114 | | int |
115 | | nczmap_close(NCZMAP* map, int delete) |
116 | 0 | { |
117 | 0 | int stat = NC_NOERR; |
118 | 0 | if(map && map->api) |
119 | 0 | stat = map->api->close(map,delete); |
120 | 0 | return THROW(stat); |
121 | 0 | } |
122 | | |
123 | | int |
124 | | nczmap_exists(NCZMAP* map, const char* key) |
125 | 0 | { |
126 | 0 | return map->api->exists(map, key); |
127 | 0 | } |
128 | | |
129 | | int |
130 | | nczmap_len(NCZMAP* map, const char* key, size64_t* lenp) |
131 | 0 | { |
132 | 0 | return map->api->len(map, key, lenp); |
133 | 0 | } |
134 | | |
135 | | int |
136 | | nczmap_read(NCZMAP* map, const char* key, size64_t start, size64_t count, void* content) |
137 | 0 | { |
138 | 0 | return map->api->read(map, key, start, count, content); |
139 | 0 | } |
140 | | |
141 | | int |
142 | | nczmap_write(NCZMAP* map, const char* key, size64_t start, size64_t count, const void* content) |
143 | 0 | { |
144 | 0 | return map->api->write(map, key, start, count, content); |
145 | 0 | } |
146 | | |
147 | | /* Define a static qsort comparator for strings for use with qsort */ |
148 | | static int |
149 | | cmp_strings(const void* a1, const void* a2) |
150 | 0 | { |
151 | 0 | const char** s1 = (const char**)a1; |
152 | 0 | const char** s2 = (const char**)a2; |
153 | 0 | return strcmp(*s1,*s2); |
154 | 0 | } |
155 | | |
156 | | int |
157 | | nczmap_search(NCZMAP* map, const char* prefix, NClist* matches) |
158 | 0 | { |
159 | 0 | int stat = NC_NOERR; |
160 | 0 | if((stat = map->api->search(map, prefix, matches)) == NC_NOERR) { |
161 | | /* sort the list */ |
162 | 0 | if(nclistlength(matches) > 1) { |
163 | 0 | void* base = nclistcontents(matches); |
164 | 0 | qsort(base, nclistlength(matches), sizeof(char*), cmp_strings); |
165 | 0 | } |
166 | 0 | } |
167 | 0 | return stat; |
168 | 0 | } |
169 | | |
170 | | /**************************************************/ |
171 | | /* Utilities */ |
172 | | |
173 | | int |
174 | | nczm_split(const char* path, NClist* segments) |
175 | 0 | { |
176 | 0 | return nczm_split_delim(path,NCZM_SEP[0],segments); |
177 | 0 | } |
178 | | |
179 | | int |
180 | | nczm_split_delim(const char* path, char delim, NClist* segments) |
181 | 0 | { |
182 | 0 | return NC_split_delim(path,delim,segments); |
183 | 0 | } |
184 | | |
185 | | /* concat the the segments with each segment preceded by '/' */ |
186 | | int |
187 | | nczm_join(NClist* segments, char** pathp) |
188 | 0 | { |
189 | 0 | int stat = NC_NOERR; |
190 | 0 | int i; |
191 | 0 | NCbytes* buf = NULL; |
192 | |
|
193 | 0 | if(segments == NULL) |
194 | 0 | {stat = NC_EINVAL; goto done;} |
195 | 0 | if((buf = ncbytesnew())==NULL) |
196 | 0 | {stat = NC_ENOMEM; goto done;} |
197 | 0 | if(nclistlength(segments) == 0) |
198 | 0 | ncbytescat(buf,"/"); |
199 | 0 | else for(i=0;i<nclistlength(segments);i++) { |
200 | 0 | const char* seg = nclistget(segments,i); |
201 | 0 | if(seg[0] != '/') |
202 | 0 | ncbytescat(buf,"/"); |
203 | 0 | ncbytescat(buf,seg); |
204 | 0 | } |
205 | |
|
206 | 0 | done: |
207 | 0 | if(!stat) { |
208 | 0 | if(pathp) *pathp = ncbytesextract(buf); |
209 | 0 | } |
210 | 0 | ncbytesfree(buf); |
211 | 0 | return THROW(stat); |
212 | 0 | } |
213 | | |
214 | | int |
215 | | nczm_concat(const char* prefix, const char* suffix, char** pathp) |
216 | 0 | { |
217 | 0 | NCbytes* buf = ncbytesnew(); |
218 | |
|
219 | 0 | if(prefix == NULL || strlen(prefix)==0) prefix = NCZM_SEP; |
220 | 0 | if(suffix == NULL) suffix = ""; |
221 | 0 | ncbytescat(buf,prefix); |
222 | 0 | if(ncbytesget(buf,ncbyteslength(buf)-1) == NCZM_SEP[0]) |
223 | 0 | ncbytessetlength(buf,ncbyteslength(buf)-1); |
224 | 0 | if(strlen(suffix) > 0 && suffix[0] != NCZM_SEP[0]) |
225 | 0 | ncbytescat(buf,NCZM_SEP); |
226 | 0 | ncbytescat(buf,suffix); |
227 | 0 | if(pathp) *pathp = ncbytesextract(buf); |
228 | 0 | ncbytesfree(buf); |
229 | 0 | return NC_NOERR; |
230 | 0 | } |
231 | | |
232 | | /* Concat multiple strings, but with no intervening separators */ |
233 | | int |
234 | | nczm_appendn(char** resultp, int n, ...) |
235 | 0 | { |
236 | 0 | va_list args; |
237 | 0 | NCbytes* buf = ncbytesnew(); |
238 | 0 | int i; |
239 | |
|
240 | 0 | va_start(args, n); |
241 | 0 | for(i=0;i<n;i++) { |
242 | 0 | char* s = va_arg(args,char*); |
243 | 0 | if(s != NULL) ncbytescat(buf,s); |
244 | 0 | } |
245 | 0 | ncbytesnull(buf); |
246 | 0 | va_end(args); |
247 | 0 | if(resultp) {*resultp = ncbytesextract(buf);} |
248 | 0 | ncbytesfree(buf); |
249 | 0 | return NC_NOERR; |
250 | 0 | } |
251 | | |
252 | | /* A segment is defined as a '/' plus characters following up |
253 | | to the end or upto the next '/' |
254 | | */ |
255 | | int |
256 | | nczm_divide_at(const char* key, int nsegs, char** prefixp, char** suffixp) |
257 | 0 | { |
258 | 0 | int stat = NC_NOERR; |
259 | 0 | char* prefix = NULL; |
260 | 0 | char* suffix = NULL; |
261 | 0 | size_t len, i; |
262 | 0 | ptrdiff_t delta; |
263 | 0 | const char* p; |
264 | 0 | int abssegs = (nsegs >= 0 ?nsegs: -nsegs); |
265 | 0 | int presegs = 0; |
266 | | |
267 | | /* Special case */ |
268 | 0 | if(key == NULL || strlen(key) == 0) goto done; |
269 | | |
270 | 0 | p = (key[0] == '/' ? key+1 : key); |
271 | | /* Count number of segments */ |
272 | 0 | for(len=0;;) { |
273 | 0 | const char* q = strchr(p,'/'); |
274 | 0 | len++; |
275 | 0 | if(q == NULL) break; |
276 | 0 | p = q+1; /* start past leading '/' of next segment */ |
277 | 0 | } |
278 | 0 | if(abssegs > len) |
279 | 0 | {stat = NC_EINVAL; goto done;} |
280 | | /* find split point */ |
281 | 0 | if(nsegs >= 0) |
282 | 0 | {presegs = abssegs;} |
283 | 0 | else |
284 | 0 | {presegs = (len - abssegs);} |
285 | | |
286 | | /* skip past the first presegs segments */ |
287 | 0 | for(p=key,i=0;i<presegs;i++) { |
288 | 0 | const char* q = strchr(p+1,'/'); |
289 | 0 | if(q == NULL) {p = (p + strlen(p)); break;} |
290 | 0 | else p = q; |
291 | 0 | } |
292 | | /* p should point at the presegs+1 start point */ |
293 | 0 | delta = (p-key); |
294 | 0 | if(prefixp) { |
295 | 0 | prefix = malloc(delta+1); |
296 | 0 | memcpy(prefix,key,delta); |
297 | 0 | prefix[delta] = '\0'; |
298 | 0 | *prefixp = prefix; |
299 | 0 | } |
300 | 0 | if(suffixp) { |
301 | 0 | suffix = strdup(p); |
302 | 0 | *suffixp = suffix; |
303 | 0 | } |
304 | 0 | done: |
305 | 0 | return stat; |
306 | 0 | } |
307 | | |
308 | | int |
309 | | nczm_clear(NCZMAP* map) |
310 | 0 | { |
311 | 0 | if(map) |
312 | 0 | nullfree(map->url); |
313 | 0 | return NC_NOERR; |
314 | 0 | } |
315 | | |
316 | | int |
317 | | nczm_isabsolutepath(const char* path) |
318 | 0 | { |
319 | 0 | if(path == NULL) return 0; |
320 | 0 | switch (path[0]) { |
321 | 0 | case '\\': return 1; |
322 | 0 | case '/': return 1; |
323 | 0 | case '\0': break; |
324 | 0 | default: |
325 | | /* Check for windows drive letter */ |
326 | 0 | if(NChasdriveletter(path)) return 1; |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | return 0; |
330 | 0 | } |
331 | | |
332 | | /* Convert forward slash to backslash ( !localize) or vice-versa (localize)*/ |
333 | | int |
334 | | nczm_localize(const char* path, char** localpathp, int localize) |
335 | 0 | { |
336 | 0 | int stat = NC_NOERR; |
337 | 0 | char* localpath = NULL; |
338 | 0 | char* p; |
339 | 0 | int forward = 1; |
340 | 0 | int offset = 0; |
341 | 0 | static const char* windrive = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
342 | |
|
343 | | #ifdef _MSC_VER |
344 | | forward = (localize?0:1); |
345 | | #endif |
346 | | /* If path comes from a url, then it may start with: /x:/... |
347 | | where x is a drive letter. If so, then remove leading / */ |
348 | 0 | if(strlen(path) >= 4 |
349 | 0 | && path[0] == '/' && strchr(windrive,path[1]) != NULL |
350 | 0 | && path[2] == ':' && path[3] == '/') |
351 | 0 | offset = 1; |
352 | 0 | if((localpath = strdup(path+offset))==NULL) return NC_ENOMEM; |
353 | | |
354 | 0 | for(p=localpath;*p;p++) { |
355 | 0 | if(forward && *p == '\\') *p = '/'; |
356 | 0 | else if(!forward && *p == '/') *p = '\\'; |
357 | 0 | } |
358 | 0 | if(localpathp) {*localpathp = localpath; localpath = NULL;} |
359 | 0 | nullfree(localpath); |
360 | 0 | return stat; |
361 | 0 | } |
362 | | |
363 | | /* Convert path0 to be: |
364 | | 1. absolute -- including drive letters |
365 | | 2. forward slashed -- we will convert back to back slash in nczm_fixpath |
366 | | */ |
367 | | |
368 | | int |
369 | | nczm_canonicalpath(const char* path, char** cpathp) |
370 | 0 | { |
371 | 0 | int ret = NC_NOERR; |
372 | 0 | char* cpath = NULL; |
373 | 0 | char* tmp1 = NULL; |
374 | |
|
375 | 0 | if(path == NULL) |
376 | 0 | {cpath = NULL; goto done;} |
377 | | |
378 | | /* Process path to make it be absolute*/ |
379 | 0 | if((tmp1 = NCpathabsolute(path))==NULL) {ret = NC_ENOMEM; goto done;} |
380 | | |
381 | | /* Fix slashes to be forward for now */ |
382 | 0 | if((ret = nczm_localize(tmp1,&cpath,!LOCALIZE))) goto done; |
383 | | |
384 | 0 | if(cpathp) {*cpathp = cpath; cpath = NULL;} |
385 | 0 | done: |
386 | 0 | nullfree(tmp1); |
387 | 0 | nullfree(cpath); |
388 | 0 | return THROW(ret); |
389 | 0 | } |
390 | | |
391 | | /* extract the first segment of a path */ |
392 | | int |
393 | | nczm_segment1(const char* path, char** seg1p) |
394 | 0 | { |
395 | 0 | int ret = NC_NOERR; |
396 | 0 | char* seg1 = NULL; |
397 | 0 | const char* p = NULL; |
398 | 0 | const char* q = NULL; |
399 | 0 | ptrdiff_t delta; |
400 | |
|
401 | 0 | if(path == NULL) |
402 | 0 | {seg1 = NULL; goto done;} |
403 | | |
404 | 0 | p = path; |
405 | 0 | if(*p == '/') p++; /* skip any leading '/' */ |
406 | 0 | q = strchr(p,'/'); |
407 | 0 | if(q == NULL) q = p+strlen(p); /* point to stop character */ |
408 | 0 | delta = (q-p); |
409 | 0 | if((seg1 = (char*)malloc(delta+1))==NULL) |
410 | 0 | {ret = NC_ENOMEM; goto done;} |
411 | 0 | memcpy(seg1,p,delta); |
412 | 0 | seg1[delta] = '\0'; |
413 | |
|
414 | 0 | if(seg1p) {*seg1p = seg1; seg1 = NULL;} |
415 | 0 | done: |
416 | 0 | nullfree(seg1); |
417 | 0 | return THROW(ret); |
418 | 0 | } |
419 | | |
420 | | /* |
421 | | Extract the last segment from path. |
422 | | */ |
423 | | |
424 | | int |
425 | | nczm_lastsegment(const char* path, char** lastp) |
426 | 0 | { |
427 | 0 | int ret = NC_NOERR; |
428 | 0 | const char* last = NULL; |
429 | |
|
430 | 0 | if(path == NULL) |
431 | 0 | {if(lastp) *lastp = NULL; goto done;} |
432 | | |
433 | 0 | last = strrchr(path,'/'); |
434 | 0 | if(last == NULL) last = path; else last++; |
435 | |
|
436 | 0 | if(lastp) *lastp = strdup(last); |
437 | |
|
438 | 0 | done: |
439 | 0 | return THROW(ret); |
440 | 0 | } |
441 | | |
442 | | /* |
443 | | Extract the basename from a path. |
444 | | Basename is last segment minus one extension. |
445 | | */ |
446 | | |
447 | | int |
448 | | nczm_basename(const char* path, char** basep) |
449 | 0 | { |
450 | 0 | int stat = NC_NOERR; |
451 | 0 | char* base = NULL; |
452 | 0 | char* last = NULL; |
453 | 0 | const char* p = NULL; |
454 | 0 | ptrdiff_t delta; |
455 | |
|
456 | 0 | if((stat=nczm_lastsegment(path,&last))) goto done; |
457 | | |
458 | 0 | if(last == NULL) goto done; |
459 | 0 | p = strrchr(last,'.'); |
460 | 0 | if(p == NULL) p = last+strlen(last); |
461 | 0 | delta = (p - last); |
462 | 0 | if((base = (char*)malloc(delta+1))==NULL) |
463 | 0 | {stat = NC_ENOMEM; goto done;} |
464 | 0 | memcpy(base,last,delta); |
465 | 0 | base[delta] = '\0'; |
466 | 0 | if(basep) {*basep = base; base = NULL;} |
467 | 0 | done: |
468 | 0 | nullfree(last); |
469 | 0 | nullfree(base); |
470 | 0 | return THROW(stat); |
471 | 0 | } |
472 | | |
473 | | /* bubble sort a list of strings */ |
474 | | void |
475 | | nczm_sortlist(NClist* l) |
476 | 0 | { |
477 | 0 | nczm_sortenvv(nclistlength(l),(char**)nclistcontents(l)); |
478 | 0 | } |
479 | | |
480 | | static int |
481 | | nczm_compare(const void* arg1, const void* arg2) |
482 | 0 | { |
483 | 0 | char* n1 = *((char**)arg1); |
484 | 0 | char* n2 = *((char**)arg2); |
485 | 0 | return strcmp(n1,n2); |
486 | 0 | } |
487 | | |
488 | | /* quick sort a list of strings */ |
489 | | void |
490 | | nczm_sortenvv(int n, char** envv) |
491 | 0 | { |
492 | 0 | if(n <= 1) return; |
493 | 0 | qsort(envv, n, sizeof(char*), nczm_compare); |
494 | | #if 0 |
495 | | {int i; |
496 | | for(i=0;i<n;i++) |
497 | | fprintf(stderr,">>> sorted: [%d] %s\n",i,(const char*)envv[i]); |
498 | | } |
499 | | #endif |
500 | 0 | } |
501 | | |
502 | | void |
503 | | NCZ_freeenvv(int n, char** envv) |
504 | 0 | { |
505 | 0 | int i; |
506 | 0 | char** p; |
507 | 0 | if(envv == NULL) return; |
508 | 0 | if(n < 0) |
509 | 0 | {for(n=0, p = envv; *p; n++); /* count number of strings */} |
510 | 0 | for(i=0;i<n;i++) { |
511 | 0 | if(envv[i]) { |
512 | 0 | free(envv[i]); |
513 | 0 | } |
514 | 0 | } |
515 | 0 | free(envv); |
516 | 0 | } |