Coverage Report

Created: 2026-01-04 06:24

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