2012-08-01 04:34:13 +08:00
|
|
|
/*********************************************************************
|
|
|
|
* Copyright 2010, UCAR/Unidata
|
2014-12-25 01:22:47 +08:00
|
|
|
* See netcdf/COPYRIGHT file for copying and redistribuution conditions.
|
2012-08-01 04:34:13 +08:00
|
|
|
* $Header$
|
|
|
|
*********************************************************************/
|
|
|
|
|
2014-06-27 06:40:08 +08:00
|
|
|
#include "config.h"
|
2012-08-01 04:34:13 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2012-08-09 07:15:18 +08:00
|
|
|
#include <assert.h>
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2014-06-27 06:40:08 +08:00
|
|
|
#include "oc.h"
|
2012-08-01 04:34:13 +08:00
|
|
|
#include "ocuri.h"
|
|
|
|
|
2014-12-28 11:42:01 +08:00
|
|
|
#undef OCURIDEBUG
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
#ifdef OCURIDEBUG
|
|
|
|
static int failpoint = 0;
|
|
|
|
#define THROW(n) {failpoint=(n); goto fail;}
|
|
|
|
#else
|
|
|
|
#define THROW(n)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#define PADDING 8
|
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
#define LBRACKET '['
|
|
|
|
#define RBRACKET ']'
|
2012-08-09 07:15:18 +08:00
|
|
|
#define EOFCHAR '\0'
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
#ifndef FIX
|
2012-08-09 07:15:18 +08:00
|
|
|
#define FIX(s) ((s)==NULL?"NULL":(s))
|
2012-08-01 04:34:13 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef NILLEN
|
|
|
|
#define NILLEN(s) ((s)==NULL?0:strlen(s))
|
|
|
|
#endif
|
|
|
|
|
2014-06-27 06:40:08 +08:00
|
|
|
#ifdef HAVE_STRDUP
|
2012-08-09 07:15:18 +08:00
|
|
|
#ifndef nulldup
|
|
|
|
#define nulldup(s) ((s)==NULL?NULL:strdup(s))
|
2012-08-01 04:34:13 +08:00
|
|
|
#endif
|
2014-06-27 06:40:08 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef HAVE_STRDUP
|
|
|
|
static char* nulldup(char* s)
|
|
|
|
{
|
|
|
|
char* dup = NULL;
|
|
|
|
if(s != NULL) {
|
|
|
|
dup = (char*)malloc(strlen(s)+1);
|
|
|
|
if(dup != NULL)
|
|
|
|
strcpy(dup,s);
|
|
|
|
}
|
|
|
|
return dup;
|
|
|
|
}
|
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
#define terminate(p) {*(p) = EOFCHAR;}
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
#define endof(p) ((p)+strlen(p))
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
static struct OC_ProtocolInfo {
|
|
|
|
char* name;
|
|
|
|
int filelike; /* 1=>this protocol has no host, user+pwd, or port */
|
|
|
|
} legalprotocols[] = {
|
|
|
|
{"file",1},
|
|
|
|
{"http",0},
|
|
|
|
{"https",0},
|
|
|
|
{"ftp",0},
|
2012-08-01 04:34:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Allowable character sets for encode */
|
2014-08-09 05:22:46 +08:00
|
|
|
static char* fileallow =
|
2012-08-01 04:34:13 +08:00
|
|
|
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
|
|
|
|
|
2014-08-09 05:22:46 +08:00
|
|
|
static char* queryallow =
|
2012-08-01 04:34:13 +08:00
|
|
|
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~";
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Forward */
|
2012-08-01 04:34:13 +08:00
|
|
|
static void ocparamfree(char** params);
|
|
|
|
static int ocfind(char** params, const char* key);
|
2012-08-09 07:15:18 +08:00
|
|
|
static void oclshift1(char* p);
|
|
|
|
static void ocrshift1(char* p);
|
|
|
|
static char* oclocate(char* p, const char* charlist);
|
|
|
|
static void ocappendparams(char* newuri, char** p);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
/* Do a simple uri parse: return 0 if fail, 1 otherwise*/
|
|
|
|
int
|
2012-08-20 05:54:30 +08:00
|
|
|
ocuriparse(const char* uri0, OCURI** durip)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2012-08-20 05:54:30 +08:00
|
|
|
OCURI* duri = NULL;
|
2012-08-03 02:43:21 +08:00
|
|
|
char* uri = NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
char* p;
|
2014-12-28 11:42:01 +08:00
|
|
|
char* q;
|
2012-08-09 07:15:18 +08:00
|
|
|
struct OC_ProtocolInfo* proto;
|
|
|
|
int i,nprotos;
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
/* accumulate parse points*/
|
|
|
|
char* protocol = NULL;
|
|
|
|
char* host = NULL;
|
|
|
|
char* port = NULL;
|
|
|
|
char* constraint = NULL;
|
2014-12-25 01:22:47 +08:00
|
|
|
char* userpwd = NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
char* file = NULL;
|
|
|
|
char* prefixparams = NULL;
|
|
|
|
char* suffixparams = NULL;
|
|
|
|
|
|
|
|
if(uri0 == NULL || strlen(uri0) == 0)
|
2012-08-09 07:15:18 +08:00
|
|
|
{THROW(1); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-20 05:54:30 +08:00
|
|
|
duri = (OCURI*)calloc(1,sizeof(OCURI));
|
|
|
|
if(duri == NULL)
|
2012-08-09 07:15:18 +08:00
|
|
|
{THROW(2); goto fail;}
|
2014-08-09 05:22:46 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* save original uri */
|
2012-08-20 05:54:30 +08:00
|
|
|
duri->uri = nulldup(uri0);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
/* make local copy of uri */
|
2012-08-09 07:15:18 +08:00
|
|
|
uri = (char*)malloc(strlen(uri0)+1+PADDING); /* +1 for trailing null,
|
|
|
|
+PADDING for shifting */
|
|
|
|
if(uri == NULL)
|
|
|
|
{THROW(3); goto fail;}
|
|
|
|
|
|
|
|
/* strings will be broken into pieces with intermixed '\0; characters;
|
|
|
|
first char is guaranteed to be '\0' */
|
|
|
|
|
2012-08-20 05:54:30 +08:00
|
|
|
duri->strings = uri;
|
2014-08-09 05:22:46 +08:00
|
|
|
uri++;
|
2012-08-09 07:15:18 +08:00
|
|
|
|
|
|
|
/* dup the incoming url */
|
2012-08-01 04:34:13 +08:00
|
|
|
strcpy(uri,uri0);
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Walk the uri and do the following:
|
|
|
|
1. remove all whitespace
|
|
|
|
2. remove all '\\' (Temp hack to remove escape characters
|
|
|
|
inserted by Windows or MinGW)
|
|
|
|
*/
|
2014-12-28 11:42:01 +08:00
|
|
|
for(q=uri,p=uri;*p;p++) {
|
|
|
|
if(*p != '\\' && *p >= ' ') /* compress out */
|
|
|
|
*q++=*p;
|
2014-08-09 05:22:46 +08:00
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
p = uri;
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* break up the uri string into big chunks: prefixparams, protocol,
|
|
|
|
host section, and the file section (i.e. remainder)
|
|
|
|
*/
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* collect any prefix bracketed parameters */
|
2012-08-01 04:34:13 +08:00
|
|
|
if(*p == LBRACKET) {
|
2014-12-28 11:42:01 +08:00
|
|
|
p++;
|
|
|
|
prefixparams = p;
|
|
|
|
/* find end of the clientparams; convert LB,RB to '&' */
|
|
|
|
for(q=p;*p;p++) {
|
2012-08-09 07:15:18 +08:00
|
|
|
if(p[0] == RBRACKET && p[1] == LBRACKET) {
|
2014-12-28 11:42:01 +08:00
|
|
|
*q++ = '&';
|
|
|
|
p++;
|
2012-08-09 07:15:18 +08:00
|
|
|
} else if(p[0] == RBRACKET && p[1] != LBRACKET)
|
|
|
|
break;
|
2014-12-28 11:42:01 +08:00
|
|
|
else
|
|
|
|
*q++=*p;
|
2012-08-09 07:15:18 +08:00
|
|
|
}
|
|
|
|
if(*p == 0)
|
|
|
|
{THROW(4); goto fail; /* malformed client params*/}
|
2014-12-28 11:42:01 +08:00
|
|
|
terminate(q); /* nul term the prefixparams */
|
2012-08-09 07:15:18 +08:00
|
|
|
p++; /* move past the final RBRACKET */
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Tag the protocol */
|
2014-08-09 05:22:46 +08:00
|
|
|
protocol = p;
|
2012-08-09 07:15:18 +08:00
|
|
|
p = strchr(p,':');
|
|
|
|
if(!p)
|
|
|
|
{THROW(5); goto fail;}
|
|
|
|
terminate(p); /*overwrite colon*/
|
|
|
|
p++; /* skip the colon */
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
/* verify that the uri starts with an acceptable protocol*/
|
2012-08-09 07:15:18 +08:00
|
|
|
nprotos = (sizeof(legalprotocols)/sizeof(struct OC_ProtocolInfo));
|
|
|
|
proto = NULL;
|
|
|
|
for(i=0;i<nprotos;i++) {
|
|
|
|
if(strcmp(protocol,legalprotocols[i].name)==0) {
|
|
|
|
proto = &legalprotocols[i];
|
2014-08-09 05:22:46 +08:00
|
|
|
break;
|
2012-08-09 07:15:18 +08:00
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
if(proto == NULL)
|
|
|
|
{THROW(6); goto fail; /* illegal protocol*/}
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
/* skip // */
|
2014-10-06 01:28:03 +08:00
|
|
|
if(p[0] != '/' || p[1] != '/')
|
2012-08-09 07:15:18 +08:00
|
|
|
{THROW(7); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
p += 2;
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* If this is all we have (proto://) then fail */
|
|
|
|
if(*p == EOFCHAR)
|
|
|
|
{THROW(8); goto fail;}
|
|
|
|
|
|
|
|
/* establish the start of the file section */
|
|
|
|
if(proto->filelike) {/* everything after proto:// */
|
|
|
|
file = p;
|
|
|
|
host = NULL; /* and no host section */
|
|
|
|
} else { /*!proto->filelike => This means there should be a host section */
|
|
|
|
/* locate the end of the host section and therefore the start
|
|
|
|
of the file section */
|
|
|
|
host = p;
|
|
|
|
p = oclocate(p,"/?#");
|
|
|
|
if(p == NULL) {
|
|
|
|
file = endof(host); /* there is no file section */
|
|
|
|
} else {
|
|
|
|
ocrshift1(p); /* make room to terminate the host section
|
|
|
|
without overwriting the leading character */
|
|
|
|
terminate(p); /* terminate the host section */
|
|
|
|
file = p+1; /* +1 becauseof the shift */
|
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
2014-08-09 05:22:46 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* If you shift in the code below, you must reset file beginning */
|
|
|
|
|
|
|
|
if(host != NULL) {/* Parse the host section */
|
|
|
|
/* Check for leading user:pwd@ */
|
|
|
|
p = strchr(host,'@');
|
|
|
|
if(p) {
|
|
|
|
if(p == host)
|
|
|
|
{THROW(9); goto fail; /* we have proto://@ */}
|
2014-12-25 01:22:47 +08:00
|
|
|
userpwd = host;
|
2012-08-09 07:15:18 +08:00
|
|
|
terminate(p); /* overwrite '@' */
|
|
|
|
host = p+1; /* start of host ip name */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* extract host and port */
|
|
|
|
p = host;
|
|
|
|
p = strchr(p,':');
|
|
|
|
if(p != NULL) {
|
|
|
|
terminate(p);
|
|
|
|
p++;
|
|
|
|
port = p;
|
|
|
|
if(*port == EOFCHAR)
|
|
|
|
{THROW(11); goto fail; /* we have proto://...:/ */}
|
|
|
|
/* The port must look something like a number */
|
|
|
|
for(;*p;p++) {
|
|
|
|
if(strchr("0123456789-",*p) == NULL)
|
|
|
|
{THROW(12); goto fail; /* probably not a real port, fail */}
|
|
|
|
}
|
|
|
|
} /* else *p == NULL */
|
|
|
|
|
|
|
|
|
|
|
|
/* check for empty host section */
|
|
|
|
if(*host == EOFCHAR)
|
|
|
|
{THROW(13); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
assert(file != NULL);
|
|
|
|
p = file;
|
|
|
|
|
|
|
|
/* find the end of the file section and the start of the
|
|
|
|
constraints and/or suffixparams
|
|
|
|
*/
|
|
|
|
p = oclocate(p,"?#");
|
|
|
|
if(p != NULL) { /* we have constraint and/or suffixparams */
|
|
|
|
char* fileend = p; /* save the end of the file section */
|
2014-08-09 05:22:46 +08:00
|
|
|
char* constraintend = NULL;
|
2012-08-09 07:15:18 +08:00
|
|
|
if(*p == '?')
|
|
|
|
constraint = p+1;
|
|
|
|
else
|
|
|
|
constraint = NULL;
|
|
|
|
p = strchr(p,'#'); /* may repeat effect of oclocate above */
|
|
|
|
if(p != NULL) {
|
|
|
|
constraintend = p;
|
|
|
|
suffixparams = p+1;
|
|
|
|
} else
|
|
|
|
suffixparams = NULL;
|
|
|
|
/* Ok, terminate the pieces */
|
|
|
|
terminate(fileend); /* terminate file section */
|
|
|
|
if(constraint != NULL && constraintend != NULL)
|
|
|
|
terminate(constraintend);
|
|
|
|
/* Suffix params are already terminated
|
|
|
|
since they should be the last section
|
|
|
|
of the original url
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for empty sections */
|
|
|
|
if(file != NULL && *file == EOFCHAR)
|
|
|
|
file = NULL; /* empty file section */
|
|
|
|
if(constraint != NULL && *constraint == EOFCHAR)
|
|
|
|
constraint = NULL; /* empty constraint section */
|
|
|
|
if(suffixparams != NULL && *suffixparams == EOFCHAR)
|
|
|
|
suffixparams = NULL; /* empty suffixparams section */
|
|
|
|
|
2012-08-03 02:43:21 +08:00
|
|
|
if(suffixparams != NULL) {
|
2012-08-09 07:15:18 +08:00
|
|
|
if(*suffixparams == EOFCHAR)
|
|
|
|
suffixparams = NULL; /* suffixparams are empty */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do last minute empty check */
|
|
|
|
if(protocol != NULL && *protocol == EOFCHAR) protocol = NULL;
|
2014-12-25 01:22:47 +08:00
|
|
|
if(userpwd != NULL && *userpwd == EOFCHAR) userpwd = NULL;
|
2012-08-09 07:15:18 +08:00
|
|
|
if(host != NULL && *host == EOFCHAR) host = NULL;
|
|
|
|
if(port != NULL && *port == EOFCHAR) port = NULL;
|
|
|
|
if(file != NULL && *file == EOFCHAR) file = NULL;
|
|
|
|
if(constraint != NULL && *constraint == EOFCHAR) constraint = NULL;
|
|
|
|
|
|
|
|
/* assemble the component pieces */
|
2012-08-20 05:54:30 +08:00
|
|
|
duri->protocol = protocol;
|
2014-12-25 01:22:47 +08:00
|
|
|
duri->userpwd = userpwd;
|
2012-08-20 05:54:30 +08:00
|
|
|
duri->host = host;
|
|
|
|
duri->port = port;
|
|
|
|
duri->file = file;
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-20 05:54:30 +08:00
|
|
|
ocurisetconstraints(duri,constraint);
|
2012-08-09 07:15:18 +08:00
|
|
|
|
|
|
|
/* concat suffix and prefix params */
|
2012-08-01 04:34:13 +08:00
|
|
|
if(prefixparams != NULL || suffixparams != NULL) {
|
2014-03-09 11:41:30 +08:00
|
|
|
size_t plen = prefixparams ? strlen(prefixparams) : 0;
|
|
|
|
size_t slen = suffixparams ? strlen(suffixparams) : 0;
|
|
|
|
size_t space = plen + slen + 1;
|
2014-12-28 11:42:01 +08:00
|
|
|
/* add 1 for an extra ampersand if both are defined */
|
2015-01-21 02:12:51 +08:00
|
|
|
if(plen > 0 && slen > 0) space++;
|
|
|
|
/* Add an extra char for null termination. */
|
|
|
|
duri->params = (char*)malloc(space+1);
|
|
|
|
if(duri->params == NULL)
|
|
|
|
return 0;
|
|
|
|
duri->params[0] = EOFCHAR; /* so we can use strcat */
|
2012-08-09 07:15:18 +08:00
|
|
|
if(plen > 0) {
|
2015-01-21 02:12:51 +08:00
|
|
|
strncat(duri->params,prefixparams,space);
|
|
|
|
if(slen > 0)
|
2014-12-28 11:42:01 +08:00
|
|
|
strncat(duri->params,"&",space);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
if(slen > 0)
|
2015-01-21 02:12:51 +08:00
|
|
|
strncat(duri->params,suffixparams,space);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
2014-12-28 11:42:01 +08:00
|
|
|
#ifdef OCURIDEBUG
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2014-12-28 11:42:01 +08:00
|
|
|
int i,nparms;
|
|
|
|
char** p;
|
2012-08-20 05:54:30 +08:00
|
|
|
fprintf(stderr,"duri:");
|
|
|
|
fprintf(stderr," protocol=|%s|",FIX(duri->protocol));
|
|
|
|
fprintf(stderr," host=|%s|",FIX(duri->host));
|
|
|
|
fprintf(stderr," port=|%s|",FIX(duri->port));
|
|
|
|
fprintf(stderr," file=|%s|",FIX(duri->file));
|
|
|
|
fprintf(stderr," constraint=|%s|",FIX(duri->constraint));
|
2014-12-28 11:42:01 +08:00
|
|
|
fprintf(stderr," params=|%s|",FIX(duri->params));
|
2012-08-01 04:34:13 +08:00
|
|
|
fprintf(stderr,"\n");
|
2014-12-28 11:42:01 +08:00
|
|
|
if(duri->paramlist == NULL) {
|
|
|
|
if(!ocuridecodeparams(duri)) {
|
|
|
|
fprintf(stderr,"DEBUG: param decode failed\n");
|
|
|
|
duri->paramlist = NULL;
|
|
|
|
}
|
2015-01-21 02:12:51 +08:00
|
|
|
}
|
2014-12-28 11:42:01 +08:00
|
|
|
if(duri->paramlist != NULL) {
|
|
|
|
for(p=duri->paramlist,nparms=0;*p;p++,nparms++);
|
|
|
|
nparms = nparms / 2;
|
|
|
|
fprintf(stderr,"params:");
|
|
|
|
for(i=0;i<nparms;i++) {
|
|
|
|
char** pos = duri->paramlist+(i*2);
|
|
|
|
fprintf(stderr," %s=|%s|",pos[0],pos[1]);
|
|
|
|
}
|
|
|
|
fprintf(stderr,"\n");
|
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
#endif
|
2012-11-22 07:57:42 +08:00
|
|
|
if(durip != NULL) *durip = duri; else free(duri);
|
2012-08-01 04:34:13 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail:
|
2012-08-20 05:54:30 +08:00
|
|
|
if(duri != NULL) {
|
|
|
|
ocurifree(duri);
|
2012-08-09 07:15:18 +08:00
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-08-20 05:54:30 +08:00
|
|
|
ocurifree(OCURI* duri)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2012-08-20 05:54:30 +08:00
|
|
|
if(duri == NULL) return;
|
|
|
|
if(duri->uri != NULL) {free(duri->uri);}
|
|
|
|
if(duri->params != NULL) {free(duri->params);}
|
|
|
|
if(duri->paramlist != NULL) ocparamfree(duri->paramlist);
|
|
|
|
if(duri->strings != NULL) {free(duri->strings);}
|
|
|
|
if(duri->constraint != NULL) {free(duri->constraint);}
|
|
|
|
if(duri->projection != NULL) {free(duri->projection);}
|
|
|
|
if(duri->selection != NULL) {free(duri->selection);}
|
|
|
|
free(duri);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Replace the constraints */
|
|
|
|
void
|
|
|
|
ocurisetconstraints(OCURI* duri,const char* constraints)
|
|
|
|
{
|
|
|
|
char* proj = NULL;
|
|
|
|
char* select = NULL;
|
|
|
|
const char* p;
|
|
|
|
|
2012-08-03 02:43:21 +08:00
|
|
|
if(duri->constraint != NULL) free(duri->constraint);
|
2012-08-01 04:34:13 +08:00
|
|
|
if(duri->projection != NULL) free(duri->projection);
|
|
|
|
if(duri->selection != NULL) free(duri->selection);
|
2014-08-09 05:22:46 +08:00
|
|
|
duri->constraint = NULL;
|
|
|
|
duri->projection = NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
duri->selection = NULL;
|
|
|
|
|
|
|
|
if(constraints == NULL || strlen(constraints)==0) return;
|
|
|
|
|
|
|
|
duri->constraint = nulldup(constraints);
|
|
|
|
if(*duri->constraint == '?')
|
2012-08-09 07:15:18 +08:00
|
|
|
oclshift1(duri->constraint);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
p = duri->constraint;
|
|
|
|
proj = (char*) p;
|
|
|
|
select = strchr(proj,'&');
|
|
|
|
if(select != NULL) {
|
|
|
|
size_t plen = (select - proj);
|
|
|
|
if(plen == 0) {
|
|
|
|
proj = NULL;
|
|
|
|
} else {
|
|
|
|
proj = (char*)malloc(plen+1);
|
|
|
|
memcpy((void*)proj,p,plen);
|
2012-08-09 07:15:18 +08:00
|
|
|
proj[plen] = EOFCHAR;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
select = nulldup(select);
|
|
|
|
} else {
|
|
|
|
proj = nulldup(proj);
|
|
|
|
select = NULL;
|
|
|
|
}
|
|
|
|
duri->projection = proj;
|
|
|
|
duri->selection = select;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Construct a complete OC URI.
|
|
|
|
Optionally with the constraints.
|
|
|
|
Optionally with the user parameters.
|
|
|
|
Caller frees returned string.
|
2012-08-01 04:34:13 +08:00
|
|
|
Optionally encode the pieces.
|
|
|
|
*/
|
|
|
|
|
|
|
|
char*
|
|
|
|
ocuribuild(OCURI* duri, const char* prefix, const char* suffix, int flags)
|
|
|
|
{
|
|
|
|
size_t len = 0;
|
|
|
|
char* newuri;
|
|
|
|
char* tmpfile;
|
|
|
|
char* tmpsuffix;
|
|
|
|
char* tmpquery;
|
2012-08-09 07:15:18 +08:00
|
|
|
int nparams = 0;
|
|
|
|
int paramslen = 0;
|
|
|
|
|
|
|
|
/* if both are specified, prefix has priority */
|
|
|
|
int withsuffixparams = ((flags&OCURISUFFIXPARAMS)!=0
|
|
|
|
&& duri->params != NULL);
|
|
|
|
int withprefixparams = ((flags&OCURIPREFIXPARAMS)!=0
|
|
|
|
&& duri->params != NULL);
|
|
|
|
int withuserpwd = ((flags&OCURIUSERPWD)!=0
|
2014-12-25 01:22:47 +08:00
|
|
|
&& duri->userpwd != NULL);
|
2012-08-09 07:15:18 +08:00
|
|
|
int withconstraints = ((flags&OCURICONSTRAINTS)!=0
|
2012-08-01 04:34:13 +08:00
|
|
|
&& duri->constraint != NULL);
|
|
|
|
#ifdef NEWESCAPE
|
|
|
|
int encode = (flags&OCURIENCODE);
|
|
|
|
#else
|
|
|
|
int encode = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if(prefix != NULL) len += NILLEN(prefix);
|
|
|
|
len += (NILLEN(duri->protocol)+NILLEN("://"));
|
2014-12-25 01:22:47 +08:00
|
|
|
if(withuserpwd)
|
|
|
|
len += (NILLEN(duri->userpwd)+NILLEN("@"));
|
2012-08-01 04:34:13 +08:00
|
|
|
len += (NILLEN(duri->host));
|
|
|
|
if(duri->port != NULL) {
|
|
|
|
len += (NILLEN(":")+NILLEN(duri->port));
|
|
|
|
}
|
2014-08-09 05:22:46 +08:00
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
tmpfile = duri->file;
|
|
|
|
if(encode)
|
|
|
|
tmpfile = ocuriencode(tmpfile,fileallow);
|
|
|
|
len += (NILLEN(tmpfile));
|
|
|
|
|
|
|
|
if(suffix != NULL) {
|
|
|
|
tmpsuffix = (char*)suffix;
|
|
|
|
if(encode)
|
|
|
|
tmpsuffix = ocuriencode(tmpsuffix,fileallow);
|
|
|
|
len += (NILLEN(tmpsuffix));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(withconstraints) {
|
|
|
|
tmpquery = duri->constraint;
|
|
|
|
if(encode)
|
|
|
|
tmpquery = ocuriencode(tmpquery,queryallow);
|
|
|
|
len += (NILLEN("?")+NILLEN(tmpquery));
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
if(withprefixparams || withsuffixparams) {
|
|
|
|
char** p;
|
|
|
|
if(duri->paramlist == NULL)
|
|
|
|
if(!ocuridecodeparams(duri))
|
2014-08-09 05:22:46 +08:00
|
|
|
return NULL;
|
2012-08-09 07:15:18 +08:00
|
|
|
for(paramslen=0,nparams=0,p=duri->paramlist;*p;p++) {
|
|
|
|
nparams++;
|
|
|
|
paramslen += NILLEN(*p);
|
|
|
|
}
|
|
|
|
if(nparams % 2 == 1)
|
|
|
|
return NULL; /* malformed */
|
|
|
|
nparams = (nparams / 2);
|
|
|
|
len += paramslen;
|
|
|
|
len += 3*nparams; /* for brackets for every param plus possible = */
|
|
|
|
if(withsuffixparams)
|
|
|
|
len += strlen("#");
|
|
|
|
}
|
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
len += 1; /* null terminator */
|
2014-08-09 05:22:46 +08:00
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
newuri = (char*)malloc(len);
|
|
|
|
if(newuri == NULL) return NULL;
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
newuri[0] = EOFCHAR;
|
2012-08-01 04:34:13 +08:00
|
|
|
if(prefix != NULL) strcat(newuri,prefix);
|
2012-08-09 07:15:18 +08:00
|
|
|
if(withprefixparams) {
|
|
|
|
ocappendparams(newuri,duri->paramlist);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
if(duri->protocol != NULL)
|
|
|
|
strcat(newuri,duri->protocol);
|
|
|
|
strcat(newuri,"://");
|
|
|
|
if(withuserpwd) {
|
2014-12-25 01:22:47 +08:00
|
|
|
strcat(newuri,duri->userpwd);
|
2012-08-01 04:34:13 +08:00
|
|
|
strcat(newuri,"@");
|
|
|
|
}
|
|
|
|
if(duri->host != NULL) { /* may be null if using file: protocol */
|
2014-08-09 05:22:46 +08:00
|
|
|
strcat(newuri,duri->host);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
if(duri->port != NULL) {
|
|
|
|
strcat(newuri,":");
|
|
|
|
strcat(newuri,duri->port);
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
if(tmpfile != NULL) {
|
|
|
|
strcat(newuri,tmpfile);
|
|
|
|
if(suffix != NULL) strcat(newuri,tmpsuffix);
|
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
if(withconstraints) {
|
|
|
|
strcat(newuri,"?");
|
|
|
|
strcat(newuri,tmpquery);
|
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
if(withsuffixparams & !withprefixparams) {
|
|
|
|
strcat(newuri,"#");
|
|
|
|
ocappendparams(newuri,duri->paramlist);
|
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
return newuri;
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
static void
|
|
|
|
ocappendparams(char* newuri, char** p)
|
|
|
|
{
|
|
|
|
while(*p) {
|
|
|
|
strcat(newuri,"[");
|
|
|
|
strcat(newuri,*p++);
|
|
|
|
if(strlen(*p) > 0) {
|
|
|
|
strcat(newuri,"=");
|
|
|
|
strcat(newuri,*p);
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
strcat(newuri,"]");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
/**************************************************/
|
|
|
|
/* Parameter support */
|
|
|
|
|
|
|
|
/*
|
2012-08-09 07:15:18 +08:00
|
|
|
In the original url, client parameters are assumed to be one
|
|
|
|
or more instances of bracketed pairs: e.g "[...][...]...".
|
2014-12-25 01:22:47 +08:00
|
|
|
prefixed to the url. This model has been extended to support
|
|
|
|
specification of the parameters as semicolon separated key=value
|
|
|
|
pairs in the fragment part of the url. The fragment part
|
|
|
|
starts with a '#' and is the last part of the url.
|
2012-08-09 07:15:18 +08:00
|
|
|
|
|
|
|
After the url is parsed, the parameter list
|
|
|
|
is converted to a semicolon separated list with all
|
|
|
|
whitespace removed.
|
|
|
|
In any case, each parameter in turn is assumed to be a
|
2014-12-25 01:22:47 +08:00
|
|
|
of the form <name>=<value> or <name>.
|
|
|
|
e.g. x=y,z,a=b,w. If the same parameter is specified more
|
|
|
|
than once, then the last occurrence is used; this is so
|
2012-08-09 07:15:18 +08:00
|
|
|
that is possible to forcibly override user specified
|
2014-12-25 01:22:47 +08:00
|
|
|
parameters by suffixing. IMPORTANT: client parameter string
|
2012-08-09 07:15:18 +08:00
|
|
|
is assumed to have blanks compressed out. Returns 1 if parse
|
2015-08-20 17:42:05 +08:00
|
|
|
succeeded, 0 otherwise; */
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
int
|
|
|
|
ocuridecodeparams(OCURI* ocuri)
|
|
|
|
{
|
2014-12-25 01:22:47 +08:00
|
|
|
char* p;
|
2012-08-09 07:15:18 +08:00
|
|
|
int i,c;
|
2012-08-01 04:34:13 +08:00
|
|
|
int nparams;
|
2014-08-09 05:22:46 +08:00
|
|
|
char* params = NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
char** plist;
|
|
|
|
|
|
|
|
if(ocuri == NULL) return 0;
|
|
|
|
if(ocuri->params == NULL) return 1;
|
|
|
|
|
2014-12-28 11:42:01 +08:00
|
|
|
params = strdup(ocuri->params);
|
2014-12-25 01:22:47 +08:00
|
|
|
if(params == NULL)
|
|
|
|
return 0; /* no memory */
|
|
|
|
|
2014-12-28 11:42:01 +08:00
|
|
|
/* Pass 1: break string into pieces at the ampersands
|
2012-08-09 07:15:18 +08:00
|
|
|
and count # of pairs */
|
2012-08-01 04:34:13 +08:00
|
|
|
nparams=0;
|
2014-12-28 11:42:01 +08:00
|
|
|
for(p=params;*p;p++) {
|
|
|
|
c = *p;
|
|
|
|
if(c == '&') {*p = EOFCHAR; nparams++;}
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
nparams++; /* for last one */
|
|
|
|
|
2014-12-28 11:42:01 +08:00
|
|
|
/* plist will be an env style list */
|
2012-08-01 04:34:13 +08:00
|
|
|
plist = (char**)calloc(1,sizeof(char*)*(2*nparams+1)); /* +1 for null termination */
|
2014-08-09 05:22:46 +08:00
|
|
|
if(plist == NULL) {
|
2014-11-22 07:20:44 +08:00
|
|
|
free(params);
|
|
|
|
return 0;
|
2014-08-09 05:22:46 +08:00
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Break up each param into a (name,value) pair*/
|
2012-08-01 04:34:13 +08:00
|
|
|
/* and insert into the param list */
|
|
|
|
/* parameters of the form name name= are converted to name=""*/
|
2014-12-25 01:22:47 +08:00
|
|
|
for(p=params,i=0;i<nparams;i++) {
|
|
|
|
char* next = p+strlen(p)+1; /* save ptr to next pair*/
|
2014-08-09 05:22:46 +08:00
|
|
|
char* vp;
|
|
|
|
/*break up the ith param*/
|
2014-12-25 01:22:47 +08:00
|
|
|
vp = strchr(p,'=');
|
2014-08-09 05:22:46 +08:00
|
|
|
if(vp != NULL) {*vp = EOFCHAR; vp++;} else {vp = "";}
|
2014-12-25 01:22:47 +08:00
|
|
|
plist[2*i] = nulldup(p);
|
2014-08-09 05:22:46 +08:00
|
|
|
plist[2*i+1] = nulldup(vp);
|
2014-12-25 01:22:47 +08:00
|
|
|
p = next;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
plist[2*nparams] = NULL;
|
|
|
|
free(params);
|
|
|
|
if(ocuri->paramlist != NULL)
|
|
|
|
ocparamfree(ocuri->paramlist);
|
|
|
|
ocuri->paramlist = plist;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
int
|
|
|
|
ocurilookup(OCURI* uri, const char* key, const char** resultp)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
|
|
|
int i;
|
2012-08-09 07:15:18 +08:00
|
|
|
char* value = NULL;
|
|
|
|
if(uri == NULL || key == NULL || uri->params == NULL) return 0;
|
2012-08-01 04:34:13 +08:00
|
|
|
if(uri->paramlist == NULL) {
|
|
|
|
i = ocuridecodeparams(uri);
|
|
|
|
if(!i) return 0;
|
|
|
|
}
|
|
|
|
i = ocfind(uri->paramlist,key);
|
2012-08-09 07:15:18 +08:00
|
|
|
if(i < 0)
|
|
|
|
return 0;
|
|
|
|
value = uri->paramlist[(2*i)+1];
|
|
|
|
if(resultp) *resultp = value;
|
|
|
|
return 1;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ocurisetparams(OCURI* uri, const char* newparams)
|
|
|
|
{
|
|
|
|
if(uri == NULL) return 0;
|
|
|
|
if(uri->paramlist != NULL) ocparamfree(uri->paramlist);
|
|
|
|
uri->paramlist = NULL;
|
|
|
|
if(uri->params != NULL) free(uri->params);
|
|
|
|
uri->params = nulldup(newparams);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Internal version of lookup; returns the paired index of the key */
|
|
|
|
static int
|
|
|
|
ocfind(char** params, const char* key)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char** p;
|
|
|
|
for(i=0,p=params;*p;p+=2,i++) {
|
|
|
|
if(strcmp(key,*p)==0) return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ocparamfree(char** params)
|
|
|
|
{
|
|
|
|
char** p;
|
|
|
|
if(params == NULL) return;
|
|
|
|
for(p=params;*p;p+=2) {
|
|
|
|
free(*p);
|
|
|
|
if(p[1] != NULL) free(p[1]);
|
|
|
|
}
|
|
|
|
free(params);
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
|
|
|
|
/* Return the ptr to the first occurrence of
|
|
|
|
any char in the list. Return NULL if no
|
|
|
|
occurrences
|
2012-08-01 04:34:13 +08:00
|
|
|
*/
|
2012-08-09 07:15:18 +08:00
|
|
|
static char*
|
|
|
|
oclocate(char* p, const char* charlist)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2012-08-09 07:15:18 +08:00
|
|
|
for(;*p;p++) {
|
|
|
|
if(strchr(charlist,*p) != NULL)
|
|
|
|
return p;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
return NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Shift every char starting at p 1 place to the left */
|
|
|
|
static void
|
|
|
|
oclshift1(char* p)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2012-08-09 07:15:18 +08:00
|
|
|
if(p != NULL && *p != EOFCHAR) {
|
|
|
|
char* q = p++;
|
|
|
|
while((*q++=*p++));
|
|
|
|
}
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
2012-08-09 07:15:18 +08:00
|
|
|
/* Shift every char starting at p 1 place to the right */
|
|
|
|
static void
|
|
|
|
ocrshift1(char* p)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2012-08-09 07:15:18 +08:00
|
|
|
char cur;
|
|
|
|
cur = 0;
|
|
|
|
do {
|
|
|
|
char next = *p;
|
|
|
|
*p++ = cur;
|
|
|
|
cur = next;
|
|
|
|
} while(cur != 0);
|
|
|
|
*p = 0; /* make sure we are still null terminated */
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Provide % encoders and decoders */
|
|
|
|
|
|
|
|
|
|
|
|
static char* hexchars = "0123456789abcdefABCDEF";
|
|
|
|
|
|
|
|
static void
|
2014-03-09 11:41:30 +08:00
|
|
|
toHex(int b, char* hex)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
|
|
|
hex[0] = hexchars[(b >> 4) & 0xff];
|
|
|
|
hex[1] = hexchars[(b) & 0xff];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-09 11:41:30 +08:00
|
|
|
static int
|
2012-08-01 04:34:13 +08:00
|
|
|
fromHex(int c)
|
|
|
|
{
|
|
|
|
if(c >= '0' && c <= '9') return (c - '0');
|
|
|
|
if(c >= 'a' && c <= 'f') return (10 + (c - 'a'));
|
|
|
|
if(c >= 'A' && c <= 'F') return (10 + (c - 'A'));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return a string representing encoding of input; caller must free;
|
|
|
|
watch out: will encode whole string, so watch what you give it.
|
|
|
|
Allowable argument specifies characters that do not need escaping.
|
|
|
|
*/
|
|
|
|
|
|
|
|
char*
|
|
|
|
ocuriencode(char* s, char* allowable)
|
|
|
|
{
|
|
|
|
size_t slen;
|
|
|
|
char* encoded;
|
|
|
|
char* inptr;
|
|
|
|
char* outptr;
|
|
|
|
|
|
|
|
if(s == NULL) return NULL;
|
|
|
|
|
|
|
|
slen = strlen(s);
|
|
|
|
encoded = (char*)malloc((3*slen) + 1); /* max possible size */
|
|
|
|
|
|
|
|
for(inptr=s,outptr=encoded;*inptr;) {
|
|
|
|
int c = *inptr++;
|
|
|
|
if(c == ' ') {
|
|
|
|
*outptr++ = '+';
|
|
|
|
} else {
|
|
|
|
/* search allowable */
|
|
|
|
int c2;
|
|
|
|
char* a = allowable;
|
|
|
|
while((c2=*a++)) {
|
|
|
|
if(c == c2) break;
|
|
|
|
}
|
|
|
|
if(c2) {*outptr++ = c;}
|
|
|
|
else {
|
|
|
|
char hex[2];
|
|
|
|
toHex(c,hex);
|
|
|
|
*outptr++ = '%';
|
|
|
|
*outptr++ = hex[0];
|
|
|
|
*outptr++ = hex[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
*outptr = EOFCHAR;
|
2012-08-01 04:34:13 +08:00
|
|
|
return encoded;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a string representing decoding of input; caller must free;*/
|
|
|
|
char*
|
|
|
|
ocuridecode(char* s)
|
|
|
|
{
|
|
|
|
return ocuridecodeonly(s,NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a string representing decoding of input only for specified
|
|
|
|
characters; caller must free
|
|
|
|
*/
|
|
|
|
char*
|
|
|
|
ocuridecodeonly(char* s, char* only)
|
|
|
|
{
|
|
|
|
size_t slen;
|
|
|
|
char* decoded;
|
|
|
|
char* outptr;
|
|
|
|
char* inptr;
|
|
|
|
unsigned int c;
|
2014-08-09 05:22:46 +08:00
|
|
|
|
2013-07-28 04:20:13 +08:00
|
|
|
if (s == NULL) return NULL;
|
2013-06-27 02:55:30 +08:00
|
|
|
if(only == NULL) only = "";
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
slen = strlen(s);
|
|
|
|
decoded = (char*)malloc(slen+1); /* Should be max we need */
|
|
|
|
|
|
|
|
outptr = decoded;
|
|
|
|
inptr = s;
|
|
|
|
while((c = *inptr++)) {
|
2013-06-27 02:55:30 +08:00
|
|
|
if(c == '+' && strchr(only,'+') != NULL)
|
2012-08-01 04:34:13 +08:00
|
|
|
*outptr++ = ' ';
|
|
|
|
else if(c == '%') {
|
|
|
|
/* try to pull two hex more characters */
|
2012-08-09 07:15:18 +08:00
|
|
|
if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR
|
2012-08-01 04:34:13 +08:00
|
|
|
&& strchr(hexchars,inptr[0]) != NULL
|
|
|
|
&& strchr(hexchars,inptr[1]) != NULL) {
|
|
|
|
/* test conversion */
|
|
|
|
int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1]));
|
2013-06-27 02:55:30 +08:00
|
|
|
if(strchr(only,xc) != NULL) {
|
2012-08-01 04:34:13 +08:00
|
|
|
inptr += 2; /* decode it */
|
|
|
|
c = xc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*outptr++ = c;
|
|
|
|
}
|
2012-08-09 07:15:18 +08:00
|
|
|
*outptr = EOFCHAR;
|
2012-08-01 04:34:13 +08:00
|
|
|
return decoded;
|
|
|
|
}
|