1/*********************************************************************
2 *   Copyright 2010, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *   $Header$
5 *********************************************************************/
6#include "config.h"
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <assert.h>
11
12#include "ncuri.h"
13
14/* Include netcdf.h to allow access to
15   NC_ error return codes. */
16#include "netcdf.h"
17
18#define NCURIDEBUG
19
20#ifdef NCURIDEBUG
21static int failpoint = 0;
22#define THROW(n) {failpoint=(n); goto fail;}
23#else
24#define THROW(n) {goto fail;}
25#endif
26
27
28#define PADDING 8
29
30#define LBRACKET '['
31#define RBRACKET ']'
32#define EOFCHAR '\0'
33
34#ifndef FIX
35#define FIX(s) ((s)==NULL?"NULL":(s))
36#endif
37
38#ifndef NILLEN
39#define NILLEN(s) ((s)==NULL?0:strlen(s))
40#endif
41
42#ifndef nulldup
43#define nulldup(s) ((s)==NULL?NULL:strdup(s))
44#endif
45
46#define terminate(p) {*(p) = EOFCHAR;}
47
48#define endof(p) ((p)+strlen(p))
49
50static struct NC_ProtocolInfo {
51char* name;
52int   filelike; /* 1=>this protocol has no host, user+pwd, or port */
53legalprotocols[] = {
54{"file",1},
55{"http",0},
56{"https",0},
57{"ftp",0},
58};
59
60/* Allowable character sets for encode */
61static char* fileallow =
62"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
63
64static char* queryallow =
65"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
66
67#ifndef HAVE_STRNCMP
68#define strndup ncstrndup
69/* Not all systems have strndup, so provide one*/
70char*
71ncstrndup(const char* s, size_t len)
72{
73    char* dup;
74    if(s == NULL) return NULL;
75    dup = (char*)malloc(len+1);
76    if(dup == NULL) return NULL;
77    memcpy((void*)dup,s,len);
78    dup[len] = '\0';
79    return dup;
80}
81#endif
82/* Forward */
83static void ncparamfree(char** params);
84static int ncfind(char** params, const char* key);
85static void nclshift1(char* p);
86static void ncrshift1(char* p);
87static char* nclocate(char* p, const char* charlist);
88static void ncappendparams(char* newuri, char** p);
89
90/* Do a simple uri parse: return 0 if fail, 1 otherwise*/
91int
92ncuriparse(const char* uri0NCURI** durip)
93{
94    NCURIduri = NULL;
95    char* uri = NULL;
96    char* p;
97    struct NC_ProtocolInfoproto;
98    int i,nprotos;
99
100    /* accumulate parse points*/
101    char* protocol = NULL;
102    char* host = NULL;
103    char* port = NULL;
104    char* constraint = NULL;
105    char* user = NULL;
106    char* pwd = NULL;
107    char* file = NULL;
108    char* prefixparams = NULL;
109    char* suffixparams = NULL;
110
111    if(uri0 == NULL || strlen(uri0) == 0)
112 {THROW(1);}
113
114    duri = (NCURI*)calloc(1,sizeof(NCURI));
115    if(duri == NULL)
116      {THROW(2);}
117
118    /* save original uri */
119    duri->uri = nulldup(uri0);
120
121    /* make local copy of uri */
122    uri = (char*)malloc(strlen(uri0)+1+PADDING); /* +1 for trailing null,
123                                                    +PADDING for shifting */
124    if(uri == NULL)
125 {THROW(3);}
126
127    /* strings will be broken into pieces with intermixed '\0; characters;
128       first char is guaranteed to be '\0' */
129
130    duri->strings = uri;
131    uri++;
132
133    /* dup the incoming url */
134    strcpy(uri,uri0);
135
136    /* Walk the uri and do the following:
137 1. remove all whitespace
138 2. remove all '\\' (Temp hack to remove escape characters
139                            inserted by Windows or MinGW)
140    */
141    for(p=uri;*p;p++) {
142 if(*p == '\\' || *p < ' ')
143     nclshift1(p); /* compress out */
144    }
145
146    p = uri;
147
148    /* break up the uri string into big chunks: prefixparams, protocol,
149       host section, and the file section (i.e. remainder)
150    */
151
152    /* collect any prefix bracketed parameters */
153    if(*p == LBRACKET) {
154 prefixparams = p+1;
155 /* find end of the clientparams; convert LB,RB to '&' */
156        for(;*p;p++) {
157     if(p[0] == RBRACKET && p[1] == LBRACKET) {
158 p[0] = '&';
159 nclshift1(p+1);
160     } else if(p[0] == RBRACKET && p[1] != LBRACKET)
161 break;
162 }
163 if(*p == 0)
164     {THROW(4); /* malformed client params*/}
165        terminate(p); /* nul term the prefixparams (overwrites
166                         the final RBRACKET) */
167 p++; /* move past the final RBRACKET */
168    }
169
170    /* Tag the protocol */
171    protocol = p;
172    p = strchr(p,':');
173    if(!p)
174 {THROW(5);}
175    terminate(p); /*overwrite colon*/
176    p++; /* skip the colon */
177
178    /* verify that the uri starts with an acceptable protocol*/
179    nprotos = (sizeof(legalprotocols)/sizeof(struct NC_ProtocolInfo));
180    proto = NULL;
181    for(i=0;i<nprotos;i++) {
182        if(strcmp(protocol,legalprotocols[i].name)==0) {
183     proto = &legalprotocols[i];
184     break;
185 }
186    }
187    if(proto == NULL)
188 {THROW(6); /* illegal protocol*/}
189
190    /* skip // */
191    if(p[0] != '/' || p[1] != '/')
192 {THROW(7);}
193    p += 2;
194
195    /* If this is all we have (proto://) then fail */
196    if(*p == EOFCHAR)
197 {THROW(8);}
198
199    /* establish the start of the file section */
200    if(proto->filelike) {/* everything after proto:// */
201 file = p;
202 host = NULL; /* and no host section */
203    } else { /*!proto->filelike => This means there should be a host section */
204        /* locate the end of the host section and therefore the start
205           of the file section */
206 host = p;
207        p  = nclocate(p,"/?#");
208 if(p == NULL) {
209     file = endof(host); /* there is no file section */
210 } else {
211     ncrshift1(p); /* make room to terminate the host section
212                             without overwriting the leading character */
213     terminate(p); /* terminate the host section */
214     file = p+1; /* +1 becauseof the shift */
215 }
216    }
217
218    /* If you shift in the code below, you must reset file beginning */
219
220    if(host != NULL) {/* Parse the host section */
221 /* Check for leading user:pwd@ */
222        p = strchr(host,'@');
223        if(p) {
224     if(p == host)
225 {THROW(9); /* we have proto://@ */}
226     user = host;
227     terminate(p); /* overwrite '@' */
228     host = p+1; /* start of host ip name */
229     p = strchr(user,':');
230      if(p == NULL)
231 {THROW(10); /* malformed */}
232     terminate(p); /*overwrite colon */
233     pwd = p+1;
234 }
235
236        /* extract host and port */
237 p = host;
238        p = strchr(p,':');
239        if(p != NULL) {
240     terminate(p);
241     p++;
242     port = p;
243     if(*port == EOFCHAR)
244 {THROW(11); /* we have proto://...:/ */}
245     /* The port must look something like a number */
246     for(;*p;p++) {
247         if(strchr("0123456789-",*p) == NULL)
248     {THROW(12);  /* probably not a real port, fail */}
249     }
250 } /* else *p == NULL */
251
252
253        /* check for empty host section */
254 if(*host == EOFCHAR)
255     {THROW(13);}
256
257    }
258
259    assert(file != NULL);
260    p = file;
261
262    /* find the end of the file section and the start of the
263       constraints and/or suffixparams
264    */
265    p = nclocate(p,"?#");
266    if(p != NULL) { /* we have constraint and/or suffixparams */
267 char* fileend = p; /* save the end of the file section */
268 char* constraintend = NULL;
269 if(*p == '?')
270            constraint = p+1;
271 else
272     constraint = NULL;
273 p = strchr(p,'#'); /* may repeat effect of nclocate above */
274 if(p != NULL) {
275     constraintend = p;
276     suffixparams = p+1;
277 } else
278     suffixparams = NULL;
279 /* Ok, terminate the pieces */
280 terminate(fileend); /* terminate file section */
281 if(constraint != NULL && constraintend != NULL)
282     terminate(constraintend);
283 /* Suffix params are already terminated
284           since they should be the last section
285           of the original url
286        */
287    }
288
289    /* check for empty sections */
290    if(file != NULL && *file == EOFCHAR)
291 file = NULL; /* empty file section */
292    if(constraint != NULL && *constraint == EOFCHAR)
293 constraint = NULL; /* empty constraint section */
294    if(suffixparams != NULL && *suffixparams == EOFCHAR)
295 suffixparams = NULL; /* empty suffixparams section */
296
297    if(suffixparams != NULL) {
298 /* there really are suffix params; so rebuild the suffix params */
299 if(*suffixparams == LBRACKETsuffixparams++;
300        p = suffixparams;
301 /* convert RBRACKET LBRACKET to '&' */
302        for(;*p;p++) {
303     if(p[0] == RBRACKET && p[1] == LBRACKET) {
304         p[0] = '&';
305 nclshift1(p+1);
306     } else if(p[0] == RBRACKET && p[1] != LBRACKET) {
307 /* terminate suffixparams */
308 *p = EOFCHAR;
309 break;
310     }
311 }
312 if(*suffixparams == EOFCHAR)
313     suffixparams = NULL; /* suffixparams are empty */
314    }
315
316    /* do last minute empty check */
317
318    if(*protocol == EOFCHARprotocol = NULL;
319    if(user != NULL && *user == EOFCHARuser = NULL;
320    if(pwd != NULL && *pwd == EOFCHARpwd = NULL;
321    if(host != NULL && *host == EOFCHARhost = NULL;
322    if(port != NULL && *port == EOFCHARport = NULL;
323    if(file != NULL && *file == EOFCHARfile = NULL;
324    if(constraint != NULL && *constraint == EOFCHARconstraint = NULL;
325
326    /* assemble the component pieces */
327    duri->protocol = protocol;
328    duri->user = user;
329    duri->password = pwd;
330    duri->host = host;
331    duri->port = port;
332    duri->file = file;
333
334    ncurisetconstraints(duri,constraint);
335
336    /* concat suffix and prefix params */
337    if(prefixparams != NULL || suffixparams != NULL) {
338 int plen = prefixparams ? strlen(prefixparams) : 0;
339 int slen = suffixparams ? strlen(suffixparams) : 0;
340 int space = plen + slen + 1;
341 /* add 1 for an extra ampersand if both are defined */
342        space++;
343        duri->params = (char*)malloc(space);
344 duri->params[0] = EOFCHAR; /* so we can use strcat */
345 if(plen > 0) {
346            strcat(duri->params,prefixparams);
347     if(slen > 0)
348 strcat(duri->params,"&");
349 }
350 if(slen > 0)
351            strcat(duri->params,suffixparams);
352    }
353
354#ifdef NCXDEBUG
355 {
356        fprintf(stderr,"duri:");
357        fprintf(stderr," params=|%s|",FIX(duri->params));
358        fprintf(stderr," protocol=|%s|",FIX(duri->protocol));
359        fprintf(stderr," host=|%s|",FIX(duri->host));
360        fprintf(stderr," port=|%s|",FIX(duri->port));
361        fprintf(stderr," file=|%s|",FIX(duri->file));
362        fprintf(stderr," constraint=|%s|",FIX(duri->constraint));
363        fprintf(stderr,"\n");
364    }
365#endif
366    if(durip != NULL)
367      *durip = duri;
368    else
369      ncurifree(duri);
370
371    return 1;
372
373fail:
374    if(duri != NULL) {
375 ncurifree(duri);
376    }
377    return 0;
378}
379
380void
381ncurifree(NCURIduri)
382{
383    if(duri == NULL) return;
384    if(duri->uri != NULL) {free(duri->uri);}
385    if(duri->params != NULL) {free(duri->params);}
386    if(duri->paramlist != NULLncparamfree(duri->paramlist);
387    if(duri->strings != NULL) {free(duri->strings);}
388    if(duri->constraint != NULL) {free(duri->constraint);}
389    if(duri->projection != NULL) {free(duri->projection);}
390    if(duri->selection != NULL) {free(duri->selection);}
391    free(duri);
392}
393
394/* Replace the constraints */
395void
396ncurisetconstraints(NCURIduri,const char* constraints)
397{
398    char* proj = NULL;
399    char* select = NULL;
400    const char* p;
401
402    if(duri->constraint != NULL) free(duri->constraint);
403    if(duri->projection != NULL) free(duri->projection);
404    if(duri->selection != NULL) free(duri->selection);
405    duri->constraint = NULL;
406    duri->projection = NULL;
407    duri->selection = NULL;
408
409    if(constraints == NULL || strlen(constraints)==0) return;
410
411    duri->constraint = nulldup(constraints);
412    if(*duri->constraint == '?')
413 nclshift1(duri->constraint);
414
415    p = duri->constraint;
416    proj = (char*) p;
417    select = strchr(proj,'&');
418    if(select != NULL) {
419        size_t plen = (size_t)(select - proj);
420 if(plen == 0) {
421     proj = NULL;
422 } else {
423     proj = (char*)malloc(plen+1);
424     memcpy((void*)proj,p,plen);
425     proj[plen] = EOFCHAR;
426 }
427 select = nulldup(select);
428    } else {
429 proj = nulldup(proj);
430 select = NULL;
431    }
432    duri->projection = proj;
433    duri->selection = select;
434}
435
436
437/* Construct a complete NC URI.
438   Optionally with the constraints.
439   Optionally with the user parameters.
440   Caller frees returned string.
441   Optionally encode the pieces.
442*/
443
444char*
445ncuribuild(NCURIduri, const char* prefix, const char* suffix, int flags)
446{
447    size_t len = 0;
448    char* newuri;
449    char* tmpfile;
450    char* tmpsuffix;
451    char* tmpquery;
452    size_t nparams = 0;
453    size_t paramslen = 0;
454
455    /* if both are specified, prefix has priority */
456    int withsuffixparams = ((flags&NCURISUFFIXPARAMS)!=0
457 && duri->params != NULL);
458    int withprefixparams = ((flags&NCURIPREFIXPARAMS)!=0
459 && duri->params != NULL);
460    int withuserpwd = ((flags&NCURIUSERPWD)!=0
461                && duri->user != NULL && duri->password != NULL);
462    int withconstraints = ((flags&NCURICONSTRAINTS)!=0
463                    && duri->constraint != NULL);
464#ifdef NEWESCAPE
465    const int encode = (flags&NCURIENCODE);
466#else
467    const int encode = 0;
468#endif
469
470    if(prefix != NULLlen += NILLEN(prefix);
471    len += (NILLEN(duri->protocol)+NILLEN("://"));
472    if(withuserpwd) {
473 len += (NILLEN(duri->user)+NILLEN(duri->password)+NILLEN(":@"));
474    }
475    len += (NILLEN(duri->host));
476    if(duri->port != NULL) {
477 len += (NILLEN(":")+NILLEN(duri->port));
478    }
479
480    tmpfile = duri->file;
481    if(encode)
482 tmpfile = ncuriencode(tmpfile,fileallow);
483    len += (NILLEN(tmpfile));
484
485    if(suffix != NULL) {
486        tmpsuffix = (char*)suffix;
487        if(encode)
488     tmpsuffix = ncuriencode(tmpsuffix,fileallow);
489        len += (NILLEN(tmpsuffix));
490    }
491
492    if(withconstraints) {
493 tmpquery = duri->constraint;
494        if(encode)
495     tmpquery = ncuriencode(tmpquery,queryallow);
496        len += (NILLEN("?")+NILLEN(tmpquery));
497    }
498
499    if(withprefixparams || withsuffixparams) {
500 char** p;
501 if(duri->paramlist == NULL)
502     if(!ncuridecodeparams(duri))
503 return NULL;
504 for(paramslen=0,nparams=0,p=duri->paramlist;*p;p++) {
505     nparams++;
506     paramslen += NILLEN(*p);
507 }
508 if(nparams % 2 == 1)
509     return NULL; /* malformed */
510 nparams = (nparams / 2);
511 len += paramslen;
512 len += 3*nparams; /* for brackets for every param plus possible = */
513 if(withsuffixparams)
514     len += strlen("#");
515    }
516
517    len += 1; /* null terminator */
518
519    newuri = (char*)malloc(len);
520    if(newuri == NULL) return NULL;
521
522    newuri[0] = EOFCHAR;
523    if(prefix != NULL) strcat(newuri,prefix);
524    if(withprefixparams) {
525 ncappendparams(newuri,duri->paramlist);
526    }
527    if(duri->protocol != NULL)
528 strcat(newuri,duri->protocol);
529    strcat(newuri,"://");
530    if(withuserpwd) {
531        strcat(newuri,duri->user);
532        strcat(newuri,":");
533        strcat(newuri,duri->password);
534        strcat(newuri,"@");
535    }
536    if(duri->host != NULL) { /* may be null if using file: protocol */
537        strcat(newuri,duri->host);
538    }
539    if(duri->port != NULL) {
540        strcat(newuri,":");
541        strcat(newuri,duri->port);
542    }
543
544    if(tmpfile != NULL) {
545        strcat(newuri,tmpfile);
546        if(suffix != NULL) strcat(newuri,tmpsuffix);
547    }
548    if(withconstraints) {
549 strcat(newuri,"?");
550 strcat(newuri,tmpquery);
551    }
552    if(withsuffixparams & !withprefixparams) {
553 strcat(newuri,"#");
554 ncappendparams(newuri,duri->paramlist);
555    }
556    return newuri;
557}
558
559static void
560ncappendparams(char* newuri, char** p)
561{
562 while(*p) {
563     strcat(newuri,"[");
564     strcat(newuri,*p++);
565     if(strlen(*p) > 0) {
566         strcat(newuri,"=");
567         strcat(newuri,*p);
568     }
569     p++;
570     strcat(newuri,"]");
571 }
572}
573
574/**************************************************/
575/* Parameter support */
576
577/*
578In the original url, client parameters are assumed to be one
579or more instances of bracketed pairs: e.g "[...][...]...".
580They may occur either at the front, or suffixed after
581a trailing # character. After processing, the list is
582converted to an ampersand separated list of the combination
583of prefix and suffix parameters.
584
585After the url is parsed, the parameter list
586is converted to an ampersand separated list with all
587whitespace removed.
588In any case, each parameter in turn is assumed to be a
589of the form <name>=<value> or [<name>].
590e.g. [x=y][z][a=b][w].  If the same parameter is specified more
591than once, then the first occurrence is used; this is so
592that is possible to forcibly override user specified
593parameters by prefixing.  IMPORTANT: client parameter string
594is assumed to have blanks compressed out.  Returns 1 if parse
595succeeded, 0 otherwise; */
596
597int
598ncuridecodeparams(NCURIncuri)
599{
600    char* cp = NULL;
601    int i,c;
602    size_t nparams;
603    char* params = NULL;
604    char** plist;
605
606    if(ncuri == NULL) return 0;
607    if(ncuri->params == NULL) return 1;
608
609    params = strndup(ncuri->params,
610      (strlen(ncuri->params)+1)); /* so we can modify */
611    if(!params)
612      return NC_ENOMEM;
613
614    /* Pass 1 to break string into pieces at the ampersands
615       and count # of pairs */
616    nparams=0;
617    for(cp=params;(c=*cp);cp++) {
618 if(c == '&') {*cp = EOFCHARnparams++;}
619    }
620    nparams++; /* for last one */
621
622    /* plist is an env style list */
623    plist = (char**)calloc(1,sizeof(char*)*(2*nparams+1)); /* +1 for null termination */
624    if(plist == NULL) {
625      if(params) free(params);
626      return 0;
627    }
628
629    /* Break up each param into a (name,value) pair*/
630    /* and insert into the param list */
631    /* parameters of the form name name= are converted to name=""*/
632    for(cp=params,i=0;i<nparams;i++) {
633 char* next = cp+strlen(cp)+1; /* save ptr to next pair*/
634 char* vp;
635 /*break up the ith param*/
636 vp = strchr(cp,'=');
637 if(vp != NULL) {*vp = EOFCHARvp++;} else {vp = "";}
638 plist[2*i] = nulldup(cp);
639 plist[2*i+1] = nulldup(vp);
640 cp = next;
641    }
642    plist[2*nparams] = NULL;
643    free(params);
644    if(ncuri->paramlist != NULL)
645 ncparamfree(ncuri->paramlist);
646    ncuri->paramlist = plist;
647    return 1;
648}
649
650int
651ncurilookup(NCURIuri, const char* key, const char** resultp)
652{
653  int i;
654  char* value = NULL;
655  if(uri == NULL || key == NULL || uri->params == NULL) return 0;
656  if(uri->paramlist == NULL) {
657 i = ncuridecodeparams(uri);
658 if(!i) return 0;
659  }
660  /* Coverity[FORWARD_NULL] */
661  i = ncfind(uri->paramlist,key);
662  if(i < 0)
663 return 0;
664
665  if(uri->paramlist) {
666    value = uri->paramlist[(2*i)+1];
667    if(resultp) *resultp = value;
668  }
669
670  return 1;
671}
672
673int
674ncurisetparams(NCURIuri, const char* newparams)
675{
676    if(uri == NULL) return 0;
677    if(uri->paramlist != NULLncparamfree(uri->paramlist);
678    uri->paramlist = NULL;
679    if(uri->params != NULL) free(uri->params);
680    uri->params = nulldup(newparams);
681    return 1;
682}
683
684/* Internal version of lookup; returns the paired index of the key */
685static int
686ncfind(char** params, const char* key)
687{
688    int i;
689    char** p;
690    for(i=0,p=params;*p;p+=2,i++) {
691 if(strcmp(key,*p)==0) return i;
692    }
693    return -1;
694}
695
696static void
697ncparamfree(char** params)
698{
699    char** p;
700    if(params == NULL) return;
701    for(p=params;*p;p+=2) {
702 free(*p);
703 if(p[1] != NULL) free(p[1]);
704    }
705    free(params);
706}
707
708
709/* Return the ptr to the first occurrence of
710   any char in the list. Return NULL if no
711   occurrences
712*/
713static char*
714nclocate(char* p, const char* charlist)
715{
716    for(;*p;p++) {
717 if(strchr(charlist,*p) != NULL)
718     return p;
719    }
720    return NULL;
721}
722
723
724/* Shift every char starting at p 1 place to the left */
725static void
726nclshift1(char* p)
727{
728    if(p != NULL && *p != EOFCHAR) {
729 char* q = p++;
730 while((*q++=*p++));
731    }
732}
733
734/* Shift every char starting at p 1 place to the right */
735static void
736ncrshift1(char* p)
737{
738    char cur;
739    cur = 0;
740    do {
741 char next = *p;
742 *p++ = cur;
743 cur = next;
744    } while(cur != 0);
745    *p = 0; /* make sure we are still null terminated */
746}
747
748
749/* Provide % encoders and decoders */
750
751
752static char* hexchars = "0123456789abcdefABCDEF";
753
754static void
755toHex(unsigned int b, char hex[2])
756{
757    hex[0] = hexchars[(b >> 4) & 0xff];
758    hex[1] = hexchars[(b) & 0xff];
759}
760
761
762static int
763fromHex(int c)
764{
765    if(c >= '0' && c <= '9') return (int) (c - '0');
766    if(c >= 'a' && c <= 'f') return (int) (10 + (c - 'a'));
767    if(c >= 'A' && c <= 'F') return (int) (10 + (c - 'A'));
768    return 0;
769}
770
771
772/* Return a string representing encoding of input; caller must free;
773   watch out: will encode whole string, so watch what you give it.
774   Allowable argument specifies characters that do not need escaping.
775 */
776
777char*
778ncuriencode(char* s, char* allowable)
779{
780    size_t slen;
781    char* encoded;
782    char* inptr;
783    char* outptr;
784
785    if(s == NULL) return NULL;
786
787    slen = strlen(s);
788    encoded = (char*)malloc((3*slen) + 1); /* max possible size */
789
790    for(inptr=s,outptr=encoded;*inptr;) {
791 int c = *inptr++;
792        if(c == ' ') {
793     *outptr++ = '+';
794        } else {
795            /* search allowable */
796            int c2;
797     char* a = allowable;
798     while((c2=*a++)) {
799 if(c == c2) break;
800     }
801            if(c2) {*outptr++ = (char)c;}
802            else {
803 char hex[2];
804 toHex(c,hex);
805 *outptr++ = '%';
806 *outptr++ = hex[0];
807 *outptr++ = hex[1];
808            }
809        }
810    }
811    *outptr = EOFCHAR;
812    return encoded;
813}
814
815/* Return a string representing decoding of input; caller must free;*/
816char*
817ncuridecode(char* s)
818{
819    return ncuridecodeonly(s,NULL);
820}
821
822/* Return a string representing decoding of input only for specified
823   characters;  caller must free
824*/
825char*
826ncuridecodeonly(char* s, char* only)
827{
828    size_t slen;
829    char* decoded;
830    char* outptr;
831    char* inptr;
832    unsigned int c;
833
834    if (s == NULL) return NULL;
835
836    slen = strlen(s);
837    decoded = (char*)malloc(slen+1); /* Should be max we need */
838
839    outptr = decoded;
840    inptr = s;
841    while((c = (unsigned int)*inptr++)) {
842 if(c == '+' && only != NULL && strchr(only,'+') != NULL)
843     *outptr++ = ' ';
844 else if(c == '%') {
845            /* try to pull two hex more characters */
846     if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR
847 && strchr(hexchars,inptr[0]) != NULL
848 && strchr(hexchars,inptr[1]) != NULL) {
849 /* test conversion */
850 int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1]));
851 if(only == NULL || strchr(only,xc) != NULL) {
852     inptr += 2; /* decode it */
853     c = (unsigned int)xc;
854                }
855            }
856        }
857        *outptr++ = (char)c;
858    }
859    *outptr = EOFCHAR;
860    return decoded;
861}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed