/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 = |'| */ |
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 = "&"; break; |
151 | 0 | case '<': entity = "<"; break; |
152 | 0 | case '>': entity = ">"; break; |
153 | 0 | case '"': entity = """; break; |
154 | 0 | case '\'': entity = "'"; 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 | } |