Coverage Report

Created: 2023-05-28 06:42

/src/netcdf-c/libdispatch/dutil.c
Line
Count
Source (jump to first uncovered line)
1
/*********************************************************************
2
 *   Copyright 2018, UCAR/Unidata
3
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 *********************************************************************/
5
6
#include "config.h"
7
#include <stdlib.h>
8
#include <string.h>
9
#include <stdio.h>
10
#include <assert.h>
11
#ifdef HAVE_UNISTD_H
12
#include <unistd.h>
13
#endif
14
#ifdef HAVE_SYS_STAT_H
15
#include <sys/stat.h>
16
#endif
17
#ifdef HAVE_FCNTL_H
18
#include <fcntl.h>
19
#endif
20
#ifdef _MSC_VER
21
#include <io.h>
22
#endif
23
#include "netcdf.h"
24
#include "ncuri.h"
25
#include "ncbytes.h"
26
#include "nclist.h"
27
#include "nclog.h"
28
#include "ncrc.h"
29
#include "ncpathmgr.h"
30
31
#define NC_MAX_PATH 4096
32
#ifndef nulldup
33
 #define nulldup(x) ((x)?strdup(x):(x))
34
#endif
35
/**************************************************/
36
/** \internal
37
 * Provide a hidden interface to allow utilities
38
 * to check if a given path name is really an ncdap4 url.
39
 * If no, return null, else return basename of the url
40
 * minus any extension.
41
 */
42
43
int
44
NC__testurl(const char* path, char** basenamep)
45
0
{
46
0
    NCURI* uri;
47
0
    int ok = NC_NOERR;
48
0
    if(ncuriparse(path,&uri))
49
0
  ok = NC_EURL;
50
0
    else {
51
0
  char* slash = (uri->path == NULL ? NULL : strrchr(uri->path, '/'));
52
0
  char* dot;
53
0
  if(slash == NULL) slash = (char*)path; else slash++;
54
0
        slash = nulldup(slash);
55
0
        if(slash == NULL)
56
0
            dot = NULL;
57
0
        else
58
0
            dot = strrchr(slash, '.');
59
0
        if(dot != NULL &&  dot != slash) *dot = '\0';
60
0
        if(basenamep)
61
0
            *basenamep=slash;
62
0
        else if(slash)
63
0
            free(slash);
64
0
    }
65
0
    ncurifree(uri);
66
0
    return ok;
67
0
}
68
69
/** \internal Return 1 if this machine is little endian */
70
int
71
NC_isLittleEndian(void)
72
0
{
73
0
    union {
74
0
        unsigned char bytes[SIZEOF_INT];
75
0
  int i;
76
0
    } u;
77
0
    u.i = 1;
78
0
    return (u.bytes[0] == 1 ? 1 : 0);
79
0
}
80
81
/** \internal */
82
char*
83
NC_backslashEscape(const char* s)
84
0
{
85
0
    const char* p;
86
0
    char* q;
87
0
    size_t len;
88
0
    char* escaped = NULL;
89
90
0
    len = strlen(s);
91
0
    escaped = (char*)malloc(1+(2*len)); /* max is everychar is escaped */
92
0
    if(escaped == NULL) return NULL;
93
0
    for(p=s,q=escaped;*p;p++) {
94
0
        char c = *p;
95
0
        switch (c) {
96
0
  case '\\':
97
0
  case '/':
98
0
  case '.':
99
0
  case '@':
100
0
      *q++ = '\\'; *q++ = '\\';
101
0
      break;
102
0
  default: *q++ = c; break;
103
0
        }
104
0
    }
105
0
    *q = '\0';
106
0
    return escaped;
107
0
}
108
109
/** \internal */
110
char*
111
NC_backslashUnescape(const char* esc)
112
0
{
113
0
    size_t len;
114
0
    char* s;
115
0
    const char* p;
116
0
    char* q;
117
118
0
    if(esc == NULL) return NULL;
119
0
    len = strlen(esc);
120
0
    s = (char*)malloc(len+1);
121
0
    if(s == NULL) return NULL;
122
0
    for(p=esc,q=s;*p;) {
123
0
  switch (*p) {
124
0
  case '\\':
125
0
       p++;
126
       /* fall thru */
127
0
  default: *q++ = *p++; break;
128
0
  }
129
0
    }
130
0
    *q = '\0';
131
0
    return s;
132
0
}
133
134
/** \internal */
135
char*
136
NC_entityescape(const char* s)
137
0
{
138
0
    const char* p;
139
0
    char* q;
140
0
    size_t len;
141
0
    char* escaped = NULL;
142
0
    const char* entity;
143
144
0
    len = strlen(s);
145
0
    escaped = (char*)malloc(1+(6*len)); /* 6 = |&apos;| */
146
0
    if(escaped == NULL) return NULL;
147
0
    for(p=s,q=escaped;*p;p++) {
148
0
  char c = *p;
149
0
  switch (c) {
150
0
  case '&':  entity = "&amp;"; break;
151
0
  case '<':  entity = "&lt;"; break;
152
0
  case '>':  entity = "&gt;"; break;
153
0
  case '"':  entity = "&quot;"; break;
154
0
  case '\'': entity = "&apos;"; break;
155
0
  default  : entity = NULL; break;
156
0
  }
157
0
  if(entity == NULL)
158
0
      *q++ = c;
159
0
  else {
160
0
      len = strlen(entity);
161
0
      memcpy(q,entity,len);
162
0
      q+=len;
163
0
  }
164
0
    }
165
0
    *q = '\0';
166
0
    return escaped;
167
0
}
168
169
/** \internal
170
Depending on the platform, the shell will sometimes
171
pass an escaped octotherpe character without removing
172
the backslash. So this function is appropriate to be called
173
on possible url paths to unescape such cases. See e.g. ncgen.
174
*/
175
char*
176
NC_shellUnescape(const char* esc)
177
0
{
178
0
    size_t len;
179
0
    char* s;
180
0
    const char* p;
181
0
    char* q;
182
183
0
    if(esc == NULL) return NULL;
184
0
    len = strlen(esc);
185
0
    s = (char*)malloc(len+1);
186
0
    if(s == NULL) return NULL;
187
0
    for(p=esc,q=s;*p;) {
188
0
  switch (*p) {
189
0
  case '\\':
190
0
       if(p[1] == '#')
191
0
           p++;
192
       /* fall thru */
193
0
  default: *q++ = *p++; break;
194
0
  }
195
0
    }
196
0
    *q = '\0';
197
0
    return s;
198
0
}
199
200
/** \internal
201
Wrap mktmp and return the generated path,
202
or null if failed.
203
Base is the base file path. XXXXX is appended
204
to allow mktmp add its unique id.
205
Return the generated path.
206
*/
207
208
char*
209
NC_mktmp(const char* base)
210
0
{
211
0
    int fd = -1;
212
0
    char* tmp = NULL;
213
0
    size_t len;
214
#ifndef HAVE_MKSTEMP
215
    int tries;
216
#define MAXTRIES 4
217
#else
218
0
    mode_t mask;
219
0
#endif
220
221
0
    len = strlen(base)+6+1;
222
0
    if((tmp = (char*)calloc(1,len))==NULL)
223
0
        goto done;
224
0
#ifdef HAVE_MKSTEMP
225
0
    strlcat(tmp,base,len);
226
0
    strlcat(tmp, "XXXXXX", len);
227
0
    mask=umask(0077);
228
0
    fd = NCmkstemp(tmp);
229
0
    (void)umask(mask);
230
#else /* !HAVE_MKSTEMP */
231
    /* Need to simulate by using some kind of pseudo-random number */
232
    for(tries=0;tries<MAXTRIES;tries++) {
233
  int rno = rand();
234
  char spid[7];
235
  if(rno < 0) rno = -rno;
236
  tmp[0] = '\0';
237
        strlcat(tmp,base,len);
238
        snprintf(spid,sizeof(spid),"%06d",rno);
239
        strlcat(tmp,spid,len);
240
        fd=NCopen3(tmp,O_RDWR|O_CREAT, _S_IREAD|_S_IWRITE);
241
  if(fd >= 0) break; /* sucess */
242
  fd = -1; /* try again */
243
    }
244
#endif /* !HAVE_MKSTEMP */
245
0
    if(fd < 0) {
246
0
        nclog(NCLOGERR, "Could not create temp file: %s",tmp);
247
0
        nullfree(tmp);
248
0
  tmp = NULL;
249
0
        goto done;
250
0
    }
251
0
done:
252
0
    if(fd >= 0) close(fd);
253
0
    return tmp;
254
0
}
255
256
/** \internal */
257
int
258
NC_readfile(const char* filename, NCbytes* content)
259
2
{
260
2
    int stat;
261
2
    stat = NC_readfilen(filename, content, -1);
262
2
    return stat;
263
2
}
264
265
int
266
NC_readfilen(const char* filename, NCbytes* content, long long amount)
267
2
{
268
2
    int ret = NC_NOERR;
269
2
    FILE* stream = NULL;
270
271
2
    stream = NCfopen(filename,"r");
272
2
    if(stream == NULL) {ret=errno; goto done;}
273
0
    ret = NC_readfileF(stream,content,amount);
274
0
    if (stream) fclose(stream);
275
2
done:
276
2
    return ret;
277
0
}
278
279
int
280
NC_readfileF(FILE* stream, NCbytes* content, long long amount)
281
0
{
282
0
#define READ_BLOCK_SIZE 4194304
283
0
    int ret = NC_NOERR;
284
0
    long long red = 0;
285
0
    char *part = (char*) malloc(READ_BLOCK_SIZE);
286
287
0
    while(amount < 0 || red < amount) {
288
0
  size_t count = fread(part, 1, READ_BLOCK_SIZE, stream);
289
0
  if(ferror(stream)) {ret = NC_EIO; goto done;}
290
0
  if(count > 0) ncbytesappendn(content,part,(unsigned long)count);
291
0
  red += count;
292
0
    if (feof(stream)) break;
293
0
    }
294
    /* Keep only amount */
295
0
    if(amount >= 0) {
296
0
  if(red > amount) ncbytessetlength(content,amount); /* read too much */
297
0
  if(red < amount) ret = NC_ETRUNC; /* |file| < amount */
298
0
    }
299
0
    ncbytesnull(content);
300
0
done:
301
0
    free(part);
302
0
    return ret;
303
0
}
304
305
/** \internal */
306
int
307
NC_writefile(const char* filename, size_t size, void* content)
308
0
{
309
0
    int ret = NC_NOERR;
310
0
    FILE* stream = NULL;
311
0
    void* p;
312
0
    size_t remain;
313
314
0
    if(content == NULL) {content = ""; size = 0;}
315
316
0
    stream = NCfopen(filename,"w");
317
0
    if(stream == NULL) {ret=errno; goto done;}
318
0
    p = content;
319
0
    remain = size;
320
0
    while(remain > 0) {
321
0
  size_t written = fwrite(p, 1, remain, stream);
322
0
  if(ferror(stream)) {ret = NC_EIO; goto done;}
323
0
  remain -= written;
324
0
    if (feof(stream)) break;
325
0
    }
326
0
done:
327
0
    if(stream) fclose(stream);
328
0
    return ret;
329
0
}
330
331
/** \internal
332
Parse a path as a url and extract the modelist.
333
If the path is not a URL, then return a NULL list.
334
If a URL, but modelist is empty or does not exist,
335
then return empty list.
336
*/
337
int
338
NC_getmodelist(const char* modestr, NClist** modelistp)
339
0
{
340
0
    int stat=NC_NOERR;
341
0
    NClist* modelist = NULL;
342
343
0
    modelist = nclistnew();
344
0
    if(modestr == NULL || strlen(modestr) == 0) goto done;
345
346
    /* Parse the mode string at the commas or EOL */
347
0
    if((stat = NC_split_delim(modestr,',',modelist))) goto done;
348
349
0
done:
350
0
    if(stat == NC_NOERR) {
351
0
  if(modelistp) {*modelistp = modelist; modelist = NULL;}
352
0
    } else
353
0
        nclistfree(modelist);
354
0
    return stat;
355
0
}
356
357
/** \internal
358
Check "mode=" list for a path and return 1 if present, 0 otherwise.
359
*/
360
int
361
NC_testpathmode(const char* path, const char* tag)
362
0
{
363
0
    int found = 0;
364
0
    NCURI* uri = NULL;
365
0
    ncuriparse(path,&uri);
366
0
    if(uri != NULL) {
367
0
        found = NC_testmode(uri,tag);
368
0
        ncurifree(uri);
369
0
    }
370
0
    return found;
371
0
}
372
373
/** \internal
374
Check "mode=" list for a url and return 1 if present, 0 otherwise.
375
*/
376
int
377
NC_testmode(NCURI* uri, const char* tag)
378
0
{
379
0
    int stat = NC_NOERR;
380
0
    int found = 0;
381
0
    int i;
382
0
    const char* modestr = NULL;
383
0
    NClist* modelist = NULL;
384
385
0
    modestr = ncurifragmentlookup(uri,"mode");
386
0
    if(modestr == NULL) goto done;
387
    /* Parse mode str */
388
0
    if((stat = NC_getmodelist(modestr,&modelist))) goto done;
389
    /* Search for tag */
390
0
    for(i=0;i<nclistlength(modelist);i++) {
391
0
        const char* mode = (const char*)nclistget(modelist,i);
392
0
  if(strcasecmp(mode,tag)==0) {found = 1; break;}
393
0
    }
394
0
done:
395
0
    nclistfreeall(modelist);
396
0
    return found;
397
0
}
398
399
#if ! defined __INTEL_COMPILER
400
#if defined __APPLE__ 
401
/** \internal */
402
403
#if ! defined HAVE_DECL_ISINF
404
405
int isinf(double x)
406
{
407
    union { unsigned long long u; double f; } ieee754;
408
    ieee754.f = x;
409
    return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) == 0x7ff00000 &&
410
           ( (unsigned)ieee754.u == 0 );
411
}
412
413
#endif /* HAVE_DECL_ISINF */
414
415
#if ! defined HAVE_DECL_ISNAN
416
/** \internal */
417
int isnan(double x)
418
{
419
    union { unsigned long long u; double f; } ieee754;
420
    ieee754.f = x;
421
    return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) +
422
           ( (unsigned)ieee754.u != 0 ) > 0x7ff00000;
423
}
424
425
#endif /* HAVE_DECL_ISNAN */
426
427
#endif /*APPLE*/
428
#endif /*!_INTEL_COMPILER*/
429
430
/** \internal */
431
int
432
NC_split_delim(const char* arg, char delim, NClist* segments)
433
0
{
434
0
    int stat = NC_NOERR;
435
0
    const char* p = NULL;
436
0
    const char* q = NULL;
437
0
    ptrdiff_t len = 0;
438
0
    char* seg = NULL;
439
440
0
    if(arg == NULL || strlen(arg)==0 || segments == NULL)
441
0
        goto done;
442
0
    p = arg;
443
0
    if(p[0] == delim) p++;
444
0
    for(;*p;) {
445
0
  q = strchr(p,delim);
446
0
  if(q==NULL)
447
0
      q = p + strlen(p); /* point to trailing nul */
448
0
        len = (q - p);
449
0
  if(len == 0)
450
0
      {stat = NC_EURL; goto done;}
451
0
  if((seg = malloc(len+1)) == NULL)
452
0
      {stat = NC_ENOMEM; goto done;}
453
0
  memcpy(seg,p,len);
454
0
  seg[len] = '\0';
455
0
  nclistpush(segments,seg);
456
0
  seg = NULL; /* avoid mem errors */
457
0
  if(*q) p = q+1; else p = q;
458
0
    }
459
460
0
done:
461
0
    nullfree(seg);
462
0
    return stat;
463
0
}
464
465
/** \internal concat the the segments with each segment preceded by '/' */
466
int
467
NC_join(NClist* segments, char** pathp)
468
0
{
469
0
    int stat = NC_NOERR;
470
0
    int i;
471
0
    NCbytes* buf = NULL;
472
473
0
    if(segments == NULL)
474
0
  {stat = NC_EINVAL; goto done;}
475
0
    if((buf = ncbytesnew())==NULL)
476
0
  {stat = NC_ENOMEM; goto done;}
477
0
    if(nclistlength(segments) == 0)
478
0
        ncbytescat(buf,"/");
479
0
    else for(i=0;i<nclistlength(segments);i++) {
480
0
  const char* seg = nclistget(segments,i);
481
0
  if(seg[0] != '/')
482
0
      ncbytescat(buf,"/");
483
0
  ncbytescat(buf,seg);
484
0
    }
485
486
0
done:
487
0
    if(!stat) {
488
0
  if(pathp) *pathp = ncbytesextract(buf);
489
0
    }
490
0
    ncbytesfree(buf);
491
0
    return stat;
492
0
}