Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}