/src/netcdf-c/libdispatch/dutil.c
Line | Count | Source |
1 | | /********************************************************************* |
2 | | * Copyright 2018, UCAR/Unidata |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | *********************************************************************/ |
5 | | |
6 | | #include "config.h" |
7 | | #include <stddef.h> |
8 | | #include <stdlib.h> |
9 | | #include <string.h> |
10 | | #include <stdio.h> |
11 | | #include <assert.h> |
12 | | #ifdef HAVE_UNISTD_H |
13 | | #include <unistd.h> |
14 | | #endif |
15 | | |
16 | | #ifdef HAVE_SYS_STAT_H |
17 | | #include <sys/stat.h> |
18 | | #endif |
19 | | #ifdef HAVE_FCNTL_H |
20 | | #include <fcntl.h> |
21 | | #endif |
22 | | #ifdef _MSC_VER |
23 | | #include <io.h> |
24 | | #endif |
25 | | #include "netcdf.h" |
26 | | #include "ncuri.h" |
27 | | #include "ncbytes.h" |
28 | | #include "nclist.h" |
29 | | #include "nclog.h" |
30 | | #include "ncpathmgr.h" |
31 | | #include "ncutil.h" |
32 | | |
33 | | #define NC_MAX_PATH 4096 |
34 | | |
35 | | /**************************************************/ |
36 | | /** \internal |
37 | | * Provide a hidden interface to allow utilities |
38 | | * to check if a given path name is really a url. |
39 | | * If no, return null, else return basename of the url path |
40 | | * minus any extension in basenamep. |
41 | | * If is a url and the protocol is file:, set isfilep. |
42 | | * @return 0 if not URL, 1 if URL |
43 | | */ |
44 | | |
45 | | int |
46 | | NC__testurl(const char* path, char** basenamep, int* isfilep) |
47 | 0 | { |
48 | 0 | int stat = NC_NOERR; |
49 | 0 | NCURI* uri = NULL; |
50 | 0 | int isurl = 1; |
51 | 0 | int isfile = 0; |
52 | 0 | const char* uripath = NULL; |
53 | 0 | char* slash = NULL; |
54 | 0 | char* dot = NULL; |
55 | | |
56 | | /* Parse url; if fails or returns null URL, then assume path is not a URL */ |
57 | 0 | stat = ncuriparse(path,&uri); |
58 | 0 | if(stat || uri == NULL) {isurl = 0; isfile = 0; goto done;} /* not a url */ |
59 | 0 | isurl = 1; |
60 | 0 | if(strcmp(uri->protocol,"file")==0) isfile = 1; |
61 | | /* Extract the basename of the URL */ |
62 | 0 | if(uri->path == NULL) |
63 | 0 | uripath = "/"; |
64 | 0 | else |
65 | 0 | uripath = uri->path; |
66 | 0 | slash = (char*)strrchr(uripath, '/'); |
67 | 0 | if(slash == NULL) slash = (char*)uripath; else slash++; |
68 | 0 | slash = nulldup(slash); |
69 | 0 | assert(slash != NULL); |
70 | 0 | dot = strrchr(slash, '.'); |
71 | 0 | if(dot != NULL && dot != slash) *dot = '\0'; |
72 | 0 | if(basenamep) |
73 | 0 | {*basenamep=slash; slash = NULL;} |
74 | 0 | done: |
75 | 0 | if(isfilep) *isfilep = isfile; |
76 | 0 | nullfree(slash); |
77 | 0 | ncurifree(uri); |
78 | 0 | return isurl; |
79 | 0 | } |
80 | | |
81 | | /** \internal Return 1 if this machine is little endian */ |
82 | | int |
83 | | NC_isLittleEndian(void) |
84 | 0 | { |
85 | 0 | union { |
86 | 0 | unsigned char bytes[SIZEOF_INT]; |
87 | 0 | int i; |
88 | 0 | } u; |
89 | 0 | u.i = 1; |
90 | 0 | return (u.bytes[0] == 1 ? 1 : 0); |
91 | 0 | } |
92 | | |
93 | | /** \internal */ |
94 | | char* |
95 | | NC_backslashEscape(const char* s) |
96 | 0 | { |
97 | 0 | const char* p; |
98 | 0 | char* q; |
99 | 0 | size_t len; |
100 | 0 | char* escaped = NULL; |
101 | |
|
102 | 0 | len = strlen(s); |
103 | 0 | escaped = (char*)malloc(1+(2*len)); /* max is everychar is escaped */ |
104 | 0 | if(escaped == NULL) return NULL; |
105 | 0 | for(p=s,q=escaped;*p;p++) { |
106 | 0 | char c = *p; |
107 | 0 | switch (c) { |
108 | 0 | case '\\': |
109 | 0 | case '/': |
110 | 0 | case '.': |
111 | 0 | case '@': |
112 | 0 | *q++ = '\\'; *q++ = '\\'; |
113 | 0 | break; |
114 | 0 | default: *q++ = c; break; |
115 | 0 | } |
116 | 0 | } |
117 | 0 | *q = '\0'; |
118 | 0 | return escaped; |
119 | 0 | } |
120 | | |
121 | | /** \internal */ |
122 | | char* |
123 | | NC_backslashUnescape(const char* esc) |
124 | 0 | { |
125 | 0 | size_t len; |
126 | 0 | char* s; |
127 | 0 | const char* p; |
128 | 0 | char* q; |
129 | |
|
130 | 0 | if(esc == NULL) return NULL; |
131 | 0 | len = strlen(esc); |
132 | 0 | s = (char*)malloc(len+1); |
133 | 0 | if(s == NULL) return NULL; |
134 | 0 | for(p=esc,q=s;*p;) { |
135 | 0 | switch (*p) { |
136 | 0 | case '\\': |
137 | 0 | p++; |
138 | | /* fall thru */ |
139 | 0 | default: *q++ = *p++; break; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | *q = '\0'; |
143 | 0 | return s; |
144 | 0 | } |
145 | | |
146 | | /** \internal */ |
147 | | char* |
148 | | NC_entityescape(const char* s) |
149 | 0 | { |
150 | 0 | const char* p; |
151 | 0 | char* q; |
152 | 0 | size_t len; |
153 | 0 | char* escaped = NULL; |
154 | 0 | const char* entity; |
155 | |
|
156 | 0 | len = strlen(s); |
157 | 0 | escaped = (char*)malloc(1+(6*len)); /* 6 = |'| */ |
158 | 0 | if(escaped == NULL) return NULL; |
159 | 0 | for(p=s,q=escaped;*p;p++) { |
160 | 0 | char c = *p; |
161 | 0 | switch (c) { |
162 | 0 | case '&': entity = "&"; break; |
163 | 0 | case '<': entity = "<"; break; |
164 | 0 | case '>': entity = ">"; break; |
165 | 0 | case '"': entity = """; break; |
166 | 0 | case '\'': entity = "'"; break; |
167 | 0 | default : entity = NULL; break; |
168 | 0 | } |
169 | 0 | if(entity == NULL) |
170 | 0 | *q++ = c; |
171 | 0 | else { |
172 | 0 | len = strlen(entity); |
173 | 0 | memcpy(q,entity,len); |
174 | 0 | q+=len; |
175 | 0 | } |
176 | 0 | } |
177 | 0 | *q = '\0'; |
178 | 0 | return escaped; |
179 | 0 | } |
180 | | |
181 | | /** \internal |
182 | | Depending on the platform, the shell will sometimes |
183 | | pass an escaped octotherpe character without removing |
184 | | the backslash. So this function is appropriate to be called |
185 | | on possible url paths to unescape such cases. See e.g. ncgen. |
186 | | */ |
187 | | char* |
188 | | NC_shellUnescape(const char* esc) |
189 | 0 | { |
190 | 0 | size_t len; |
191 | 0 | char* s; |
192 | 0 | const char* p; |
193 | 0 | char* q; |
194 | |
|
195 | 0 | if(esc == NULL) return NULL; |
196 | 0 | len = strlen(esc); |
197 | 0 | s = (char*)malloc(len+1); |
198 | 0 | if(s == NULL) return NULL; |
199 | 0 | for(p=esc,q=s;*p;) { |
200 | 0 | switch (*p) { |
201 | 0 | case '\\': |
202 | 0 | if(p[1] == '#') |
203 | 0 | p++; |
204 | | /* fall thru */ |
205 | 0 | default: *q++ = *p++; break; |
206 | 0 | } |
207 | 0 | } |
208 | 0 | *q = '\0'; |
209 | 0 | return s; |
210 | 0 | } |
211 | | |
212 | | /** \internal |
213 | | Wrap mktmp and return the generated path, |
214 | | or null if failed. |
215 | | Base is the base file path. XXXXX is appended |
216 | | to allow mktmp add its unique id. |
217 | | Return any error |
218 | | */ |
219 | | |
220 | | int |
221 | | NC_mktmp(const char* base, char** tmpp) |
222 | 0 | { |
223 | 0 | int ret = NC_NOERR; |
224 | 0 | int fd = -1; |
225 | 0 | char* tmp = NULL; |
226 | 0 | size_t len; |
227 | | #ifndef HAVE_MKSTEMP |
228 | | int tries; |
229 | | #define MAXTRIES 4 |
230 | | #else |
231 | 0 | mode_t mask; |
232 | 0 | #endif |
233 | |
|
234 | 0 | len = strlen(base)+6+1; |
235 | 0 | if((tmp = (char*)calloc(1,len))==NULL) |
236 | 0 | goto done; |
237 | 0 | #ifdef HAVE_MKSTEMP |
238 | 0 | strlcat(tmp,base,len); |
239 | 0 | strlcat(tmp, "XXXXXX", len); |
240 | 0 | mask=umask(0077); |
241 | 0 | fd = NCmkstemp(tmp); |
242 | 0 | (void)umask(mask); |
243 | | #else /* !HAVE_MKSTEMP */ |
244 | | /* Need to simulate by using some kind of pseudo-random number */ |
245 | | for(tries=0;tries<MAXTRIES;tries++) { |
246 | | int rno = rand(); |
247 | | char spid[7]; |
248 | | if(rno < 0) rno = -rno; |
249 | | tmp[0] = '\0'; |
250 | | strlcat(tmp,base,len); |
251 | | snprintf(spid,sizeof(spid),"%06d",rno); |
252 | | strlcat(tmp,spid,len); |
253 | | fd=NCopen3(tmp,O_RDWR|O_CREAT, _S_IREAD|_S_IWRITE); |
254 | | if(fd >= 0) break; /* sucess */ |
255 | | fd = -1; /* try again */ |
256 | | } |
257 | | #endif /* !HAVE_MKSTEMP */ |
258 | 0 | if(fd < 0) { |
259 | 0 | nclog(NCLOGERR, "Could not create temp file: %s",tmp); |
260 | 0 | ret = errno; errno = 0; |
261 | 0 | nullfree(tmp); |
262 | 0 | tmp = NULL; |
263 | 0 | goto done; |
264 | 0 | } |
265 | 0 | done: |
266 | 0 | if(fd >= 0) close(fd); |
267 | 0 | if(tmpp) {*tmpp = tmp;} |
268 | 0 | return ret; |
269 | 0 | } |
270 | | |
271 | | /** \internal */ |
272 | | int |
273 | | NC_readfile(const char* filename, NCbytes* content) |
274 | 2 | { |
275 | 2 | int stat; |
276 | 2 | stat = NC_readfilen(filename, content, -1); |
277 | 2 | return stat; |
278 | 2 | } |
279 | | |
280 | | int |
281 | | NC_readfilen(const char* filename, NCbytes* content, long long amount) |
282 | 2 | { |
283 | 2 | int ret = NC_NOERR; |
284 | 2 | FILE* stream = NULL; |
285 | | |
286 | 2 | stream = NCfopen(filename,"r"); |
287 | 2 | if(stream == NULL) {ret=errno; goto done;} |
288 | 0 | ret = NC_readfileF(stream,content,amount); |
289 | 0 | if (stream) fclose(stream); |
290 | 2 | done: |
291 | 2 | return ret; |
292 | 0 | } |
293 | | |
294 | | int |
295 | | NC_readfileF(FILE* stream, NCbytes* content, long long amount) |
296 | 0 | { |
297 | 0 | #define READ_BLOCK_SIZE 4194304 |
298 | 0 | int ret = NC_NOERR; |
299 | 0 | long long red = 0; |
300 | 0 | char *part = (char*) malloc(READ_BLOCK_SIZE); |
301 | |
|
302 | 0 | while(amount < 0 || red < amount) { |
303 | 0 | size_t count = fread(part, 1, READ_BLOCK_SIZE, stream); |
304 | 0 | if(ferror(stream)) {ret = NC_EIO; goto done;} |
305 | 0 | if(count > 0) ncbytesappendn(content,part,(unsigned long)count); |
306 | 0 | red += (long long)count; |
307 | 0 | if (feof(stream)) break; |
308 | 0 | } |
309 | | /* Keep only amount */ |
310 | 0 | if(amount >= 0) { |
311 | 0 | if(red > amount) ncbytessetlength(content, (unsigned long)amount); /* read too much */ |
312 | 0 | if(red < amount) ret = NC_ETRUNC; /* |file| < amount */ |
313 | 0 | } |
314 | 0 | ncbytesnull(content); |
315 | 0 | done: |
316 | 0 | free(part); |
317 | 0 | return ret; |
318 | 0 | } |
319 | | |
320 | | /** \internal */ |
321 | | int |
322 | | NC_writefile(const char* filename, size_t size, void* content) |
323 | 0 | { |
324 | 0 | int ret = NC_NOERR; |
325 | 0 | FILE* stream = NULL; |
326 | 0 | void* p; |
327 | 0 | size_t remain; |
328 | |
|
329 | 0 | if(content == NULL) {content = ""; size = 0;} |
330 | |
|
331 | 0 | stream = NCfopen(filename,"w"); |
332 | 0 | if(stream == NULL) {ret=errno; goto done;} |
333 | 0 | p = content; |
334 | 0 | remain = size; |
335 | 0 | while(remain > 0) { |
336 | 0 | size_t written = fwrite(p, 1, remain, stream); |
337 | 0 | if(ferror(stream)) {ret = NC_EIO; goto done;} |
338 | 0 | remain -= written; |
339 | 0 | if (feof(stream)) break; |
340 | 0 | } |
341 | 0 | done: |
342 | 0 | if(stream) fclose(stream); |
343 | 0 | return ret; |
344 | 0 | } |
345 | | |
346 | | /** \internal |
347 | | Parse a path as a url and extract the modelist. |
348 | | If the path is not a URL, then return a NULL list. |
349 | | If a URL, but modelist is empty or does not exist, |
350 | | then return empty list. |
351 | | */ |
352 | | int |
353 | | NC_getmodelist(const char* modestr, NClist** modelistp) |
354 | 0 | { |
355 | 0 | int stat=NC_NOERR; |
356 | 0 | NClist* modelist = NULL; |
357 | |
|
358 | 0 | modelist = nclistnew(); |
359 | 0 | if(modestr == NULL || strlen(modestr) == 0) goto done; |
360 | | |
361 | | /* Parse the mode string at the commas or EOL */ |
362 | 0 | if((stat = NC_split_delim(modestr,',',modelist))) goto done; |
363 | | |
364 | 0 | done: |
365 | 0 | if(stat == NC_NOERR) { |
366 | 0 | if(modelistp) {*modelistp = modelist; modelist = NULL;} |
367 | 0 | } else |
368 | 0 | nclistfree(modelist); |
369 | 0 | return stat; |
370 | 0 | } |
371 | | |
372 | | /** \internal |
373 | | Check "mode=" list for a path and return 1 if present, 0 otherwise. |
374 | | */ |
375 | | int |
376 | | NC_testpathmode(const char* path, const char* tag) |
377 | 0 | { |
378 | 0 | int found = 0; |
379 | 0 | NCURI* uri = NULL; |
380 | 0 | ncuriparse(path,&uri); |
381 | 0 | if(uri != NULL) { |
382 | 0 | found = NC_testmode(uri,tag); |
383 | 0 | ncurifree(uri); |
384 | 0 | } |
385 | 0 | return found; |
386 | 0 | } |
387 | | |
388 | | /** \internal |
389 | | Check "mode=" list for a url and return 1 if present, 0 otherwise. |
390 | | */ |
391 | | int |
392 | | NC_testmode(NCURI* uri, const char* tag) |
393 | 0 | { |
394 | 0 | int stat = NC_NOERR; |
395 | 0 | int found = 0; |
396 | 0 | size_t i; |
397 | 0 | const char* modestr = NULL; |
398 | 0 | NClist* modelist = NULL; |
399 | |
|
400 | 0 | modestr = ncurifragmentlookup(uri,"mode"); |
401 | 0 | if(modestr == NULL) goto done; |
402 | | /* Parse mode str */ |
403 | 0 | if((stat = NC_getmodelist(modestr,&modelist))) goto done; |
404 | | /* Search for tag */ |
405 | 0 | for(i=0;i<nclistlength(modelist);i++) { |
406 | 0 | const char* mode = (const char*)nclistget(modelist,i); |
407 | 0 | if(strcasecmp(mode,tag)==0) {found = 1; break;} |
408 | 0 | } |
409 | 0 | done: |
410 | 0 | nclistfreeall(modelist); |
411 | 0 | return found; |
412 | 0 | } |
413 | | |
414 | | /** \internal |
415 | | Add tag to fragment mode list unless already present. |
416 | | */ |
417 | | int |
418 | | NC_addmodetag(NCURI* uri, const char* tag) |
419 | 0 | { |
420 | 0 | int stat = NC_NOERR; |
421 | 0 | int found = 0; |
422 | 0 | const char* modestr = NULL; |
423 | 0 | char* modevalue = NULL; |
424 | 0 | NClist* modelist = NULL; |
425 | |
|
426 | 0 | modestr = ncurifragmentlookup(uri,"mode"); |
427 | 0 | if(modestr != NULL) { |
428 | | /* Parse mode str */ |
429 | 0 | if((stat = NC_getmodelist(modestr,&modelist))) goto done; |
430 | 0 | } else |
431 | 0 | modelist = nclistnew(); |
432 | | /* Search for tag */ |
433 | 0 | for(size_t i=0;i<nclistlength(modelist);i++) { |
434 | 0 | const char* mode = (const char*)nclistget(modelist,i); |
435 | 0 | if(strcasecmp(mode,tag)==0) {found = 1; break;} |
436 | 0 | } |
437 | | /* If not found, then add to modelist */ |
438 | 0 | if(!found) nclistpush(modelist,strdup(tag)); |
439 | | /* Convert modelist back to string */ |
440 | 0 | if((stat=NC_joinwith(modelist,",",NULL,NULL,&modevalue))) goto done; |
441 | | /* modify the url */ |
442 | 0 | if((stat=ncurisetfragmentkey(uri,"mode",modevalue))) goto done; |
443 | | |
444 | 0 | done: |
445 | 0 | nclistfreeall(modelist); |
446 | 0 | nullfree(modevalue); |
447 | 0 | return stat; |
448 | 0 | } |
449 | | |
450 | | #if ! defined __INTEL_COMPILER |
451 | | #if defined __APPLE__ |
452 | | /** \internal */ |
453 | | |
454 | | #if ! defined HAVE_DECL_ISINF |
455 | | |
456 | | int isinf(double x) |
457 | | { |
458 | | union { unsigned long long u; double f; } ieee754; |
459 | | ieee754.f = x; |
460 | | return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) == 0x7ff00000 && |
461 | | ( (unsigned)ieee754.u == 0 ); |
462 | | } |
463 | | |
464 | | #endif /* HAVE_DECL_ISINF */ |
465 | | |
466 | | #if ! defined HAVE_DECL_ISNAN |
467 | | /** \internal */ |
468 | | int isnan(double x) |
469 | | { |
470 | | union { unsigned long long u; double f; } ieee754; |
471 | | ieee754.f = x; |
472 | | return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) + |
473 | | ( (unsigned)ieee754.u != 0 ) > 0x7ff00000; |
474 | | } |
475 | | |
476 | | #endif /* HAVE_DECL_ISNAN */ |
477 | | |
478 | | #endif /*APPLE*/ |
479 | | #endif /*!_INTEL_COMPILER*/ |
480 | | |
481 | | /** \internal */ |
482 | | int |
483 | | NC_split_delim(const char* arg, char delim, NClist* segments) |
484 | 0 | { |
485 | 0 | int stat = NC_NOERR; |
486 | 0 | const char* p = NULL; |
487 | 0 | const char* q = NULL; |
488 | 0 | ptrdiff_t len = 0; |
489 | 0 | char* seg = NULL; |
490 | |
|
491 | 0 | if(arg == NULL || strlen(arg)==0 || segments == NULL) |
492 | 0 | goto done; |
493 | 0 | p = arg; |
494 | 0 | if(p[0] == delim) p++; |
495 | 0 | for(;*p;) { |
496 | 0 | q = strchr(p,delim); |
497 | 0 | if(q==NULL) |
498 | 0 | q = p + strlen(p); /* point to trailing nul */ |
499 | 0 | len = (q - p); |
500 | 0 | if(len == 0) |
501 | 0 | {stat = NC_EURL; goto done;} |
502 | 0 | if((seg = malloc((size_t)len+1)) == NULL) |
503 | 0 | {stat = NC_ENOMEM; goto done;} |
504 | 0 | memcpy(seg,p,(size_t)len); |
505 | 0 | seg[len] = '\0'; |
506 | 0 | nclistpush(segments,seg); |
507 | 0 | seg = NULL; /* avoid mem errors */ |
508 | 0 | if(*q) p = q+1; else p = q; |
509 | 0 | } |
510 | | |
511 | 0 | done: |
512 | 0 | nullfree(seg); |
513 | 0 | return stat; |
514 | 0 | } |
515 | | |
516 | | /** \internal concat the the segments with each segment preceded by '/' */ |
517 | | int |
518 | | NC_join(NClist* segments, char** pathp) |
519 | 0 | { |
520 | 0 | return NC_joinwith(segments,"/","/",NULL,pathp); |
521 | 0 | } |
522 | | |
523 | | /** \internal |
524 | | Concat the the segments with separator. |
525 | | @param segments to join |
526 | | @param sep to use between segments |
527 | | @param prefix put at front of joined string: NULL => no prefix |
528 | | @param suffix put at end of joined string: NULL => no suffix |
529 | | @param pathp return the join in this |
530 | | */ |
531 | | int |
532 | | NC_joinwith(NClist* segments, const char* sep, const char* prefix, const char* suffix, char** pathp) |
533 | 0 | { |
534 | 0 | int stat = NC_NOERR; |
535 | 0 | size_t i; |
536 | 0 | NCbytes* buf = NULL; |
537 | 0 | size_t seplen = nulllen(sep); |
538 | |
|
539 | 0 | if(segments == NULL) |
540 | 0 | {stat = NC_EINVAL; goto done;} |
541 | 0 | if((buf = ncbytesnew())==NULL) |
542 | 0 | {stat = NC_ENOMEM; goto done;} |
543 | 0 | if(prefix) ncbytescat(buf,prefix); |
544 | 0 | for(i=0;i<nclistlength(segments);i++) { |
545 | 0 | const char* seg = nclistget(segments,i); |
546 | 0 | if(i>0 && strncmp(seg,sep,seplen)!=0) |
547 | 0 | ncbytescat(buf,sep); |
548 | 0 | ncbytescat(buf,seg); |
549 | 0 | } |
550 | 0 | if(suffix) ncbytescat(buf,suffix); |
551 | 0 | if(pathp) *pathp = ncbytesextract(buf); |
552 | 0 | done: |
553 | 0 | ncbytesfree(buf); |
554 | 0 | return stat; |
555 | 0 | } |
556 | | |
557 | | #if 0 |
558 | | /* concat the the segments with each segment preceded by '/' */ |
559 | | int |
560 | | NC_join(NClist* segments, char** pathp) |
561 | | { |
562 | | int stat = NC_NOERR; |
563 | | size_t i; |
564 | | NCbytes* buf = NULL; |
565 | | |
566 | | if(segments == NULL) |
567 | | {stat = NC_EINVAL; goto done;} |
568 | | if((buf = ncbytesnew())==NULL) |
569 | | {stat = NC_ENOMEM; goto done;} |
570 | | if(nclistlength(segments) == 0) |
571 | | ncbytescat(buf,"/"); |
572 | | else for(i=0;i<nclistlength(segments);i++) { |
573 | | const char* seg = nclistget(segments,i); |
574 | | if(seg[0] != '/') |
575 | | ncbytescat(buf,"/"); |
576 | | ncbytescat(buf,seg); |
577 | | } |
578 | | |
579 | | done: |
580 | | if(!stat) { |
581 | | if(pathp) *pathp = ncbytesextract(buf); |
582 | | } |
583 | | ncbytesfree(buf); |
584 | | return THROW(stat); |
585 | | } |
586 | | #endif |
587 | | |
588 | | #if 0 |
589 | | static int |
590 | | extendenvv(char*** envvp, int amount, int* oldlenp) |
591 | | { |
592 | | char** envv = *envvp; |
593 | | char** p; |
594 | | int len; |
595 | | for(len=0,p=envv;*p;p++) len++; |
596 | | *oldlenp = len; |
597 | | if((envv = (char**)malloc((amount+len+1)*sizeof(char*)))==NULL) return NC_ENOMEM; |
598 | | memcpy(envv,*envvp,sizeof(char*)*len); |
599 | | envv[len] = NULL; |
600 | | nullfree(*envvp); |
601 | | *envvp = envv; envv = NULL; |
602 | | return NC_NOERR; |
603 | | } |
604 | | #endif |
605 | | |
606 | | static int |
607 | | nc_compare(const void* arg1, const void* arg2) |
608 | 0 | { |
609 | 0 | char* n1 = *((char**)arg1); |
610 | 0 | char* n2 = *((char**)arg2); |
611 | 0 | return strcmp(n1,n2); |
612 | 0 | } |
613 | | |
614 | | /* quick sort a list of strings */ |
615 | | void |
616 | | NC_sortenvv(size_t n, char** envv) |
617 | 0 | { |
618 | 0 | if(n <= 1) return; |
619 | 0 | qsort(envv, n, sizeof(char*), nc_compare); |
620 | | #if 0 |
621 | | {int i; |
622 | | for(i=0;i<n;i++) |
623 | | fprintf(stderr,">>> sorted: [%d] %s\n",i,(const char*)envv[i]); |
624 | | } |
625 | | #endif |
626 | 0 | } |
627 | | |
628 | | void |
629 | | NC_freeenvv(size_t n, char** envv) |
630 | 0 | { |
631 | 0 | size_t i; |
632 | 0 | char** p; |
633 | 0 | if(envv == NULL) return; |
634 | 0 | if(n < 0) |
635 | 0 | {for(n=0, p = envv; *p; n++) {}; /* count number of strings */} |
636 | 0 | for(i=0;i<n;i++) { |
637 | 0 | if(envv[i]) { |
638 | 0 | free(envv[i]); |
639 | 0 | } |
640 | 0 | } |
641 | 0 | free(envv); |
642 | 0 | } |
643 | | |