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


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