/src/netcdf-c/libdispatch/ncuri.c
| Line | Count | Source | 
| 1 |  | /********************************************************************* | 
| 2 |  |  *   Copyright 2018, UCAR/Unidata | 
| 3 |  |  *   See netcdf/COPYRIGHT file for copying and redistribution conditions. | 
| 4 |  |  *   $Header$ | 
| 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 |  |  | 
| 13 |  | #include "ncuri.h" | 
| 14 |  | #include "ncbytes.h" | 
| 15 |  | #include "nclist.h" | 
| 16 |  |  | 
| 17 |  | /* Include netcdf.h to allow access to | 
| 18 |  |    NC_ error return codes. */ | 
| 19 |  | #include "netcdf.h" | 
| 20 |  |  | 
| 21 |  | #define NCURIDEBUG | 
| 22 |  |  | 
| 23 |  | /* Extra debug info */ | 
| 24 |  | #undef NCXDEBUG | 
| 25 |  |  | 
| 26 |  | #ifdef NCURIDEBUG | 
| 27 | 339 | #define THROW(n) {ret=(n); goto done;} | 
| 28 |  | #else | 
| 29 |  | #define THROW(n) {goto done;} | 
| 30 |  | #endif | 
| 31 |  |  | 
| 32 |  | #define PADDING 8 | 
| 33 |  |  | 
| 34 | 339 | #define LBRACKET '[' | 
| 35 |  | #define RBRACKET ']' | 
| 36 | 0 | #define EOFCHAR '\0' | 
| 37 | 0 | #define RBRACKETSTR "]" | 
| 38 |  |  | 
| 39 | 0 | #define DRIVELETTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | 
| 40 |  |  | 
| 41 |  | #ifndef FIX | 
| 42 |  | #define FIX(s) ((s)==NULL?"NULL":(s)) | 
| 43 |  | #endif | 
| 44 |  |  | 
| 45 |  | #ifndef NILLEN | 
| 46 |  | #define NILLEN(s) ((s)==NULL?0:strlen(s)) | 
| 47 |  | #endif | 
| 48 |  |  | 
| 49 |  | #ifndef nulldup | 
| 50 |  | #define nulldup(s) ((s)==NULL?NULL:strdup(s)) | 
| 51 |  | #endif | 
| 52 |  |  | 
| 53 | 0 | #define terminate(p) {*(p) = EOFCHAR;} | 
| 54 |  |  | 
| 55 |  | #define endof(p) ((p)+strlen(p)) | 
| 56 |  |  | 
| 57 |  | #define lshift(buf,buflen) {memmove(buf,buf+1,buflen+1);} | 
| 58 |  | #define rshift(buf,buflen) {memmove(buf+1,buf,buflen+1);} | 
| 59 |  |  | 
| 60 |  | /* Allowable character sets for encode */ | 
| 61 |  |  | 
| 62 |  | static char* ascii = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; | 
| 63 |  |  | 
| 64 |  | /* Classes according to the URL RFC" */ | 
| 65 |  | #define RFCRESERVED " !*'();:@&=+$,/?#[]" | 
| 66 |  | #define RFCUNRESERVED "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~" | 
| 67 |  | #define RFCOTHER "\"%<>\\^`{|}" | 
| 68 |  |  | 
| 69 |  | /* I really hate the URL encoding mess */ | 
| 70 |  |  | 
| 71 |  | static const char* pathallow = | 
| 72 |  | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; | 
| 73 |  |  | 
| 74 |  | static const char* queryallow = | 
| 75 |  | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; | 
| 76 |  |  | 
| 77 |  | /* user+pwd allow = path allow - "@:" */ | 
| 78 |  | static const char* userpwdallow = | 
| 79 |  | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$&'()*+,-.;=_~?#/"; | 
| 80 |  |  | 
| 81 |  | #ifndef HAVE_STRNDUP | 
| 82 |  | #define strndup ncstrndup | 
| 83 |  | /* Not all systems have strndup, so provide one*/ | 
| 84 |  | char* | 
| 85 |  | ncstrndup(const char* s, size_t len) | 
| 86 |  | { | 
| 87 |  |     char* dup; | 
| 88 |  |     if(s == NULL) return NULL; | 
| 89 |  |     dup = (char*)malloc(len+1); | 
| 90 |  |     if(dup == NULL) return NULL; | 
| 91 |  |     memcpy((void*)dup,s,len); | 
| 92 |  |     dup[len] = '\0'; | 
| 93 |  |     return dup; | 
| 94 |  | } | 
| 95 |  | #endif | 
| 96 |  | /* Forward */ | 
| 97 |  | static int collectprefixparams(char* text, char** nextp); | 
| 98 |  | static void freestringlist(NClist* list); | 
| 99 |  | static int ncfind(NClist* params, const char* key); | 
| 100 |  | static char* nclocate(char* p, const char* charlist); | 
| 101 |  | static int parselist(const char* ptext, NClist* list); | 
| 102 |  | static int unparselist(const NClist* vec, const char* prefix, int encode, NCbytes*); | 
| 103 |  | static int ensurefraglist(NCURI* uri); | 
| 104 |  | static int ensurequerylist(NCURI* uri); | 
| 105 |  | static void removedups(NClist* list); | 
| 106 |  |  | 
| 107 |  | /**************************************************/ | 
| 108 |  | /* | 
| 109 |  | A note about parameter support: | 
| 110 |  | In the original url format for opendap (dap2), client parameters were | 
| 111 |  | assumed to be one or more instances of bracketed pairs: e.g | 
| 112 |  |     "[...][...]...". | 
| 113 |  | These were assumed to be placed at the front of the url.  In this newer | 
| 114 |  | version, the parameters may be encoded after a trailing # character each | 
| 115 |  | separated by ampersand (&).  For back compatibility, the bracketed | 
| 116 |  | parameter form is supported. However, if ncuribuild is used, all | 
| 117 |  | parameters will be converted to the | 
| 118 |  |     #...&...& format. | 
| 119 |  | In any case, each parameter in turn is assumed to be a of the form | 
| 120 |  | <name>=<value> or <name>; e.g. #x=y&z&a=b&w. | 
| 121 |  | If the same parameter is specified more than once, then the first | 
| 122 |  | occurrence is used; this is so that is possible to forcibly override | 
| 123 |  | user specified parameters by prefixing. | 
| 124 |  | IMPORTANT: the client parameter string is assumed to have blanks compressed out. | 
| 125 |  | */ | 
| 126 |  |  | 
| 127 |  | /**************************************************/ | 
| 128 |  |  | 
| 129 |  | /* Do a simple uri parse: return NC_NOERR if success, NC_EXXX if failed */ | 
| 130 |  | int | 
| 131 |  | ncuriparse(const char* uri0, NCURI** durip) | 
| 132 | 339 | { | 
| 133 | 339 |     int ret = NC_NOERR; | 
| 134 | 339 |     NCURI tmp; | 
| 135 | 339 |     char* p; | 
| 136 | 339 |     char* q; | 
| 137 | 339 |     int isfile; | 
| 138 | 339 |     int hashost; | 
| 139 | 339 |     char* uri = NULL; | 
| 140 | 339 |     NCURI* duri = NULL; | 
| 141 | 339 |     char* prefix = NULL; | 
| 142 | 339 |     char* next = NULL; | 
| 143 | 339 |     NClist* params = nclistnew(); | 
| 144 | 339 |     NClist* querylist = nclistnew(); | 
| 145 | 339 |     size_t len0; | 
| 146 | 339 |     char pathchar; | 
| 147 |  |  | 
| 148 | 339 |     if(uri0 == NULL) | 
| 149 | 0 |   {THROW(NC_EURL);} | 
| 150 |  |  | 
| 151 | 339 |     len0 = strlen(uri0); | 
| 152 | 339 |     if(len0 == 0) | 
| 153 | 0 |   {THROW(NC_EURL);} | 
| 154 |  |  | 
| 155 |  |     /* Create a local NCURI instance to hold | 
| 156 |  |        pointers into the parsed string | 
| 157 |  |     */ | 
| 158 | 339 |     memset(&tmp,0,sizeof(tmp)); | 
| 159 |  |  | 
| 160 |  |     /* make mutable copy. Add some extra space | 
| 161 |  |        because we will need to null terminate the host section | 
| 162 |  |        without losing the first character of the path section. | 
| 163 |  |     */ | 
| 164 | 339 |     uri = (char*)malloc(len0+1+1); /* +2 for nul term and for host section terminator */ | 
| 165 | 339 |     if(uri == NULL) | 
| 166 | 0 |   {THROW(NC_ENOMEM);} | 
| 167 |  |     /* Safe because we allocated enough space right above (and */ | 
| 168 |  |     /* `strdup` isn't usable because we need "one more char"). */ | 
| 169 | 339 |     strcpy(uri,uri0); | 
| 170 |  |  | 
| 171 |  |     /* Walk the uri and do the following: | 
| 172 |  |   1. remove leading and trailing whitespace | 
| 173 |  |   2. convert all '\\' -> '\' (Temp hack to remove escape characters | 
| 174 |  |                                     inserted by Windows or MinGW) | 
| 175 |  |     */ | 
| 176 | 339 |     p = uri; | 
| 177 | 339 |     while(*p == ' ') p++; | 
| 178 | 4.40k |     for(q=uri;*p;p++) {if((*p == '\\' && p[1] == '\\')) {continue;} else {*q++ = *p;}} | 
| 179 | 339 |     while((q - 1) >= uri && *(q - 1) == ' ') q--; | 
| 180 | 339 |     *q = '\0'; | 
| 181 |  |  | 
| 182 | 339 |     p = uri; | 
| 183 |  |  | 
| 184 |  |     /* break up the url into coarse pieces */ | 
| 185 | 339 |     if(*p == LBRACKET) { | 
| 186 | 0 |         prefix = p; | 
| 187 | 0 |         ret = collectprefixparams(p,&next); /* collect the prefix; convert to & form */ | 
| 188 | 0 |         if(ret != NC_NOERR) | 
| 189 | 0 |             {THROW(NC_EURL);} | 
| 190 | 0 |          p = next; | 
| 191 | 339 |     } else { | 
| 192 | 339 |   prefix = NULL; | 
| 193 | 339 |     } | 
| 194 | 339 |     tmp.uri = p; /* will be the core */ | 
| 195 |  |     /* Skip past the core of the url */ | 
| 196 | 339 |     next = nclocate(p,"?#"); | 
| 197 | 339 |     if(next != NULL) { | 
| 198 | 0 |   int c = *next; | 
| 199 | 0 |   terminate(next); | 
| 200 | 0 |   next++; | 
| 201 | 0 |   if(c == '?') { | 
| 202 | 0 |       tmp.query = next; | 
| 203 | 0 |       next = nclocate(next,"#"); | 
| 204 | 0 |       if(next == NULL) | 
| 205 | 0 |     tmp.fragment = NULL; | 
| 206 | 0 |       else { | 
| 207 | 0 |     terminate(next); | 
| 208 | 0 |     next++; | 
| 209 | 0 |           tmp.fragment = next; | 
| 210 | 0 |       } | 
| 211 | 0 |   } else { /*c == '#'*/ | 
| 212 | 0 |       tmp.fragment = next; | 
| 213 | 0 |   } | 
| 214 | 0 |     } | 
| 215 |  |  | 
| 216 |  |     /* Parse the prefix parameters */ | 
| 217 | 339 |     if(prefix != NULL) { | 
| 218 | 0 |         if(parselist(prefix,params) != NC_NOERR) | 
| 219 | 0 |             {THROW(NC_EURL);} | 
| 220 | 0 |     } | 
| 221 |  |     /* Parse the fragment parameters into the params list */ | 
| 222 | 339 |     if(tmp.fragment != NULL) { | 
| 223 | 0 |         if(parselist(tmp.fragment,params) != NC_NOERR) | 
| 224 | 0 |             {THROW(NC_EURL);} | 
| 225 | 0 |     } | 
| 226 |  |     /* Remove duplicates */ | 
| 227 | 339 |     removedups(params); | 
| 228 | 339 |     tmp.fraglist = params; | 
| 229 | 339 |     params = NULL; | 
| 230 |  |  | 
| 231 |  |     /* Parse the query */ | 
| 232 | 339 |     if(tmp.query != NULL) { | 
| 233 | 0 |         if(parselist(tmp.query,querylist) != NC_NOERR) | 
| 234 | 0 |             {THROW(NC_EURL);} | 
| 235 | 0 |   tmp.querylist = querylist; | 
| 236 | 0 |   querylist = NULL; | 
| 237 | 0 |     } | 
| 238 |  |  | 
| 239 |  |     /* Now parse the core of the url */ | 
| 240 | 339 |     p = tmp.uri; | 
| 241 |  |  | 
| 242 |  |     /* Mark the protocol */ | 
| 243 | 339 |     tmp.protocol = p; | 
| 244 | 339 |     p = strchr(p,':'); | 
| 245 | 339 |     if(!p) | 
| 246 | 339 |   {THROW(NC_EURL);} | 
| 247 | 0 |     terminate(p); /*overwrite colon*/ | 
| 248 | 0 |     p++; /* skip the colon */ | 
| 249 | 0 |     if(strlen(tmp.protocol)==0) | 
| 250 | 0 |   {THROW(NC_EURL);} | 
| 251 |  |     /* | 
| 252 |  |        The legal formats for file: urls are a problem since | 
| 253 |  |        many variants are often accepted. | 
| 254 |  |        By RFC, the proper general format is: file://host/path, | 
| 255 |  |        where the 'host' can be omitted and defaults to 'localhost'. | 
| 256 |  |        and the path includes the leading '/'. | 
| 257 |  |        So, assuming no host, the format is: "file:///path". | 
| 258 |  |        Some implementations, however, ignore the host, and allow | 
| 259 |  |        the format: file:/path. | 
| 260 |  |        We also simplify things by assuming the host part is always empty. | 
| 261 |  |        which means we can have file:///path, but not file://..../path. | 
| 262 |  |        Note also in all cases, the leading '/' is considered part of the path, | 
| 263 |  |        which is then assumed to be an absolute path. But also note that | 
| 264 |  |        the windows drive letter has to be taken into account. Our rule is that | 
| 265 |  |        if the path looks like D:..., | 
| 266 |  |        where D is a single alphabetic letter (a-z or A-Z), | 
| 267 |  |        then it is a windows path and can be use in place of a /path. | 
| 268 |  |        Note also that it is desirable to support relative paths even | 
| 269 |  |        though the RFC technically does not allow this. This will occur | 
| 270 |  |        if the form is file://path where path does not start with '/'. | 
| 271 |  |        The rules implemented here (for file:) are then as follows | 
| 272 |  |        1. file:D:... : assume D: is a windows drive letter and treat D:... as the path | 
| 273 |  |        2. file:/X, where X does not start with a slash: treat /X as the path. | 
| 274 |  |        3. file://D:... : assume D: is a windows drive letter and treat as the path | 
| 275 |  |        4. file:///X, where X does not start with a slash: treat /X as the path. | 
| 276 |  |        5. file://X, where X does not start with a slash: treat X as the | 
| 277 |  |           relative path. | 
| 278 |  |        All other cases are disallowed. | 
| 279 |  |     */ | 
| 280 |  |  | 
| 281 | 0 |     isfile = (strcmp(tmp.protocol,"file")==0); | 
| 282 | 0 |     if(isfile) { | 
| 283 | 0 |   size_t l = strlen(p); /* to test if we have enough characters */ | 
| 284 | 0 |   hashost = 0; /* always */ | 
| 285 | 0 |   if(l >= 2 && p[1] == ':' && strchr(DRIVELETTERS,p[0]) != NULL) { /* case 1 */ | 
| 286 | 0 |       ; /* p points to the start of the path */ | 
| 287 | 0 |         } else if(l >= 2 && p[0] == '/' && p[1] != '/') { /* case 2 */ | 
| 288 | 0 |       ; /* p points to the start of the path */ | 
| 289 | 0 |   } else if(l >= 4 && p[0] == '/' && p[1] == '/' | 
| 290 | 0 |     && p[3] == ':' && strchr(DRIVELETTERS,p[2]) != NULL) { /* case 3 */ | 
| 291 | 0 |       p = p+2; /* points to the start of the windows path */ | 
| 292 | 0 |         } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] == '/' && p[3] != '/') { /* case 4 */ | 
| 293 | 0 |       p += 2; /* points to the start of the path */ | 
| 294 | 0 |         } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] != '/') { /* case 5 */ | 
| 295 | 0 |       p += 2; /* points to the start of the path */ | 
| 296 | 0 |         } else /* everything else is illegal */ | 
| 297 | 0 |       {THROW(NC_EURL);} | 
| 298 | 0 |     } else { | 
| 299 | 0 |         if(p[0] != '/' || p[1] != '/') /* must be proto:// */ | 
| 300 | 0 |       {THROW(NC_EURL);} | 
| 301 | 0 |   p += 2; | 
| 302 | 0 |         hashost = 1; /* Assume we have a hostname */ | 
| 303 | 0 |     } | 
| 304 | 0 |     if(!hashost) { | 
| 305 | 0 |         tmp.path = p; | 
| 306 | 0 |   pathchar = EOFCHAR; | 
| 307 | 0 |     } else { /* assume there should be a host section */ | 
| 308 |  |   /* We already extracted the query and/or fragment sections above, | 
| 309 |  |            so locate the end of the host section and therefore the start | 
| 310 |  |            of the path. | 
| 311 |  |         */ | 
| 312 | 0 |   tmp.host = p; | 
| 313 | 0 |         p  = nclocate(p,"/"); | 
| 314 | 0 |   if(p == NULL) { /* no path */ | 
| 315 | 0 |       tmp.path = NULL; /* default */ | 
| 316 | 0 |       pathchar = EOFCHAR; | 
| 317 | 0 |   } else { | 
| 318 | 0 |       tmp.path = p; /* save ptr to rest of the path */ | 
| 319 | 0 |       pathchar = *p; /* save leading char of the path */ | 
| 320 | 0 |       terminate(p); /* overwrite the leading char of the path; restored below */ | 
| 321 | 0 |   } | 
| 322 | 0 |     } | 
| 323 |  |     /* Nullify tmp.host for consistency */ | 
| 324 | 0 |     if(tmp.host != NULL && strlen(tmp.host)==0) {tmp.host = NULL;} | 
| 325 |  | 
 | 
| 326 | 0 |     if(tmp.host != NULL) {/* Parse the host section */ | 
| 327 | 0 |         char* pp; | 
| 328 |  |   /* Check for leading user:pwd@ */ | 
| 329 | 0 |         char* newhost = strchr(tmp.host,'@'); | 
| 330 | 0 |         if(newhost != NULL) { | 
| 331 | 0 |       if(newhost == tmp.host) | 
| 332 | 0 |     {THROW(NC_EURL);} /* we have proto://@ */ | 
| 333 | 0 |       terminate(newhost); /* overwrite '@' */ | 
| 334 | 0 |       newhost++; /* should point past usr+pwd */ | 
| 335 | 0 |       tmp.user = tmp.host; | 
| 336 |  |       /* Break user+pwd into two pieces */ | 
| 337 | 0 |       pp = strchr(tmp.user,':'); | 
| 338 | 0 |       if(pp == NULL) | 
| 339 | 0 |     {THROW(NC_EURL);} /* we have user only */ | 
| 340 | 0 |       terminate(pp); /* overwrite ':' */ | 
| 341 | 0 |       pp++; | 
| 342 | 0 |       if(strlen(tmp.user)==0) | 
| 343 | 0 |     {THROW(NC_EURL);} /* we have empty user */ | 
| 344 | 0 |       if(strlen(pp)==0) | 
| 345 | 0 |     {THROW(NC_EURL);} /* we have empty password */ | 
| 346 | 0 |       tmp.password = pp; | 
| 347 | 0 |       tmp.host = newhost; | 
| 348 | 0 |   } | 
| 349 |  |   /* Breakup host into host + port */ | 
| 350 | 0 |   pp = tmp.host; | 
| 351 | 0 |         pp = strchr(pp,':'); | 
| 352 | 0 |         if(pp != NULL) { /* there is a port */ | 
| 353 | 0 |       terminate(pp); /* overwrite ':' */ | 
| 354 | 0 |       pp++; /* skip colon */ | 
| 355 | 0 |       if(strlen(tmp.host) == 0) | 
| 356 | 0 |     {THROW(NC_EURL);} /* empty host */ | 
| 357 | 0 |       if(strlen(pp)==0) | 
| 358 | 0 |     {THROW(NC_EURL);} /* empty port */ | 
| 359 | 0 |       tmp.port = pp; | 
| 360 |  |       /* The port must look something like a number */ | 
| 361 | 0 |       for(pp=tmp.port;*pp;pp++) { | 
| 362 | 0 |           if(strchr("0123456789-",*pp) == NULL) | 
| 363 | 0 |         {THROW(NC_EURL);}  /* probably not a real port, fail */ | 
| 364 | 0 |       } | 
| 365 | 0 |   } /* else no port */ | 
| 366 | 0 |     } | 
| 367 |  |  | 
| 368 |  |     /* Fill in duri from tmp */ | 
| 369 | 0 |     duri = (NCURI*)calloc(1,sizeof(NCURI)); | 
| 370 | 0 |     if(duri == NULL) | 
| 371 | 0 |       {THROW(NC_ENOMEM);} | 
| 372 |  |     /* save original uri */ | 
| 373 | 0 |     duri->uri = strdup(uri0); | 
| 374 | 0 |     duri->protocol = nulldup(tmp.protocol); | 
| 375 |  |     /* before saving, we need to decode the user+pwd */ | 
| 376 | 0 |     duri->user = NULL; | 
| 377 | 0 |     duri->password = NULL; | 
| 378 | 0 |     if(tmp.user != NULL) | 
| 379 | 0 |         duri->user = ncuridecode(tmp.user); | 
| 380 | 0 |     if(tmp.password != NULL) | 
| 381 | 0 |         duri->password = ncuridecode(tmp.password); | 
| 382 | 0 |     duri->host = nulldup(tmp.host); | 
| 383 | 0 |     duri->port = nulldup(tmp.port); | 
| 384 | 0 |     if(tmp.path != NULL) { | 
| 385 |  |   /* We need to add back the previously overwritten path lead char (if necessary); | 
| 386 |  |            this must be done after all host section related pieces have been captured */ | 
| 387 | 0 |   if(pathchar != EOFCHAR) | 
| 388 | 0 |       *tmp.path = pathchar; | 
| 389 | 0 |         duri->path = nulldup(tmp.path); | 
| 390 | 0 |     } | 
| 391 | 0 |     duri->query = NULL; /* let ensurequery fix this */ | 
| 392 | 0 |     duri->fragment = NULL; /* let ensurefrag fix this */ | 
| 393 | 0 |     duri->fraglist = tmp.fraglist; tmp.fraglist = NULL; | 
| 394 | 0 |     duri->querylist = tmp.querylist; tmp.querylist = NULL; | 
| 395 |  |  | 
| 396 |  |     /* make sure query and fragment strings are defined */ | 
| 397 | 0 |     ensurequerylist(duri); | 
| 398 | 0 |     ensurefraglist(duri); | 
| 399 |  | 
 | 
| 400 | 0 |     if(durip) | 
| 401 | 0 |       *durip = duri; | 
| 402 | 0 |     else | 
| 403 | 0 |       free(duri); | 
| 404 |  | 
 | 
| 405 |  | #ifdef NCXDEBUG | 
| 406 |  |   { | 
| 407 |  |         fprintf(stderr,"duri:"); | 
| 408 |  |   fprintf(stderr," protocol=|%s|",FIX(duri->protocol)); | 
| 409 |  |   fprintf(stderr," user=|%s|",FIX(duri->user)); | 
| 410 |  |   fprintf(stderr," password=|%s|",FIX(duri->password)); | 
| 411 |  |   fprintf(stderr," host=|%s|",FIX(duri->host)); | 
| 412 |  |   fprintf(stderr," port=|%s|",FIX(duri->port)); | 
| 413 |  |   fprintf(stderr," path=|%s|",FIX(duri->path)); | 
| 414 |  |   fprintf(stderr," query=|%s|",FIX(duri->query)); | 
| 415 |  |   fprintf(stderr," fragment=|%s|",FIX(duri->fragment)); | 
| 416 |  |         fprintf(stderr,"\n"); | 
| 417 |  |     } | 
| 418 |  | #endif | 
| 419 |  | 
 | 
| 420 | 339 | done: | 
| 421 | 339 |     if(uri != NULL) | 
| 422 | 339 |       free(uri); | 
| 423 |  |  | 
| 424 | 339 |     freestringlist(params); | 
| 425 | 339 |     freestringlist(querylist); | 
| 426 | 339 |     if(tmp.fraglist) | 
| 427 | 339 |       nclistfreeall(tmp.fraglist); | 
| 428 | 339 |     if(tmp.querylist) | 
| 429 | 0 |       nclistfreeall(tmp.querylist); | 
| 430 |  |  | 
| 431 | 339 |     return ret; | 
| 432 | 0 | } | 
| 433 |  |  | 
| 434 |  | static void | 
| 435 |  | freestringlist(NClist* list) | 
| 436 | 678 | { | 
| 437 | 678 |     if(list != NULL) { | 
| 438 | 339 |   size_t i; | 
| 439 | 339 |   for(i=0;i<nclistlength(list);i++) { | 
| 440 | 0 |       void* p = nclistget(list,i); | 
| 441 | 0 |       nullfree(p); | 
| 442 | 0 |   } | 
| 443 | 339 |         nclistfree(list); | 
| 444 | 339 |     } | 
| 445 | 678 | } | 
| 446 |  |  | 
| 447 |  | #if 0 | 
| 448 |  | static void | 
| 449 |  | freestringvec(char** list) | 
| 450 |  | { | 
| 451 |  |     if(list != NULL) { | 
| 452 |  |   char** p; | 
| 453 |  |         for(p=list;*p;p++) {nullfree(*p);} | 
| 454 |  |   nullfree(list); | 
| 455 |  |     } | 
| 456 |  | } | 
| 457 |  | #endif | 
| 458 |  |  | 
| 459 |  | void | 
| 460 |  | ncurifree(NCURI* duri) | 
| 461 | 339 | { | 
| 462 | 339 |     if(duri == NULL) return; | 
| 463 | 0 |     nullfree(duri->uri); | 
| 464 | 0 |     nullfree(duri->protocol); | 
| 465 | 0 |     nullfree(duri->user); | 
| 466 | 0 |     nullfree(duri->password); | 
| 467 | 0 |     nullfree(duri->host); | 
| 468 | 0 |     nullfree(duri->port); | 
| 469 | 0 |     nullfree(duri->path); | 
| 470 | 0 |     nullfree(duri->query); | 
| 471 | 0 |     nullfree(duri->fragment); | 
| 472 | 0 |     nclistfreeall(duri->querylist); | 
| 473 | 0 |     nclistfreeall(duri->fraglist); | 
| 474 | 0 |     free(duri); | 
| 475 | 0 | } | 
| 476 |  |  | 
| 477 |  | /* Replace the protocol */ | 
| 478 |  | int | 
| 479 |  | ncurisetprotocol(NCURI* duri,const char* protocol) | 
| 480 | 0 | { | 
| 481 | 0 |     nullfree(duri->protocol); | 
| 482 | 0 |     duri->protocol = strdup(protocol); | 
| 483 | 0 |     return (NC_NOERR); | 
| 484 | 0 | } | 
| 485 |  |  | 
| 486 |  | /* Replace the host */ | 
| 487 |  | int | 
| 488 |  | ncurisethost(NCURI* duri,const char* host) | 
| 489 | 0 | { | 
| 490 | 0 |     nullfree(duri->host); | 
| 491 | 0 |     duri->host = strdup(host); | 
| 492 | 0 |     return (NC_NOERR); | 
| 493 | 0 | } | 
| 494 |  |  | 
| 495 |  | /* Replace the path */ | 
| 496 |  | int | 
| 497 |  | ncurisetpath(NCURI* duri,const char* newpath) | 
| 498 | 0 | { | 
| 499 | 0 |     nullfree(duri->path); | 
| 500 | 0 |     duri->path = strdup(newpath); | 
| 501 | 0 |     return (NC_NOERR); | 
| 502 | 0 | } | 
| 503 |  |  | 
| 504 |  | /* Replace the query */ | 
| 505 |  | int | 
| 506 |  | ncurisetquery(NCURI* duri,const char* query) | 
| 507 | 0 | { | 
| 508 | 0 |     int ret = NC_NOERR; | 
| 509 | 0 |     nclistfreeall((NClist*)duri->querylist); | 
| 510 | 0 |     nullfree(duri->query); | 
| 511 | 0 |     duri->query = NULL; | 
| 512 | 0 |     duri->querylist = NULL; | 
| 513 | 0 |     if(query != NULL && strlen(query) > 0) { | 
| 514 | 0 |         duri->query = strdup(query); | 
| 515 | 0 |   ensurequerylist(duri); | 
| 516 | 0 |     } | 
| 517 | 0 |     return ret; | 
| 518 | 0 | } | 
| 519 |  |  | 
| 520 |  | /* Replace the fragments*/ | 
| 521 |  | int | 
| 522 |  | ncurisetfragments(NCURI* duri,const char* fragments) | 
| 523 | 0 | { | 
| 524 | 0 |     int ret = NC_NOERR; | 
| 525 | 0 |     nclistfreeall((NClist*)duri->fraglist); | 
| 526 | 0 |     nullfree(duri->fragment); | 
| 527 | 0 |     duri->fragment = NULL; | 
| 528 | 0 |     duri->fraglist = NULL; | 
| 529 | 0 |     if(fragments != NULL && strlen(fragments) > 0) { | 
| 530 | 0 |   duri->fragment = strdup(fragments); | 
| 531 | 0 |   ensurefraglist(duri); | 
| 532 | 0 |     } | 
| 533 | 0 |     return ret; | 
| 534 | 0 | } | 
| 535 |  |  | 
| 536 |  | /* Replace the path */ | 
| 537 |  | int | 
| 538 |  | ncurirebuild(NCURI* duri) | 
| 539 | 0 | { | 
| 540 | 0 |     char* surl = ncuribuild(duri,NULL,NULL,NCURIALL); | 
| 541 | 0 |     nullfree(duri->uri); | 
| 542 | 0 |     duri->uri = surl; | 
| 543 | 0 |     return (NC_NOERR); | 
| 544 | 0 | } | 
| 545 |  |  | 
| 546 |  | /* Replace a specific fragment key*/ | 
| 547 |  | int | 
| 548 |  | ncurisetfragmentkey(NCURI* duri, const char* key, const char* value) | 
| 549 | 0 | { | 
| 550 | 0 |     int ret = NC_NOERR; | 
| 551 | 0 |     int pos = -1; | 
| 552 |  | 
 | 
| 553 | 0 |     ensurefraglist(duri); | 
| 554 | 0 |     pos = ncfind(duri->fraglist, key); | 
| 555 | 0 |     if(pos < 0) { /* does not exist */ | 
| 556 | 0 |   if(duri->fraglist == NULL) duri->fraglist = nclistnew(); | 
| 557 | 0 |   nclistpush(duri->fraglist,strdup(key)); | 
| 558 | 0 |   nclistpush(duri->fraglist,strdup(value)); | 
| 559 | 0 |     } else { | 
| 560 | 0 |         nullfree(nclistget(duri->fraglist,(size_t)pos+1)); | 
| 561 | 0 |         nclistset(duri->fraglist,(size_t)pos+1,strdup(value)); | 
| 562 | 0 |     } | 
| 563 |  |     /* Rebuild the fragment */ | 
| 564 | 0 |     nullfree(duri->fragment); duri->fragment = NULL; | 
| 565 | 0 |     if((ret = ensurefraglist(duri))) goto done; | 
| 566 | 0 | done: | 
| 567 | 0 |     return ret; | 
| 568 | 0 | } | 
| 569 |  |  | 
| 570 |  | /* Replace or add a specific fragment key*/ | 
| 571 |  | int | 
| 572 |  | ncuriappendfragmentkey(NCURI* duri,const char* key, const char* value) | 
| 573 | 0 | { | 
| 574 | 0 |     int ret = NC_NOERR; | 
| 575 | 0 |     int pos = -1; | 
| 576 |  | 
 | 
| 577 | 0 |     ensurefraglist(duri); | 
| 578 | 0 |     pos = ncfind(duri->fraglist, key); | 
| 579 | 0 |     if(pos < 0) { /* does not exist */ | 
| 580 | 0 |   nclistpush((NClist*)duri->fraglist,strdup(key)); | 
| 581 | 0 |   nclistpush((NClist*)duri->fraglist,nulldup(value)); | 
| 582 | 0 |     } else { | 
| 583 | 0 |         nullfree(nclistget(duri->fraglist,(size_t)pos+1)); | 
| 584 | 0 |   nclistset(duri->fraglist,(size_t)pos+1,nulldup(value)); | 
| 585 | 0 |     } | 
| 586 |  |     /* Rebuild the fragment */ | 
| 587 | 0 |     nullfree(duri->fraglist); duri->fraglist = NULL; | 
| 588 | 0 |     if((ret=ensurefraglist(duri))) goto done; | 
| 589 | 0 | done: | 
| 590 | 0 |     return ret; | 
| 591 | 0 | } | 
| 592 |  |  | 
| 593 |  | /* Replace a specific query key*/ | 
| 594 |  | int | 
| 595 |  | ncurisetquerykey(NCURI* duri,const char* key, const char* value) | 
| 596 | 0 | { | 
| 597 | 0 |     int ret = NC_NOERR; | 
| 598 | 0 |     int pos = -1; | 
| 599 |  | 
 | 
| 600 | 0 |     ensurequerylist(duri); | 
| 601 | 0 |     pos = ncfind(duri->querylist, key); | 
| 602 | 0 |     if(pos < 0) { /* does not exist */ | 
| 603 | 0 |   if(duri->querylist == NULL) duri->querylist = nclistnew(); | 
| 604 | 0 |   nclistpush(duri->querylist,key); | 
| 605 | 0 |   nclistpush(duri->querylist,value); | 
| 606 | 0 |     } else { | 
| 607 | 0 |         nullfree(nclistget(duri->querylist,(size_t)pos+1)); | 
| 608 | 0 |         nclistset(duri->querylist,(size_t)pos+1,strdup(value)); | 
| 609 | 0 |     } | 
| 610 |  |     /* Rebuild the query */ | 
| 611 | 0 |     nullfree(duri->query); duri->query = NULL; | 
| 612 | 0 |     if((ret = ensurequerylist(duri))) goto done; | 
| 613 | 0 | done: | 
| 614 | 0 |     return ret; | 
| 615 | 0 | } | 
| 616 |  |  | 
| 617 |  | /* Replace or add a specific query key*/ | 
| 618 |  | int | 
| 619 |  | ncuriappendquerykey(NCURI* duri,const char* key, const char* value) | 
| 620 | 0 | { | 
| 621 | 0 |     int ret = NC_NOERR; | 
| 622 | 0 |     int pos = -1; | 
| 623 |  | 
 | 
| 624 | 0 |     ensurequerylist(duri); | 
| 625 | 0 |     pos = ncfind(duri->querylist, key); | 
| 626 | 0 |     if(pos < 0) { /* does not exist */ | 
| 627 | 0 |   nclistpush((NClist*)duri->querylist,strdup(key)); | 
| 628 | 0 |   nclistpush((NClist*)duri->querylist,nulldup(value)); | 
| 629 | 0 |     } else { | 
| 630 | 0 |         nullfree(nclistget(duri->querylist,(size_t)pos+1)); | 
| 631 | 0 |   nclistset(duri->querylist,(size_t)pos+1,nulldup(value)); | 
| 632 | 0 |     } | 
| 633 |  |     /* Rebuild the query */ | 
| 634 | 0 |     nullfree(duri->querylist); duri->querylist = NULL; | 
| 635 | 0 |     if((ret=ensurequerylist(duri))) goto done; | 
| 636 | 0 | done: | 
| 637 | 0 |     return ret; | 
| 638 | 0 | } | 
| 639 |  |  | 
| 640 |  | #if 0 | 
| 641 |  | /* Replace the constraints */ | 
| 642 |  | int | 
| 643 |  | ncurisetconstraints(NCURI* duri,const char* constraints) | 
| 644 |  | { | 
| 645 |  |     char* proj = NULL; | 
| 646 |  |     char* select = NULL; | 
| 647 |  |     const char* p; | 
| 648 |  |  | 
| 649 |  |     if(duri->constraint != NULL) free(duri->constraint); | 
| 650 |  |     if(duri->projection != NULL) free(duri->projection); | 
| 651 |  |     if(duri->selection != NULL) free(duri->selection); | 
| 652 |  |     duri->constraint = NULL; | 
| 653 |  |     duri->projection = NULL; | 
| 654 |  |     duri->selection = NULL; | 
| 655 |  |  | 
| 656 |  |     if(constraints == NULL || strlen(constraints)==0) return (NC_ECONSTRAINTS); | 
| 657 |  |  | 
| 658 |  |     duri->constraint = nulldup(constraints); | 
| 659 |  |     if(*duri->constraint == '?') | 
| 660 |  |   nclshift1(duri->constraint); | 
| 661 |  |  | 
| 662 |  |     p = duri->constraint; | 
| 663 |  |     proj = (char*) p; | 
| 664 |  |     select = strchr(proj,'&'); | 
| 665 |  |     if(select != NULL) { | 
| 666 |  |         size_t plen = (size_t)(select - proj); | 
| 667 |  |   if(plen == 0) { | 
| 668 |  |       proj = NULL; | 
| 669 |  |   } else { | 
| 670 |  |       proj = (char*)malloc(plen+1); | 
| 671 |  |       memcpy((void*)proj,p,plen); | 
| 672 |  |       proj[plen] = EOFCHAR; | 
| 673 |  |   } | 
| 674 |  |   select = nulldup(select); | 
| 675 |  |     } else { | 
| 676 |  |   proj = nulldup(proj); | 
| 677 |  |   select = NULL; | 
| 678 |  |     } | 
| 679 |  |     duri->projection = proj; | 
| 680 |  |     duri->selection = select; | 
| 681 |  |     return NC_NOERR; | 
| 682 |  | } | 
| 683 |  | #endif | 
| 684 |  |  | 
| 685 |  | /* Construct a complete NC URI. | 
| 686 |  |    Optionally with the constraints. | 
| 687 |  |    Optionally with the user parameters. | 
| 688 |  |    Caller frees returned string. | 
| 689 |  |    Optionally encode the pieces. | 
| 690 |  | */ | 
| 691 |  |  | 
| 692 |  | char* | 
| 693 |  | ncuribuild(NCURI* duri, const char* prefix, const char* suffix, int flags) | 
| 694 | 0 | { | 
| 695 | 0 |     char* newuri = NULL; | 
| 696 | 0 |     NCbytes* buf = ncbytesnew(); | 
| 697 | 0 |     const int encodepath = (flags&NCURIENCODEPATH ? 1 : 0); | 
| 698 | 0 |     const int encodequery = (flags&NCURIENCODEQUERY ? 1 : 0); | 
| 699 |  | 
 | 
| 700 | 0 |     if(prefix != NULL) | 
| 701 | 0 |   ncbytescat(buf,prefix); | 
| 702 |  | 
 | 
| 703 | 0 |     ncbytescat(buf,duri->protocol); | 
| 704 | 0 |     ncbytescat(buf,"://"); /* this will produce file:///... */ | 
| 705 |  | 
 | 
| 706 | 0 |     if((flags & NCURIPWD) && duri->user != NULL && duri->password != NULL) { | 
| 707 |  |   /* The user and password must be encoded */ | 
| 708 | 0 |         char* encoded = ncuriencodeonly(duri->user,userpwdallow); | 
| 709 | 0 |   ncbytescat(buf,encoded); | 
| 710 | 0 |   nullfree(encoded); | 
| 711 | 0 |   ncbytescat(buf,":"); | 
| 712 | 0 |   encoded = ncuriencodeonly(duri->password,userpwdallow); | 
| 713 | 0 |   ncbytescat(buf,encoded); | 
| 714 | 0 |   nullfree(encoded); | 
| 715 | 0 |   ncbytescat(buf,"@"); | 
| 716 | 0 |     } | 
| 717 | 0 |     if(duri->host != NULL) ncbytescat(buf,duri->host); | 
| 718 | 0 |     if(duri->port != NULL) { | 
| 719 | 0 |   ncbytescat(buf,":"); | 
| 720 | 0 |   ncbytescat(buf,duri->port); | 
| 721 | 0 |     } | 
| 722 | 0 |     if((flags & NCURIPATH)) { | 
| 723 | 0 |   if(duri->path == NULL) | 
| 724 | 0 |       ncbytescat(buf,"/"); | 
| 725 | 0 |   else if(encodepath) { | 
| 726 | 0 |       char* encoded = ncuriencodeonly(duri->path,pathallow); | 
| 727 | 0 |       ncbytescat(buf,encoded); | 
| 728 | 0 |       nullfree(encoded); | 
| 729 | 0 |   } else | 
| 730 | 0 |       ncbytescat(buf,duri->path); | 
| 731 | 0 |     } | 
| 732 |  |  | 
| 733 |  |     /* The suffix is intended to some kind of path extension (e.g. .dds) | 
| 734 |  |        so insert here | 
| 735 |  |     */ | 
| 736 | 0 |     if(suffix != NULL) | 
| 737 | 0 |   ncbytescat(buf,suffix); | 
| 738 |  |  | 
| 739 |  |     /* The query and the querylist are assumed to be unencoded */ | 
| 740 | 0 |     if(flags & NCURIQUERY) { | 
| 741 | 0 |   ensurequerylist(duri); | 
| 742 | 0 |         if(duri->query != NULL) { | 
| 743 | 0 |             ncbytescat(buf,"?"); | 
| 744 | 0 |       if(encodequery) { | 
| 745 | 0 |           char* encoded = ncuriencodeonly(duri->query,queryallow); | 
| 746 | 0 |           ncbytescat(buf,encoded); | 
| 747 | 0 |           nullfree(encoded); | 
| 748 | 0 |       } else | 
| 749 | 0 |           ncbytescat(buf,duri->query); | 
| 750 | 0 |   } | 
| 751 | 0 |     } | 
| 752 | 0 |     if(flags & NCURIFRAG) { | 
| 753 | 0 |   ensurefraglist(duri); | 
| 754 | 0 |         if(duri->fragment != NULL) { | 
| 755 | 0 |             ncbytescat(buf,"#"); | 
| 756 | 0 |       ncbytescat(buf,duri->fragment); | 
| 757 | 0 |   } | 
| 758 | 0 |     } | 
| 759 | 0 |     ncbytesnull(buf); | 
| 760 | 0 |     newuri = ncbytesextract(buf); | 
| 761 | 0 |     ncbytesfree(buf); | 
| 762 | 0 |     return newuri; | 
| 763 | 0 | } | 
| 764 |  |  | 
| 765 |  |  | 
| 766 |  | const char* | 
| 767 |  | ncurifragmentlookup(NCURI* uri, const char* key) | 
| 768 | 0 | { | 
| 769 | 0 |   int i; | 
| 770 | 0 |   char* value = NULL; | 
| 771 | 0 |   if(uri == NULL || key == NULL) return NULL; | 
| 772 | 0 |   if(ensurefraglist(uri)) return NULL; | 
| 773 | 0 |   i = ncfind(uri->fraglist,key); | 
| 774 | 0 |   if(i < 0) return NULL; | 
| 775 | 0 |   value = nclistget(uri->fraglist,(size_t)i+1); | 
| 776 | 0 |   return value; | 
| 777 | 0 | } | 
| 778 |  |  | 
| 779 |  | const char* | 
| 780 |  | ncuriquerylookup(NCURI* uri, const char* key) | 
| 781 | 0 | { | 
| 782 | 0 |   int i; | 
| 783 | 0 |   char* value = NULL; | 
| 784 | 0 |   if(uri == NULL || key == NULL) return NULL; | 
| 785 | 0 |   if(ensurequerylist(uri)) return NULL; | 
| 786 | 0 |   i = ncfind(uri->querylist,key); | 
| 787 | 0 |   if(i < 0) return NULL; | 
| 788 | 0 |   value = nclistget(uri->querylist,(size_t)i+1); | 
| 789 | 0 |   return value; | 
| 790 | 0 | } | 
| 791 |  |  | 
| 792 |  | #if 0 | 
| 793 |  | /* Obtain the complete list of fragment pairs in envv format */ | 
| 794 |  | const char** | 
| 795 |  | ncurifragmentparams(NCURI* uri) | 
| 796 |  | { | 
| 797 |  |     ensurefraglist(uri); | 
| 798 |  |     return (const char**)nclistcontents(uri->fraglist; | 
| 799 |  | } | 
| 800 |  |  | 
| 801 |  | /* Obtain the complete list of query pairs in envv format */ | 
| 802 |  | const char** | 
| 803 |  | ncuriqueryparams(NCURI* uri) | 
| 804 |  | { | 
| 805 |  |     ensurequerylist(uri); | 
| 806 |  |     return (const char**)uri->querylist; | 
| 807 |  | } | 
| 808 |  |  | 
| 809 |  | int | 
| 810 |  | ncuriremoveparam(NCURI* uri, const char* key) | 
| 811 |  | { | 
| 812 |  |     char** p; | 
| 813 |  |     char** q = NULL; | 
| 814 |  |  | 
| 815 |  |     if(uri->fraglist == NULL) return NC_NOERR; | 
| 816 |  |     for(q=uri->fraglist,p=uri->fraglist;*p;) { | 
| 817 |  |         if(strcmp(key,*p)==0) { | 
| 818 |  |       p += 2; /* skip this entry */ | 
| 819 |  |   } else { | 
| 820 |  |       *q++ = *p++; /* move key */ | 
| 821 |  |       *q++ = *p++; /* move value */ | 
| 822 |  |   } | 
| 823 |  |     } | 
| 824 |  |     return NC_NOERR; | 
| 825 |  | } | 
| 826 |  | #endif | 
| 827 |  |  | 
| 828 |  |  | 
| 829 |  | /* Internal version of lookup; returns the paired index of the key; | 
| 830 |  |    case insensitive | 
| 831 |  |  */ | 
| 832 |  | static int | 
| 833 |  | ncfind(NClist* params, const char* key) | 
| 834 | 0 | { | 
| 835 | 0 |     size_t i; | 
| 836 | 0 |     if(key == NULL) return -1; | 
| 837 | 0 |     if(params == NULL) return -1; | 
| 838 | 0 |     for(i=0;i<nclistlength(params);i+=2) { | 
| 839 | 0 |         char* p=nclistget(params,(size_t)i); | 
| 840 | 0 |   if(strcasecmp(key,p)==0) return i; | 
| 841 | 0 |     } | 
| 842 | 0 |     return -1; | 
| 843 | 0 | } | 
| 844 |  |  | 
| 845 |  | #if 0 | 
| 846 |  | static void | 
| 847 |  | ncparamfree(char** params) | 
| 848 |  | { | 
| 849 |  |     char** p; | 
| 850 |  |     if(params == NULL) return; | 
| 851 |  |     for(p=params;*p;p+=2) { | 
| 852 |  |   free(*p); | 
| 853 |  |   if(p[1] != NULL) free(p[1]); | 
| 854 |  |     } | 
| 855 |  |     free(params); | 
| 856 |  | } | 
| 857 |  | #endif | 
| 858 |  |  | 
| 859 |  | /* Return the ptr to the first occurrence of | 
| 860 |  |    any char in the list. Return NULL if no | 
| 861 |  |    occurrences | 
| 862 |  | */ | 
| 863 |  | static char* | 
| 864 |  | nclocate(char* p, const char* charlist) | 
| 865 | 339 | { | 
| 866 | 4.40k |     for(;*p;p++) { | 
| 867 | 4.06k |   if(*p == '\\') p++; | 
| 868 | 4.06k |   else if(strchr(charlist,*p) != NULL) | 
| 869 | 0 |       return p; | 
| 870 | 4.06k |     } | 
| 871 | 339 |     return NULL; | 
| 872 | 339 | } | 
| 873 |  |  | 
| 874 |  | #if 0 | 
| 875 |  | /* Shift every char starting at p 1 place to the left */ | 
| 876 |  | static void | 
| 877 |  | nclshift1(char* p) | 
| 878 |  | { | 
| 879 |  |     if(p != NULL && *p != EOFCHAR) { | 
| 880 |  |   char* q = p++; | 
| 881 |  |   while((*q++=*p++)); | 
| 882 |  |     } | 
| 883 |  | } | 
| 884 |  |  | 
| 885 |  | /* Shift every char starting at p 1 place to the right */ | 
| 886 |  | static void | 
| 887 |  | ncrshift1(char* p) | 
| 888 |  | { | 
| 889 |  |     char cur; | 
| 890 |  |     cur = 0; | 
| 891 |  |     do { | 
| 892 |  |   char next = *p; | 
| 893 |  |   *p++ = cur; | 
| 894 |  |   cur = next; | 
| 895 |  |     } while(cur != 0); | 
| 896 |  |     *p = 0; /* make sure we are still null terminated */ | 
| 897 |  | } | 
| 898 |  | #endif | 
| 899 |  |  | 
| 900 |  | /* Provide % encoders and decoders */ | 
| 901 |  |  | 
| 902 |  | static const char* hexchars = "0123456789abcdefABCDEF"; | 
| 903 |  |  | 
| 904 |  | static void | 
| 905 |  | toHex(char b, char hex[2]) | 
| 906 | 0 | { | 
| 907 | 0 |     hex[0] = hexchars[(b >> 4) & 0xf]; | 
| 908 | 0 |     hex[1] = hexchars[(b) & 0xf]; | 
| 909 | 0 | } | 
| 910 |  |  | 
| 911 |  |  | 
| 912 |  | static int | 
| 913 |  | fromHex(int c) | 
| 914 | 0 | { | 
| 915 | 0 |     if(c >= '0' && c <= '9') return (int) (c - '0'); | 
| 916 | 0 |     if(c >= 'a' && c <= 'f') return (int) (10 + (c - 'a')); | 
| 917 | 0 |     if(c >= 'A' && c <= 'F') return (int) (10 + (c - 'A')); | 
| 918 | 0 |     return 0; | 
| 919 | 0 | } | 
| 920 |  |  | 
| 921 |  | /* | 
| 922 |  | Support encode of user and password fields | 
| 923 |  | */ | 
| 924 |  | char* | 
| 925 |  | ncuriencodeuserpwd(const char* s) | 
| 926 | 0 | { | 
| 927 | 0 |     return ncuriencodeonly(s,userpwdallow); | 
| 928 | 0 | } | 
| 929 |  |  | 
| 930 |  | /* Return a string representing encoding of input; caller must free; | 
| 931 |  |    watch out: will encode whole string, so watch what you give it. | 
| 932 |  |    Allowable argument specifies characters that do not need escaping. | 
| 933 |  |  */ | 
| 934 |  |  | 
| 935 |  | char* | 
| 936 |  | ncuriencodeonly(const char* s, const char* allowable) | 
| 937 | 0 | { | 
| 938 | 0 |     size_t slen; | 
| 939 | 0 |     char* encoded; | 
| 940 | 0 |     const char* inptr; | 
| 941 | 0 |     char* outptr; | 
| 942 |  | 
 | 
| 943 | 0 |     if(s == NULL) return NULL; | 
| 944 |  |  | 
| 945 | 0 |     slen = strlen(s); | 
| 946 | 0 |     encoded = (char*)malloc((3*slen) + 1); /* max possible size */ | 
| 947 |  | 
 | 
| 948 | 0 |     for(inptr=s,outptr=encoded;*inptr;) { | 
| 949 | 0 |   char c = *inptr++; | 
| 950 | 0 |   { | 
| 951 |  |             /* search allowable */ | 
| 952 | 0 |       char* p = strchr(allowable,c); | 
| 953 | 0 |       if(p != NULL) { | 
| 954 | 0 |                 *outptr++ = (char)c; | 
| 955 | 0 |             } else { | 
| 956 | 0 |     char hex[2]; | 
| 957 | 0 |     toHex(c,hex); | 
| 958 | 0 |     *outptr++ = '%'; | 
| 959 | 0 |     *outptr++ = hex[0]; | 
| 960 | 0 |     *outptr++ = hex[1]; | 
| 961 | 0 |             } | 
| 962 | 0 |         } | 
| 963 | 0 |     } | 
| 964 | 0 |     *outptr = EOFCHAR; | 
| 965 | 0 |     return encoded; | 
| 966 | 0 | } | 
| 967 |  |  | 
| 968 |  | /* Return a string representing decoding of input; caller must free;*/ | 
| 969 |  | char* | 
| 970 |  | ncuridecode(const char* s) | 
| 971 | 0 | { | 
| 972 | 0 |     size_t slen; | 
| 973 | 0 |     char* decoded; | 
| 974 | 0 |     char* outptr; | 
| 975 | 0 |     const char* inptr; | 
| 976 | 0 |     unsigned int c; | 
| 977 |  | 
 | 
| 978 | 0 |     if (s == NULL) return NULL; | 
| 979 |  |  | 
| 980 | 0 |     slen = strlen(s); | 
| 981 | 0 |     decoded = (char*)malloc(slen+1); /* Should be max we need */ | 
| 982 |  | 
 | 
| 983 | 0 |     outptr = decoded; | 
| 984 | 0 |     inptr = s; | 
| 985 | 0 |     while((c = (unsigned int)*inptr++)) { | 
| 986 | 0 |   if(c == '%') { | 
| 987 |  |             /* try to pull two hex more characters */ | 
| 988 | 0 |       if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR | 
| 989 | 0 |     && strchr(hexchars,inptr[0]) != NULL | 
| 990 | 0 |     && strchr(hexchars,inptr[1]) != NULL) { | 
| 991 |  |     /* test conversion */ | 
| 992 | 0 |     int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); | 
| 993 | 0 |     inptr += 2; /* decode it */ | 
| 994 | 0 |     c = (unsigned int)xc; | 
| 995 | 0 |             } | 
| 996 | 0 |         } | 
| 997 | 0 |         *outptr++ = (char)c; | 
| 998 | 0 |     } | 
| 999 | 0 |     *outptr = EOFCHAR; | 
| 1000 | 0 |     return decoded; | 
| 1001 | 0 | } | 
| 1002 |  |  | 
| 1003 |  | /* | 
| 1004 |  | Partially decode a string. Only characters in 'decodeset' | 
| 1005 |  | are decoded. Return decoded string; caller must free. | 
| 1006 |  | */ | 
| 1007 |  | char* | 
| 1008 |  | ncuridecodepartial(const char* s, const char* decodeset) | 
| 1009 | 0 | { | 
| 1010 | 0 |     size_t slen; | 
| 1011 | 0 |     char* decoded; | 
| 1012 | 0 |     char* outptr; | 
| 1013 | 0 |     const char* inptr; | 
| 1014 | 0 |     unsigned int c; | 
| 1015 |  | 
 | 
| 1016 | 0 |     if (s == NULL || decodeset == NULL) return NULL; | 
| 1017 |  |  | 
| 1018 | 0 |     slen = strlen(s); | 
| 1019 | 0 |     decoded = (char*)malloc(slen+1); /* Should be max we need */ | 
| 1020 |  | 
 | 
| 1021 | 0 |     outptr = decoded; | 
| 1022 | 0 |     inptr = s; | 
| 1023 | 0 |     while((c = (unsigned int)*inptr++)) { | 
| 1024 | 0 |   if(c == '+' && strchr(decodeset,'+') != NULL) | 
| 1025 | 0 |       *outptr++ = ' '; | 
| 1026 | 0 |   else if(c == '%') { | 
| 1027 |  |             /* try to pull two hex more characters */ | 
| 1028 | 0 |       if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR | 
| 1029 | 0 |     && strchr(hexchars,inptr[0]) != NULL | 
| 1030 | 0 |     && strchr(hexchars,inptr[1]) != NULL) { | 
| 1031 |  |     /* test conversion */ | 
| 1032 | 0 |     int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); | 
| 1033 | 0 |     if(strchr(decodeset,xc) != NULL) { | 
| 1034 | 0 |         inptr += 2; /* decode it */ | 
| 1035 | 0 |         c = (unsigned int)xc; | 
| 1036 | 0 |     } | 
| 1037 | 0 |             } | 
| 1038 | 0 |             *outptr++ = (char)c; /* pass either the % or decoded char */ | 
| 1039 | 0 |         } else /* Not a % char */ | 
| 1040 | 0 |             *outptr++ = (char)c; | 
| 1041 | 0 |     } | 
| 1042 | 0 |     *outptr = EOFCHAR; | 
| 1043 | 0 |     return decoded; | 
| 1044 | 0 | } | 
| 1045 |  |  | 
| 1046 |  | /* Deep clone a uri */ | 
| 1047 |  | NCURI* | 
| 1048 |  | ncuriclone(NCURI* uri) | 
| 1049 | 0 | { | 
| 1050 | 0 |     int stat = NC_NOERR; | 
| 1051 | 0 |     NCURI* newuri = NULL; | 
| 1052 |  |  | 
| 1053 |  |     /* make sure fragments and query are up to date */ | 
| 1054 | 0 |     if((stat=ensurefraglist(uri))) goto done; | 
| 1055 | 0 |     if((stat=ensurequerylist(uri))) goto done; | 
| 1056 |  |      | 
| 1057 | 0 |     if((newuri = (NCURI*)calloc(1,sizeof(NCURI)))==NULL) | 
| 1058 | 0 |         {stat = NC_ENOMEM; goto done;} | 
| 1059 | 0 |     *newuri = *uri; /* copy */ | 
| 1060 |  |     /* deep clone fields */ | 
| 1061 |  |      | 
| 1062 | 0 |     newuri->uri = nulldup(uri->uri); | 
| 1063 | 0 |     newuri->protocol = nulldup(uri->protocol); | 
| 1064 | 0 |     newuri->user = nulldup(uri->user); | 
| 1065 | 0 |     newuri->password = nulldup(uri->password); | 
| 1066 | 0 |     newuri->host = nulldup(uri->host); | 
| 1067 | 0 |     newuri->port = nulldup(uri->port); | 
| 1068 | 0 |     newuri->path = nulldup(uri->path); | 
| 1069 | 0 |     newuri->query = nulldup(uri->query); | 
| 1070 | 0 |     newuri->fragment = nulldup(uri->fragment); | 
| 1071 |  |     /* make these be rebuilt */ | 
| 1072 | 0 |     newuri->fraglist = NULL; | 
| 1073 | 0 |     newuri->querylist = NULL; | 
| 1074 | 0 | done: | 
| 1075 | 0 |     return newuri; | 
| 1076 | 0 | } | 
| 1077 |  |  | 
| 1078 |  | static int | 
| 1079 |  | collectprefixparams(char* text, char** nextp) | 
| 1080 | 0 | { | 
| 1081 | 0 |     int ret = NC_NOERR; | 
| 1082 | 0 |     char* sp; | 
| 1083 | 0 |     char* ep; | 
| 1084 | 0 |     char* last; | 
| 1085 |  | 
 | 
| 1086 | 0 |     if(text == NULL) return NC_EURL; | 
| 1087 | 0 |     if(strlen(text) == 0) { | 
| 1088 | 0 |   if(nextp) *nextp = text; | 
| 1089 | 0 |   return NC_NOERR; | 
| 1090 | 0 |     } | 
| 1091 |  |     /* pass 1: locate last rbracket and nul term the prefix */ | 
| 1092 | 0 |     sp = text; | 
| 1093 | 0 |     last = NULL; | 
| 1094 | 0 |     for(;;) { | 
| 1095 | 0 |   if(*sp != LBRACKET) { | 
| 1096 | 0 |       if(nextp) *nextp = sp; | 
| 1097 | 0 |       break; | 
| 1098 | 0 |   } | 
| 1099 |  |         /* use nclocate because \\ escapes might be present */ | 
| 1100 | 0 |         ep = nclocate(sp,RBRACKETSTR); | 
| 1101 | 0 |   if(ep == NULL) {ret = NC_EINVAL; goto done;} /* malformed */ | 
| 1102 | 0 |   last = ep; /* save this position  */ | 
| 1103 | 0 |   ep++; /* move past rbracket */ | 
| 1104 | 0 |   sp = ep; | 
| 1105 | 0 |     } | 
| 1106 |  |     /* nul terminate */ | 
| 1107 | 0 |     if(last != NULL) | 
| 1108 | 0 |   terminate(last); | 
| 1109 |  |  | 
| 1110 |  |     /* pass 2: convert [] to & */ | 
| 1111 | 0 |     sp = text; | 
| 1112 | 0 |     for(;;) { | 
| 1113 | 0 |   char* p; char* q; | 
| 1114 |  |   /* by construction, here we are at an LBRACKET: compress it out */ | 
| 1115 | 0 |   for(p=sp,q=sp+1;(*p++=*q++);) | 
| 1116 | 0 |       ; | 
| 1117 |  |         /* locate the next RRACKET */ | 
| 1118 | 0 |         ep = nclocate(sp,RBRACKETSTR); | 
| 1119 | 0 |   if(ep == NULL) break;/* we are done */ | 
| 1120 |  |   /* convert the BRACKET to '&' */ | 
| 1121 | 0 |   *ep = '&'; | 
| 1122 | 0 |   ep++; /* move past rbracket */ | 
| 1123 | 0 |   sp = ep; | 
| 1124 | 0 |     } | 
| 1125 | 0 | done: | 
| 1126 | 0 |     return ret; | 
| 1127 | 0 | } | 
| 1128 |  |  | 
| 1129 |  | static int | 
| 1130 |  | parselist(const char* text, NClist* list) | 
| 1131 | 0 | { | 
| 1132 | 0 |     int ret = NC_NOERR; | 
| 1133 | 0 |     char* ptext = NULL; | 
| 1134 | 0 |     char* p; | 
| 1135 | 0 |     ptext = strdup(text); /* We need to modify */ | 
| 1136 | 0 |     p = ptext; /* start of next parameter */ | 
| 1137 | 0 |     for(;;) { | 
| 1138 | 0 |   char* sp = p; | 
| 1139 | 0 |   char* eq; | 
| 1140 | 0 |   char* ep; | 
| 1141 | 0 |   char* key; | 
| 1142 | 0 |   char* value; | 
| 1143 | 0 |   if(*p == EOFCHAR) break; /* we are done */ | 
| 1144 |  |         /* use nclocate because \\ escapes might be present */ | 
| 1145 | 0 |   ep = nclocate(sp,"&"); | 
| 1146 | 0 |   if(ep != NULL) { | 
| 1147 | 0 |       terminate(ep); /* overwrite the trailing ampersand */ | 
| 1148 | 0 |       p = ep+1; /* next param */ | 
| 1149 | 0 |   } | 
| 1150 |  |   /* split into key + value */ | 
| 1151 | 0 |         eq = strchr(sp,'='); | 
| 1152 | 0 |         if(eq != NULL) { /* value is present */ | 
| 1153 | 0 |       terminate(eq); eq++; | 
| 1154 | 0 |       key = strdup(sp); | 
| 1155 | 0 |       value = strdup(eq); | 
| 1156 | 0 |   } else {/* there is no value */ | 
| 1157 | 0 |       key = strdup(sp); | 
| 1158 | 0 |       value = strdup(""); | 
| 1159 | 0 |   } | 
| 1160 | 0 |         nclistpush(list,key); | 
| 1161 | 0 |   nclistpush(list,value); | 
| 1162 | 0 |   if(ep == NULL) | 
| 1163 | 0 |       break; | 
| 1164 | 0 |     } | 
| 1165 | 0 |     nullfree(ptext); | 
| 1166 | 0 |     return ret; | 
| 1167 | 0 | } | 
| 1168 |  |  | 
| 1169 |  | static int | 
| 1170 |  | unparselist(const NClist* vec, const char* prefix, int encode, NCbytes* buf) | 
| 1171 | 0 | { | 
| 1172 | 0 |     int stat = NC_NOERR; | 
| 1173 | 0 |     size_t i; | 
| 1174 | 0 |     int first = 1; | 
| 1175 |  | 
 | 
| 1176 | 0 |     if(nclistlength(vec) == 0) goto done; | 
| 1177 | 0 |     if(prefix != NULL) ncbytescat(buf,prefix); | 
| 1178 | 0 |     for(i=0;i<nclistlength(vec);i+=2,first=0) { | 
| 1179 | 0 |   const char* p0 = (const char*)nclistget(vec,i); | 
| 1180 | 0 |   const char* p1 = (const char*)nclistget(vec,i+1); | 
| 1181 | 0 |         if(!first) ncbytescat(buf,"&"); | 
| 1182 | 0 |         if(encode) { | 
| 1183 | 0 |       char* encoded = ncuriencodeonly(p0,queryallow); | 
| 1184 | 0 |       ncbytescat(buf,encoded); | 
| 1185 | 0 |       nullfree(encoded); | 
| 1186 | 0 |   } else | 
| 1187 | 0 |       ncbytescat(buf,p0); | 
| 1188 | 0 |   if(p1 != NULL && strlen(p1) > 0) { | 
| 1189 | 0 |       ncbytescat(buf,"="); | 
| 1190 | 0 |       if(encode) { | 
| 1191 | 0 |     char* encoded = ncuriencodeonly(p1,queryallow); | 
| 1192 | 0 |     ncbytescat(buf,encoded); | 
| 1193 | 0 |           nullfree(encoded); | 
| 1194 | 0 |       } else | 
| 1195 | 0 |     ncbytescat(buf,p1); | 
| 1196 | 0 |   } | 
| 1197 | 0 |     } | 
| 1198 | 0 | done: | 
| 1199 | 0 |     return stat; | 
| 1200 | 0 | } | 
| 1201 |  |  | 
| 1202 |  | static int | 
| 1203 |  | ensurefraglist(NCURI* uri) | 
| 1204 | 0 | { | 
| 1205 | 0 |     int stat = NC_NOERR; | 
| 1206 | 0 |     int hastext = 0; | 
| 1207 | 0 |     int haslist = 0; | 
| 1208 | 0 |     NClist* fraglist = nclistnew(); | 
| 1209 | 0 |     NCbytes* frag = NULL; | 
| 1210 |  |    | 
| 1211 | 0 |     if(nulllen(uri->fragment) == 0) | 
| 1212 | 0 |         {nullfree(uri->fragment); uri->fragment = NULL; hastext=0;} | 
| 1213 | 0 |     else hastext = 1; | 
| 1214 | 0 |     if(nclistlength((NClist*)uri->fraglist) == 0) | 
| 1215 | 0 |         {nclistfree((NClist*)uri->fraglist); uri->fraglist = NULL; haslist=0;} | 
| 1216 | 0 |     else haslist = 1; | 
| 1217 |  |  | 
| 1218 |  |     /* Four cases: */ | 
| 1219 | 0 |     if(!haslist && !hastext) { | 
| 1220 |  |   /* do nothing */ | 
| 1221 | 0 |     } else if(!haslist && hastext) { | 
| 1222 | 0 |   if((stat = parselist(uri->fragment,fraglist))) goto done;  | 
| 1223 | 0 |   removedups(fraglist); | 
| 1224 | 0 |   uri->fraglist = fraglist; fraglist = NULL; | 
| 1225 | 0 |     } else if(haslist && !hastext) { | 
| 1226 |  |   /* Create the fragment string from fraglist */ | 
| 1227 | 0 |   frag = ncbytesnew(); | 
| 1228 | 0 |   if((stat=unparselist((const NClist*)uri->fraglist,NULL,0,frag))) goto done; /* do not encode */ | 
| 1229 | 0 |   uri->fragment = ncbytesextract(frag); | 
| 1230 | 0 |     } else if(haslist && hastext) { | 
| 1231 |  |   /* assume already consistent */ | 
| 1232 | 0 |     } | 
| 1233 |  |  | 
| 1234 | 0 | done: | 
| 1235 | 0 |     ncbytesfree(frag); | 
| 1236 | 0 |     nclistfreeall(fraglist); | 
| 1237 | 0 |     return stat; | 
| 1238 | 0 | } | 
| 1239 |  |  | 
| 1240 |  | static int | 
| 1241 |  | ensurequerylist(NCURI* uri) | 
| 1242 | 0 | { | 
| 1243 | 0 |     int stat = NC_NOERR; | 
| 1244 | 0 |     int hastext = 0; | 
| 1245 | 0 |     int haslist = 0; | 
| 1246 | 0 |     NClist* querylist = nclistnew(); | 
| 1247 | 0 |     NCbytes* query = NULL; | 
| 1248 |  |    | 
| 1249 | 0 |     if(nulllen(uri->query) == 0) | 
| 1250 | 0 |         {nullfree(uri->query); uri->query = NULL; hastext=0;} | 
| 1251 | 0 |     else hastext = 1; | 
| 1252 | 0 |     if(nclistlength((NClist*)uri->querylist) == 0) | 
| 1253 | 0 |         {nclistfree((NClist*)uri->querylist); uri->querylist = NULL; haslist=0;} | 
| 1254 | 0 |     else haslist = 1; | 
| 1255 |  |      | 
| 1256 |  |     /* Four cases: */ | 
| 1257 | 0 |     if(!haslist && !hastext) { | 
| 1258 |  |   /* do nothing */ | 
| 1259 | 0 |     } else if(!haslist && hastext) { | 
| 1260 | 0 |   if((stat = parselist(uri->query,querylist))) goto done;  | 
| 1261 | 0 |   removedups(querylist); | 
| 1262 | 0 |   uri->querylist = querylist; querylist = NULL; | 
| 1263 | 0 |     } else if(haslist && !hastext) { | 
| 1264 |  |   /* Create the query string from querylist */ | 
| 1265 | 0 |   query = ncbytesnew(); | 
| 1266 | 0 |   if((stat=unparselist((const NClist*)uri->querylist,NULL,0,query))) goto done; /* do not encode */ | 
| 1267 | 0 |   uri->query = ncbytesextract(query); | 
| 1268 | 0 |     } else if(haslist && hastext) { | 
| 1269 |  |   /* assume consistent */ | 
| 1270 | 0 |     } | 
| 1271 |  |  | 
| 1272 | 0 | done: | 
| 1273 | 0 |     ncbytesfree(query); | 
| 1274 | 0 |     nclistfreeall(querylist); | 
| 1275 | 0 |     return stat; | 
| 1276 | 0 | } | 
| 1277 |  |  | 
| 1278 |  | static void | 
| 1279 |  | removedups(NClist* list) | 
| 1280 | 339 | { | 
| 1281 | 339 |     size_t i,j; | 
| 1282 |  |  | 
| 1283 | 339 |     if(nclistlength(list) <= 2) return; /* need at least 2 pairs */ | 
| 1284 | 0 |     for(i=0;i<nclistlength(list);i+=2) { | 
| 1285 |  |   /* look for dups for this entry */ | 
| 1286 | 0 |   for(j=nclistlength(list)-2;j>i;j-=2) { | 
| 1287 | 0 |       if(strcasecmp(nclistget(list,i),nclistget(list,j))==0 | 
| 1288 | 0 |     && strcasecmp(nclistget(list,i+1),nclistget(list,j+1))==0) { | 
| 1289 | 0 |     nclistremove(list,j+1); nclistremove(list,j); | 
| 1290 | 0 |       } | 
| 1291 | 0 |   } | 
| 1292 | 0 |     } | 
| 1293 | 0 | } | 
| 1294 |  |  | 
| 1295 |  | #if 0 | 
| 1296 |  | static int | 
| 1297 |  | extendenvv(char*** envvp, int amount, int* oldlenp) | 
| 1298 |  | { | 
| 1299 |  |     char** envv = *envvp; | 
| 1300 |  |     char** p; | 
| 1301 |  |     int len; | 
| 1302 |  |     for(len=0,p=envv;*p;p++) len++; | 
| 1303 |  |     *oldlenp = len; | 
| 1304 |  |     if((envv = (char**)malloc((amount+len+1)*sizeof(char*)))==NULL) return NC_ENOMEM; | 
| 1305 |  |     memcpy(envv,*envvp,sizeof(char*)*len); | 
| 1306 |  |     envv[len] = NULL; | 
| 1307 |  |     nullfree(*envvp); | 
| 1308 |  |     *envvp = envv; envv = NULL; | 
| 1309 |  |     return NC_NOERR; | 
| 1310 |  | } | 
| 1311 |  | #endif | 
| 1312 |  |  | 
| 1313 |  | /* Use for gdb debug */ | 
| 1314 |  | char* | 
| 1315 |  | ncuriunescape(const char* s) | 
| 1316 | 0 | { | 
| 1317 | 0 |     return ncuridecodepartial(s,ascii); | 
| 1318 | 0 | } | 
| 1319 |  |  | 
| 1320 |  | /* Get the actual list of queryies */ | 
| 1321 |  | void* | 
| 1322 |  | ncuriqueryparams(NCURI* uri) | 
| 1323 | 0 | { | 
| 1324 | 0 |     ensurequerylist(uri); | 
| 1325 | 0 |     return uri->querylist; | 
| 1326 | 0 | } | 
| 1327 |  |  | 
| 1328 |  | /* Get the actual list of frags */ | 
| 1329 |  | void* | 
| 1330 |  | ncurifragmentparams(NCURI* uri) | 
| 1331 | 0 | { | 
| 1332 | 0 |     ensurefraglist(uri); | 
| 1333 | 0 |     return uri->fraglist; | 
| 1334 | 0 | } | 
| 1335 |  |  |