Coverage Report

Created: 2025-10-28 07:06

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