/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 |  |  |