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