ocuri.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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 "oc.h"
  11. #include "ocuri.h"
  12. #define OCURIDEBUG
  13. #define LBRACKET '['
  14. #define RBRACKET ']'
  15. #ifndef FIX
  16. #define FIX(s) ((s)==NULL?"":(s))
  17. #endif
  18. #ifndef NILLEN
  19. #define NILLEN(s) ((s)==NULL?0:strlen(s))
  20. #endif
  21. #ifdef HAVE_STRDUP
  22. # ifndef nulldup
  23. # define nulldup(s) ((s)==NULL?NULL:strdup(s))
  24. # endif
  25. #endif
  26. #ifndef HAVE_STRDUP
  27. static char* nulldup(char* s)
  28. {
  29. char* dup = NULL;
  30. if(s != NULL) {
  31. dup = (char*)malloc(strlen(s)+1);
  32. if(dup != NULL)
  33. strcpy(dup,s);
  34. }
  35. return dup;
  36. }
  37. #endif
  38. #ifdef OCIGNORE
  39. /* Not all systems have strndup, so provide one*/
  40. static char*
  41. ocstrndup(const char* s, size_t len)
  42. {
  43. char* dup;
  44. if(s == NULL) return NULL;
  45. dup = (char*)ocmalloc(len+1);
  46. MEMCHECK(dup,NULL);
  47. memcpy((void*)dup,s,len);
  48. dup[len] = '\0';
  49. return dup;
  50. }
  51. #endif
  52. /* Do not trust strncmp */
  53. static int
  54. ocuristrncmp(const char* s1, const char* s2, size_t len)
  55. {
  56. const char *p,*q;
  57. if(s1 == s2) return 0;
  58. if(s1 == NULL) return -1;
  59. if(s2 == NULL) return +1;
  60. for(p=s1,q=s2;len > 0;p++,q++,len--) {
  61. if(*p != *q)
  62. return (*p - *q);
  63. if(*p == 0) return 0; /* *p == *q == 0 */
  64. }
  65. /* 1st len chars are same */
  66. return 0;
  67. }
  68. static char* legalprotocols[] = {
  69. "file:",
  70. "http:",
  71. "https:",
  72. "ftp:",
  73. NULL /* NULL terminate*/
  74. };
  75. /* Allowable character sets for encode */
  76. static char* fileallow =
  77. "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
  78. static char* queryallow =
  79. "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
  80. static void ocparamfree(char** params);
  81. static int ocfind(char** params, const char* key);
  82. /* Do a simple uri parse: return 0 if fail, 1 otherwise*/
  83. int
  84. ocuriparse(const char* uri0, OCURI** ocurip)
  85. {
  86. OCURI* ocuri = NULL;
  87. char* uri;
  88. char** pp;
  89. char* p;
  90. char* p1;
  91. int c;
  92. /* accumulate parse points*/
  93. char* protocol = NULL;
  94. char* params = NULL;
  95. char* host = NULL;
  96. char* port = NULL;
  97. char* constraint = NULL;
  98. char* user = NULL;
  99. char* pwd = NULL;
  100. char* file = NULL;
  101. if(uri0 == NULL)
  102. return OC_EBADURL;
  103. ocuri = (OCURI*)calloc(1,sizeof(OCURI));
  104. if(ocuri == NULL) return 0;
  105. /* make local copy of uri */
  106. uri = nulldup(uri0);
  107. /* remove all whitespace*/
  108. p = uri;
  109. p1 = uri;
  110. while((c=*p1++)) {if(c != ' ' && c != '\t') *p++ = c;}
  111. p = uri;
  112. /* break up the uri string into pieces*/
  113. /* 1. leading bracketed parameters */
  114. if(*p == LBRACKET) {
  115. params = p+1;
  116. /* find end of the clientparams*/
  117. for(;*p;p++) {if(p[0] == RBRACKET && p[1] != LBRACKET) break;}
  118. if(*p == 0) goto fail; /* malformed client params*/
  119. *p = '\0'; /* leave off the trailing rbracket for now */
  120. p++; /* move past the params*/
  121. }
  122. /* verify that the uri starts with an acceptable protocol*/
  123. for(pp=legalprotocols;*pp;pp++) {
  124. if(ocuristrncmp(p,*pp,strlen(*pp))==0) break;
  125. }
  126. if(*pp == NULL) goto fail; /* illegal protocol*/
  127. /* save the protocol */
  128. protocol = *pp;
  129. /* 4. skip protocol */
  130. p += strlen(protocol);
  131. /* 5. skip // */
  132. if(*p != '/' && *(p+1) != '/')
  133. goto fail;
  134. p += 2;
  135. /* 6. Mark the end of the host section */
  136. file = strchr(p,'/');
  137. if(file) {
  138. *file++ = '\0'; /* warning: we just overwrote the leading / */
  139. } else
  140. goto fail;
  141. /* 7. extract any user:pwd */
  142. p1 = strchr(p,'@');
  143. if(p1) {/* Assume we have user:pwd@ */
  144. *p1 = '\0';
  145. user = p;
  146. pwd = strchr(p,':');
  147. if(!pwd) goto fail; /* malformed */
  148. *pwd++ = '\0';
  149. p = pwd+strlen(pwd)+1;
  150. }
  151. /* 8. extract host and port */
  152. host = p;
  153. port = strchr(p,':');
  154. if(port) {
  155. *port++ = '\0';
  156. }
  157. /* 9. Look for '?' */
  158. constraint = strchr(file,'?');
  159. if(constraint) {
  160. *constraint++ = '\0';
  161. }
  162. /* assemble the component pieces*/
  163. if(uri0 && strlen(uri0) > 0)
  164. ocuri->uri = nulldup(uri0);
  165. if(protocol && strlen(protocol) > 0) {
  166. ocuri->protocol = nulldup(protocol);
  167. /* remove trailing ':' */
  168. ocuri->protocol[strlen(protocol)-1] = '\0';
  169. }
  170. if(user && strlen(user) > 0)
  171. ocuri->user = nulldup(user);
  172. if(pwd && strlen(pwd) > 0)
  173. ocuri->password = nulldup(pwd);
  174. if(host && strlen(host) > 0)
  175. ocuri->host = nulldup(host);
  176. if(port && strlen(port) > 0)
  177. ocuri->port = nulldup(port);
  178. if(file && strlen(file) > 0) {
  179. /* Add back the leading / */
  180. ocuri->file = malloc(strlen(file)+2);
  181. strcpy(ocuri->file,"/");
  182. strcat(ocuri->file,file);
  183. }
  184. if(constraint && strlen(constraint) > 0)
  185. ocuri->constraint = nulldup(constraint);
  186. ocurisetconstraints(ocuri,constraint);
  187. if(params != NULL && strlen(params) > 0) {
  188. ocuri->params = (char*)malloc(1+2+strlen(params));
  189. strcpy(ocuri->params,"[");
  190. strcat(ocuri->params,params);
  191. strcat(ocuri->params,"]");
  192. }
  193. #ifdef OCXDEBUG
  194. {
  195. fprintf(stderr,"ocuri:");
  196. fprintf(stderr," params=|%s|",FIX(ocuri->params));
  197. fprintf(stderr," protocol=|%s|",FIX(ocuri->protocol));
  198. fprintf(stderr," host=|%s|",FIX(ocuri->host));
  199. fprintf(stderr," port=|%s|",FIX(ocuri->port));
  200. fprintf(stderr," file=|%s|",FIX(ocuri->file));
  201. fprintf(stderr," constraint=|%s|",FIX(ocuri->constraint));
  202. fprintf(stderr,"\n");
  203. }
  204. #endif
  205. free(uri);
  206. if(ocurip != NULL) *ocurip = ocuri;
  207. return 1;
  208. fail:
  209. if(ocuri) ocurifree(ocuri);
  210. if(uri != NULL) free(uri);
  211. return 0;
  212. }
  213. void
  214. ocurifree(OCURI* ocuri)
  215. {
  216. if(ocuri == NULL) return;
  217. if(ocuri->uri != NULL) {free(ocuri->uri);}
  218. if(ocuri->protocol != NULL) {free(ocuri->protocol);}
  219. if(ocuri->user != NULL) {free(ocuri->user);}
  220. if(ocuri->password != NULL) {free(ocuri->password);}
  221. if(ocuri->host != NULL) {free(ocuri->host);}
  222. if(ocuri->port != NULL) {free(ocuri->port);}
  223. if(ocuri->file != NULL) {free(ocuri->file);}
  224. if(ocuri->constraint != NULL) {free(ocuri->constraint);}
  225. if(ocuri->projection != NULL) {free(ocuri->projection);}
  226. if(ocuri->selection != NULL) {free(ocuri->selection);}
  227. if(ocuri->params != NULL) {free(ocuri->params);}
  228. if(ocuri->paramlist != NULL) ocparamfree(ocuri->paramlist);
  229. free(ocuri);
  230. }
  231. /* Replace the constraints */
  232. void
  233. ocurisetconstraints(OCURI* duri,const char* constraints)
  234. {
  235. char* proj = NULL;
  236. char* select = NULL;
  237. const char* p;
  238. if(duri->constraint == NULL) free(duri->constraint);
  239. if(duri->projection != NULL) free(duri->projection);
  240. if(duri->selection != NULL) free(duri->selection);
  241. duri->constraint = NULL;
  242. duri->projection = NULL;
  243. duri->selection = NULL;
  244. if(constraints == NULL || strlen(constraints)==0) return;
  245. duri->constraint = nulldup(constraints);
  246. if(*duri->constraint == '?')
  247. strcpy(duri->constraint,duri->constraint+1);
  248. p = duri->constraint;
  249. proj = (char*) p;
  250. select = strchr(proj,'&');
  251. if(select != NULL) {
  252. size_t plen = (select - proj);
  253. if(plen == 0) {
  254. proj = NULL;
  255. } else {
  256. proj = (char*)malloc(plen+1);
  257. memcpy((void*)proj,p,plen);
  258. proj[plen] = '\0';
  259. }
  260. select = nulldup(select);
  261. } else {
  262. proj = nulldup(proj);
  263. select = NULL;
  264. }
  265. duri->projection = proj;
  266. duri->selection = select;
  267. }
  268. /* Construct a complete OC URI without the client params
  269. and optionally with the constraints;
  270. caller frees returned string.
  271. Optionally encode the pieces.
  272. */
  273. char*
  274. ocuribuild(OCURI* duri, const char* prefix, const char* suffix, int flags)
  275. {
  276. size_t len = 0;
  277. char* newuri;
  278. char* tmpfile;
  279. char* tmpsuffix;
  280. char* tmpquery;
  281. int withparams = ((flags&OCURIPARAMS)
  282. && duri->params != NULL);
  283. int withuserpwd = ((flags&OCURIUSERPWD)
  284. && duri->user != NULL && duri->password != NULL);
  285. int withconstraints = ((flags&OCURICONSTRAINTS)
  286. && duri->constraint != NULL);
  287. #ifdef NEWESCAPE
  288. int encode = (flags&OCURIENCODE);
  289. #else
  290. int encode = 0;
  291. #endif
  292. if(prefix != NULL) len += NILLEN(prefix);
  293. if(withparams) {
  294. len += NILLEN("[]");
  295. len += NILLEN(duri->params);
  296. }
  297. len += (NILLEN(duri->protocol)+NILLEN("://"));
  298. if(withuserpwd) {
  299. len += (NILLEN(duri->user)+NILLEN(duri->password)+NILLEN(":@"));
  300. }
  301. len += (NILLEN(duri->host));
  302. if(duri->port != NULL) {
  303. len += (NILLEN(":")+NILLEN(duri->port));
  304. }
  305. tmpfile = duri->file;
  306. if(encode)
  307. tmpfile = ocuriencode(tmpfile,fileallow);
  308. len += (NILLEN(tmpfile));
  309. if(suffix != NULL) {
  310. tmpsuffix = (char*)suffix;
  311. if(encode)
  312. tmpsuffix = ocuriencode(tmpsuffix,fileallow);
  313. len += (NILLEN(tmpsuffix));
  314. }
  315. if(withconstraints) {
  316. tmpquery = duri->constraint;
  317. if(encode)
  318. tmpquery = ocuriencode(tmpquery,queryallow);
  319. len += (NILLEN("?")+NILLEN(tmpquery));
  320. }
  321. len += 1; /* null terminator */
  322. newuri = (char*)malloc(len);
  323. if(newuri == NULL) return NULL;
  324. newuri[0] = '\0';
  325. if(prefix != NULL) strcat(newuri,prefix);
  326. if(withparams) {
  327. strcat(newuri,"[");
  328. strcat(newuri,duri->params);
  329. strcat(newuri,"]");
  330. }
  331. if(duri->protocol != NULL)
  332. strcat(newuri,duri->protocol);
  333. strcat(newuri,"://");
  334. if(withuserpwd) {
  335. strcat(newuri,duri->user);
  336. strcat(newuri,":");
  337. strcat(newuri,duri->password);
  338. strcat(newuri,"@");
  339. }
  340. if(duri->host != NULL) { /* may be null if using file: protocol */
  341. strcat(newuri,duri->host);
  342. }
  343. if(duri->port != NULL) {
  344. strcat(newuri,":");
  345. strcat(newuri,duri->port);
  346. }
  347. strcat(newuri,tmpfile);
  348. if(suffix != NULL) strcat(newuri,tmpsuffix);
  349. if(withconstraints) {
  350. strcat(newuri,"?");
  351. strcat(newuri,tmpquery);
  352. }
  353. return newuri;
  354. }
  355. /**************************************************/
  356. /* Parameter support */
  357. /*
  358. Client parameters are assumed to be
  359. one or more instances of bracketed pairs:
  360. e.g "[...][...]...".
  361. The bracket content in turn is assumed to be a
  362. comma separated list of <name>=<value> pairs.
  363. e.g. x=y,z=,a=b.
  364. If the same parameter is specifed more than once,
  365. then the first occurrence is used; this is so that
  366. is possible to forcibly override user specified
  367. parameters by prefixing.
  368. IMPORTANT: client parameter string is assumed to
  369. have blanks compress out.
  370. Returns 1 if parse suceeded, 0 otherwise;
  371. */
  372. int
  373. ocuridecodeparams(OCURI* ocuri)
  374. {
  375. char* cp;
  376. char* cq;
  377. int c;
  378. int i;
  379. int nparams;
  380. char* params0;
  381. char* params;
  382. char* params1;
  383. char** plist;
  384. if(ocuri == NULL) return 0;
  385. if(ocuri->params == NULL) return 1;
  386. params0 = ocuri->params;
  387. /* Pass 1 to replace beginning '[' and ending ']' */
  388. if(params0[0] == '[')
  389. params = nulldup(params0+1);
  390. else
  391. params = nulldup(params0);
  392. if(params[strlen(params)-1] == ']')
  393. params[strlen(params)-1] = '\0';
  394. /* Pass 2 to replace "][" pairs with ','*/
  395. params1 = nulldup(params);
  396. cp=params; cq = params1;
  397. while((c=*cp++)) {
  398. if(c == RBRACKET && *cp == LBRACKET) {cp++; c = ',';}
  399. *cq++ = c;
  400. }
  401. *cq = '\0';
  402. free(params);
  403. params = params1;
  404. /* Pass 3 to break string into pieces and count # of pairs */
  405. nparams=0;
  406. for(cp=params;(c=*cp);cp++) {
  407. if(c == ',') {*cp = '\0'; nparams++;}
  408. }
  409. nparams++; /* for last one */
  410. /* plist is an env style list */
  411. plist = (char**)calloc(1,sizeof(char*)*(2*nparams+1)); /* +1 for null termination */
  412. /* Pass 4 to break up each pass into a (name,value) pair*/
  413. /* and insert into the param list */
  414. /* parameters of the form name name= are converted to name=""*/
  415. cp = params;
  416. for(i=0;i<nparams;i++) {
  417. char* next = cp+strlen(cp)+1; /* save ptr to next pair*/
  418. char* vp;
  419. /*break up the ith param*/
  420. vp = strchr(cp,'=');
  421. if(vp != NULL) {*vp = '\0'; vp++;} else {vp = "";}
  422. plist[2*i] = nulldup(cp);
  423. plist[2*i+1] = nulldup(vp);
  424. cp = next;
  425. }
  426. plist[2*nparams] = NULL;
  427. free(params);
  428. if(ocuri->paramlist != NULL)
  429. ocparamfree(ocuri->paramlist);
  430. ocuri->paramlist = plist;
  431. return 1;
  432. }
  433. const char*
  434. ocurilookup(OCURI* uri, const char* key)
  435. {
  436. int i;
  437. if(uri == NULL || key == NULL || uri->params == NULL) return NULL;
  438. if(uri->paramlist == NULL) {
  439. i = ocuridecodeparams(uri);
  440. if(!i) return 0;
  441. }
  442. i = ocfind(uri->paramlist,key);
  443. if(i >= 0)
  444. return uri->paramlist[(2*i)+1];
  445. return NULL;
  446. }
  447. int
  448. ocurisetparams(OCURI* uri, const char* newparams)
  449. {
  450. if(uri == NULL) return 0;
  451. if(uri->paramlist != NULL) ocparamfree(uri->paramlist);
  452. uri->paramlist = NULL;
  453. if(uri->params != NULL) free(uri->params);
  454. uri->params = nulldup(newparams);
  455. return 1;
  456. }
  457. /* Internal version of lookup; returns the paired index of the key */
  458. static int
  459. ocfind(char** params, const char* key)
  460. {
  461. int i;
  462. char** p;
  463. for(i=0,p=params;*p;p+=2,i++) {
  464. if(strcmp(key,*p)==0) return i;
  465. }
  466. return -1;
  467. }
  468. static void
  469. ocparamfree(char** params)
  470. {
  471. char** p;
  472. if(params == NULL) return;
  473. for(p=params;*p;p+=2) {
  474. free(*p);
  475. if(p[1] != NULL) free(p[1]);
  476. }
  477. free(params);
  478. }
  479. #ifdef OCIGNORE
  480. /*
  481. Delete the entry.
  482. return value = 1 => found and deleted;
  483. 0 => param not found
  484. */
  485. int
  486. ocparamdelete(char** params, const char* key)
  487. {
  488. int i;
  489. char** p;
  490. char** q;
  491. if(params == NULL || key == NULL) return 0;
  492. i = ocfind(params,key);
  493. if(i < 0) return 0;
  494. p = params+(2*i);
  495. for(q=p+2;*q;) {
  496. *p++ = *q++;
  497. }
  498. *p = NULL;
  499. return 1;
  500. }
  501. static int
  502. oclength(char** params)
  503. {
  504. int i = 0;
  505. if(params != NULL) {
  506. while(*params) {params+=2; i++;}
  507. }
  508. return i;
  509. }
  510. /*
  511. Insert new client param (name,value);
  512. return value = 1 => not already defined
  513. 0 => param already defined (no change)
  514. */
  515. char**
  516. ocparaminsert(char** params, const char* key, const char* value)
  517. {
  518. int i;
  519. char** newp;
  520. size_t len;
  521. if(params == NULL || key == NULL) return 0;
  522. i = ocfind(params,key);
  523. if(i >= 0) return 0;
  524. /* not found, append */
  525. i = oclength(params);
  526. len = sizeof(char*)*((2*i)+1);
  527. newp = realloc(params,len+2*sizeof(char*));
  528. memcpy(newp,params,len);
  529. newp[2*i] = nulldup(key);
  530. newp[2*i+1] = (value==NULL?NULL:nulldup(value));
  531. return newp;
  532. }
  533. /*
  534. Replace new client param (name,value);
  535. return value = 1 => replacement performed
  536. 0 => key not found (no change)
  537. */
  538. int
  539. ocparamreplace(char** params, const char* key, const char* value)
  540. {
  541. int i;
  542. if(params == NULL || key == NULL) return 0;
  543. i = ocfind(params,key);
  544. if(i < 0) return 0;
  545. if(params[2*i+1] != NULL) free(params[2*i+1]);
  546. params[2*i+1] = nulldup(value);
  547. return 1;
  548. }
  549. #endif
  550. /* Provide % encoders and decoders */
  551. static char* hexchars = "0123456789abcdefABCDEF";
  552. static void
  553. toHex(unsigned int b, char hex[2])
  554. {
  555. hex[0] = hexchars[(b >> 4) & 0xff];
  556. hex[1] = hexchars[(b) & 0xff];
  557. }
  558. static unsigned int
  559. fromHex(int c)
  560. {
  561. if(c >= '0' && c <= '9') return (c - '0');
  562. if(c >= 'a' && c <= 'f') return (10 + (c - 'a'));
  563. if(c >= 'A' && c <= 'F') return (10 + (c - 'A'));
  564. return -1;
  565. }
  566. /* Return a string representing encoding of input; caller must free;
  567. watch out: will encode whole string, so watch what you give it.
  568. Allowable argument specifies characters that do not need escaping.
  569. */
  570. char*
  571. ocuriencode(char* s, char* allowable)
  572. {
  573. size_t slen;
  574. char* encoded;
  575. char* inptr;
  576. char* outptr;
  577. if(s == NULL) return NULL;
  578. slen = strlen(s);
  579. encoded = (char*)malloc((3*slen) + 1); /* max possible size */
  580. for(inptr=s,outptr=encoded;*inptr;) {
  581. int c = *inptr++;
  582. if(c == ' ') {
  583. *outptr++ = '+';
  584. } else {
  585. /* search allowable */
  586. int c2;
  587. char* a = allowable;
  588. while((c2=*a++)) {
  589. if(c == c2) break;
  590. }
  591. if(c2) {*outptr++ = c;}
  592. else {
  593. char hex[2];
  594. toHex(c,hex);
  595. *outptr++ = '%';
  596. *outptr++ = hex[0];
  597. *outptr++ = hex[1];
  598. }
  599. }
  600. }
  601. *outptr = '\0';
  602. return encoded;
  603. }
  604. /* Return a string representing decoding of input; caller must free;*/
  605. char*
  606. ocuridecode(char* s)
  607. {
  608. return ocuridecodeonly(s,NULL);
  609. }
  610. /* Return a string representing decoding of input only for specified
  611. characters; caller must free
  612. */
  613. char*
  614. ocuridecodeonly(char* s, char* only)
  615. {
  616. size_t slen;
  617. char* decoded;
  618. char* outptr;
  619. char* inptr;
  620. unsigned int c;
  621. if (s == NULL) return NULL;
  622. if(only == NULL) only = "";
  623. slen = strlen(s);
  624. decoded = (char*)malloc(slen+1); /* Should be max we need */
  625. outptr = decoded;
  626. inptr = s;
  627. while((c = *inptr++)) {
  628. if(c == '+' && strchr(only,'+') != NULL)
  629. *outptr++ = ' ';
  630. else if(c == '%') {
  631. /* try to pull two hex more characters */
  632. if(inptr[0] != '\0' && inptr[1] != '\0'
  633. && strchr(hexchars,inptr[0]) != NULL
  634. && strchr(hexchars,inptr[1]) != NULL) {
  635. /* test conversion */
  636. int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1]));
  637. if(strchr(only,xc) != NULL) {
  638. inptr += 2; /* decode it */
  639. c = xc;
  640. }
  641. }
  642. }
  643. *outptr++ = c;
  644. }
  645. *outptr = '\0';
  646. return decoded;
  647. }