Newer
Older
} while(!longopt && !singleopt && *++parse && !*usedarg);
return PARAM_OK;
static void parseconfig(const char *filename,
struct Configurable *config)
char filebuffer[512];
char *home;
if(!filename || !*filename) {
/* NULL or no file name attempts to load .curlrc from the homedir! */
#define CURLRC DOT_CHAR "curlrc"
filename = CURLRC; /* sensible default */
home = homedir(); /* portable homedir finder */
if(home) {
if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
snprintf(filebuffer, sizeof(filebuffer),
"%s%s%s", home, DIR_CHAR, CURLRC);
filename = filebuffer;
}
free(home); /* we've used it, now free it */
}
# else /* AmigaOS */
/* On AmigaOS all the config files are into env:
*/
filename = "ENV:" CURLRC;
#endif
}
if(strcmp(filename,"-"))
file = fopen(filename, "r");
else
file = stdin;
Daniel Stenberg
committed
if(file) {
Daniel Stenberg
committed
char *line;
char *aline;
char *option;
char *param;
int lineno=0;
bool alloced_param;
#define isseparator(x) (((x)=='=') || ((x) == ':'))
while (NULL != (aline = my_get_line(file))) {
lineno++;
line = aline;
alloced_param=FALSE;
Daniel Stenberg
committed
/* lines with # in the fist column is a comment! */
Daniel Stenberg
committed
while(*line && isspace((int)*line))
line++;
switch(*line) {
case '#':
case '/':
case '\r':
case '\n':
case '*':
case '\0':
Daniel Stenberg
committed
continue;
}
/* the option keywords starts here */
option = line;
while(*line && !isspace((int)*line) && !isseparator(*line))
line++;
/* ... and has ended here */
Daniel Stenberg
committed
if(*line)
*line++=0; /* zero terminate, we have a local copy of the data */
#ifdef DEBUG_CONFIG
fprintf(stderr, "GOT: %s\n", option);
#endif
/* pass spaces and separator(s) */
Daniel Stenberg
committed
while(*line && (isspace((int)*line) || isseparator(*line)))
line++;
/* the parameter starts here (unless quoted) */
if(*line == '\"') {
char *ptr;
/* quoted parameter, do the qoute dance */
line++;
param=strdup(line); /* parameter */
alloced_param=TRUE;
ptr=param;
while(*line && (*line != '\"')) {
if(*line == '\\') {
line++;
/* default is to output the letter after the backslah */
switch(out = *line) {
case '\0':
continue; /* this'll break out of the loop */
case 't':
out='\t';
break;
case 'n':
out='\n';
break;
case 'r':
out='\r';
break;
case 'v':
out='\v';
break;
}
*ptr++=out;
line++;
}
else
*ptr++=*line++;
}
*ptr=0; /* always zero terminate */
else {
param=line; /* parameter starts here */
while(*line && !isspace((int)*line))
line++;
*line=0; /* zero terminate */
if (param && !*param) {
/* do this so getparameter can check for required parameters.
Otherwise it always thinks there's a parameter. */
if (alloced_param)
free(param);
param = NULL;
}
#ifdef DEBUG_CONFIG
fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
#endif
res = getparameter(option, param, &usedarg, config);
Daniel Stenberg
committed
if (param && *param && !usedarg)
/* we passed in a parameter that wasn't used! */
res = PARAM_GOT_EXTRA_PARAMETER;
Daniel Stenberg
committed
if(res != PARAM_OK) {
/* the help request isn't really an error */
if(!strcmp(filename, "-")) {
}
if(PARAM_HELP_REQUESTED != res) {
const char *reason = param2text(res);
fprintf(stderr, "%s:%d: warning: '%s' %s\n",
filename, lineno, option, reason);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
if(alloced_param)
{
free(param);
param = NULL;
}
free(aline);
}
if(file != stdin)
fclose(file);
}
}
#ifdef HAVE_POLL
/* portable subsecond "sleep" */
poll((void *)0, 0, ms);
#else
/* systems without poll() need other solutions */
#ifdef WIN32
/* Windows offers a millisecond sleep */
Sleep(ms);
#else
/* Other systems must use select() for this */
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = ms * 1000;
select(0, NULL, NULL, NULL, &timeout);
#endif
#endif
struct OutStruct {
char *filename;
FILE *stream;
struct Configurable *config;
static int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
Daniel Stenberg
committed
int rc;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure */
}
if(config->recvpersecond) {
/*
* We know when we received data the previous time. We know how much data
* we get now. Make sure that this is not faster than we are told to run.
* If we're faster, sleep a while *before* doing the fwrite() here.
*/
time_t timediff;
time_t now;
time_t sleep_time;
now = time(NULL);
timediff = now - config->lastrecvtime;
if( size*nmemb > config->recvpersecond*timediff) {
/* figure out how many milliseconds to rest */
sleep_time = (size*nmemb)*1000/config->recvpersecond - timediff*1000;
/*
* Make sure we don't sleep for so long that we trigger the speed limit.
* This won't limit the bandwidth quite the way we've been asked to, but
* at least the transfer has a chance.
*/
if (config->low_speed_time > 0)
sleep_time = MIN(sleep_time,(config->low_speed_time * 1000) / 2);
go_sleep (sleep_time);
now = time(NULL);
}
config->lastrecvtime = now;
}
Daniel Stenberg
committed
rc = fwrite(buffer, size, nmemb, out->stream);
if(config->nobuffer)
/* disable output buffering */
fflush(out->stream);
return rc;
struct InStruct {
FILE *stream;
struct Configurable *config;
};
static int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
{
struct InStruct *in=(struct InStruct *)userp;
struct Configurable *config = in->config;
if(config->sendpersecond) {
/*
* We know when we sent data the previous time. We know how much data
* we sent. Make sure that this was not faster than we are told to run.
* If we're faster, sleep a while *before* doing the fread() here.
* Also, make no larger fread() than should be sent this second!
*/
time_t timediff;
time_t now;
now = time(NULL);
timediff = now - config->lastsendtime;
if( config->lastsendsize > config->sendpersecond*timediff) {
/* figure out how many milliseconds to rest */
go_sleep ( config->lastsendsize*1000/config->sendpersecond -
timediff*1000 );
now = time(NULL);
}
config->lastsendtime = now;
if(size*nmemb > config->sendpersecond) {
/* lower the size to actually read */
nmemb = config->sendpersecond;
size = 1;
}
config->lastsendsize = size*nmemb;
}
return fread(buffer, size, nmemb, in->stream);
}
struct ProgressData {
Daniel Stenberg
committed
int calls;
double prev;
int width;
FILE *out; /* where to write everything to */
};
static int myprogress (void *clientp,
double dltotal,
double dlnow,
double ultotal,
double ulnow)
{
/* The original progress-bar source code was written for curl by Lars Aas,
and this new edition inherits some of his concepts. */
char line[256];
char outline[256];
char format[40];
double frac;
double percent;
int barwidth;
int num;
int i;
struct ProgressData *bar = (struct ProgressData *)clientp;
double total = dltotal + ultotal + bar->initial_size;
double point = dlnow + ulnow + bar->initial_size; /* we've come this far */
Daniel Stenberg
committed
bar->calls++; /* simply count invokes */
int prevblock = (int)bar->prev / 1024;
int thisblock = (int)point / 1024;
while ( thisblock > prevblock ) {
fprintf( bar->out, "#" );
prevblock++;
}
}
else {
frac = point / total;
percent = frac * 100.0f;
barwidth = bar->width - 7;
num = (int) (((double)barwidth) * frac);
i = 0;
for ( i = 0; i < num; i++ ) {
line[i] = '#';
}
line[i] = '\0';
sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
sprintf( outline, format, line, percent );
fprintf( bar->out, "\r%s", outline );
}
fflush(bar->out);
bar->prev = point;
return 0;
}
static
void progressbarinit(struct ProgressData *bar,
struct Configurable *config)
{
#ifdef __EMX__
/* 20000318 mgs */
int scr_size [2];
#endif
char *colp;
memset(bar, 0, sizeof(struct ProgressData));
/* pass this through to progress function so
* it can display progress towards total file
* not just the part that's left. (21-may-03, dbyron) */
if (config->use_resume)
bar->initial_size = config->resume_from;
/* TODO: get terminal width through ansi escapes or something similar.
try to update width when xterm is resized... - 19990617 larsa */
#ifndef __EMX__
/* 20000318 mgs
* OS/2 users most likely won't have this env var set, and besides that
* we're using our own way to determine screen width */
colp = curl_getenv("COLUMNS");
if (colp != NULL) {
bar->width = atoi(colp);
}
else
bar->width = 79;
#else
/* 20000318 mgs
* We use this emx library call to get the screen width, and subtract
* one from what we got in order to avoid a problem with the cursor
* advancing to the next line if we print a string that is as long as
* the screen is wide. */
_scrsize(scr_size);
bar->width = scr_size[0] - 1;
#endif
bar->out = config->errors;
}
Daniel Stenberg
committed
static
void dump(const char *text,
FILE *stream, unsigned char *ptr, size_t size,
bool nohex)
Daniel Stenberg
committed
{
size_t i;
size_t c;
unsigned int width=0x10;
if(nohex)
/* without the hex output, we can fit more on screen */
width = 0x40;
Daniel Stenberg
committed
fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
Daniel Stenberg
committed
Daniel Stenberg
committed
fprintf(stream, "%04zx: ", i);
Daniel Stenberg
committed
if(!nohex) {
/* hex not disabled, show it */
for(c = 0; c < width; c++)
if(i+c < size)
fprintf(stream, "%02x ", ptr[i+c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i+c < size); c++) {
/* check for 0D0A; if found, skip past and start a new line of output */
if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
i+=(c+2-width);
break;
}
Daniel Stenberg
committed
fprintf(stream, "%c",
(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
/* check again for 0D0A, to avoid an extra \n if it's at width */
if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
i+=(c+3-width);
break;
}
}
Daniel Stenberg
committed
fputc('\n', stream); /* newline */
}
Daniel Stenberg
committed
}
static
int my_trace(CURL *handle, curl_infotype type,
unsigned char *data, size_t size,
void *userp)
{
struct Configurable *config = (struct Configurable *)userp;
FILE *output=config->errors;
Daniel Stenberg
committed
(void)handle; /* prevent compiler warning */
if(!config->trace_stream) {
Daniel Stenberg
committed
/* open for append */
Daniel Stenberg
committed
if(curl_strequal("-", config->trace_dump))
config->trace_stream = stdout;
else {
config->trace_stream = fopen(config->trace_dump, "w");
config->trace_fopened = TRUE;
}
}
Daniel Stenberg
committed
if(config->trace_stream)
output = config->trace_stream;
switch (type) {
case CURLINFO_TEXT:
fprintf(output, "== Info: %s", data);
default: /* in case a new one is introduced to shock us */
return 0;
Daniel Stenberg
committed
case CURLINFO_HEADER_OUT:
Daniel Stenberg
committed
break;
case CURLINFO_DATA_OUT:
text = "=> Send data";
Daniel Stenberg
committed
break;
case CURLINFO_HEADER_IN:
Daniel Stenberg
committed
break;
case CURLINFO_DATA_IN:
Daniel Stenberg
committed
break;
}
dump(text, output, data, size, config->trace_ascii);
Daniel Stenberg
committed
return 0;
}
static void free_config_fields(struct Configurable *config)
{
if(config->random_file)
free(config->random_file);
if(config->egd_file)
free(config->egd_file);
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
if(config->userpwd)
free(config->userpwd);
if(config->postfields)
free(config->postfields);
if(config->proxy)
free(config->proxy);
if(config->proxyuserpwd)
free(config->proxyuserpwd);
if(config->cookie)
free(config->cookie);
if(config->cookiefile)
free(config->cookiefile);
if(config->krb4level)
free(config->krb4level);
if(config->headerfile)
free(config->headerfile);
if(config->ftpport)
free(config->ftpport);
if(config->range)
free(config->range);
if(config->customrequest)
free(config->customrequest);
if(config->writeout)
free(config->writeout);
if(config->httppost)
curl_formfree(config->httppost);
if(config->cacert)
free(config->cacert);
if(config->capath)
free(config->capath);
if(config->cookiejar)
free(config->cookiejar);
curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
curl_slist_free_all(config->postquote); /* */
curl_slist_free_all(config->headers); /* */
}
Daniel Stenberg
committed
#if defined(WIN32) && !defined(__CYGWIN32__)
/* Function to find CACert bundle on a Win32 platform using SearchPath.
* (SearchPath is defined in windows.h, which is #included into libcurl)
* (Use the ASCII version instead of the unicode one!)
* The order of the directories it searches is:
* 1. application's directory
* 2. current working directory
* 3. Windows System directory (e.g. C:\windows\system32)
* 4. Windows Directory (e.g. C:\windows)
* 5. all directories along %PATH%
*/
static void FindWin32CACert(struct Configurable *config,
const char *bundle_file)
{
/* only check for cert file if "we" support SSL */
if(curlinfo->features & CURL_VERSION_SSL) {
Daniel Stenberg
committed
DWORD buflen;
char *ptr = NULL;
char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
if (!retval)
return;
retval[0] = '\0';
buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
if (buflen > 0) {
GetStr(&config->cacert, retval);
}
free(retval);
}
}
#endif
static int
operate(struct Configurable *config, int argc, char *argv[])
char errorbuffer[CURL_ERROR_SIZE];
char useragent[128]; /* buah, we don't want a larger default user agent */
struct ProgressData progressbar;
struct getout *urlnode;
struct getout *nextnode;
Daniel Stenberg
committed
struct OutStruct heads;
Daniel Stenberg
committed
URLGlob *urls=NULL;
URLGlob *inglob=NULL;
int infilenum;
char *infiles; /* might a glob pattern */
char *uploadfile=NULL; /* a single file, never a glob */
FILE *infd = stdin;
bool infdfopen;
FILE *headerfilep = NULL;
char *urlbuffer=NULL;
curl_off_t uploadfilesize; /* -1 means unknown */
Daniel Stenberg
committed
char *httpgetfields=NULL;
int res = 0;
int up; /* upload file counter within a single upload glob */
Daniel Stenberg
committed
char *env;
/* this sends all memory debug messages to a logfile named memdump */
env = curl_getenv("CURL_MEMDEBUG");
if(env) {
curl_memdebug("memdump");
}
env = curl_getenv("CURL_MEMLIMIT");
if(env) {
curl_memlimit(atoi(env));
curl_free(env);
}
memset(&outs,0,sizeof(outs));
/* we get libcurl info right away */
curlinfo = curl_version_info(CURLVERSION_NOW);
errorbuffer[0]=0; /* prevent junk from being output */
/* inits */
if (main_init() != CURLE_OK) {
helpf("error initializing curl library\n");
return CURLE_FAILED_INIT;
}
config->showerror=TRUE;
config->conf=CONF_DEFAULT;
Daniel Stenberg
committed
config->use_httpget=FALSE;
config->create_dirs=FALSE;
Daniel Stenberg
committed
(!curl_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
strchr(argv[1], 'q')) {
/*
* The first flag, that is not a verbose name, but a shortname
* and it includes the 'q' flag!
*/
;
}
else {
}
/* Parse options */
for (i = 1; i < argc; i++) {
if(stillflags &&
('-' == argv[i][0])) {
char *nextarg;
bool passarg;
char *origopt=argv[i];
char *flag = argv[i];
Daniel Stenberg
committed
if(curl_strequal("--", argv[i]))
/* this indicates the end of the flags and thus enables the
following (URL) argument to start with -. */
stillflags=FALSE;
else {
nextarg= (i < argc - 1)? argv[i+1]: NULL;
res = getparameter(flag, nextarg, &passarg, config);
if(res) {
const char *reason = param2text(res);
if(res != PARAM_HELP_REQUESTED)
helpf("option %s: %s\n", origopt, reason);
return CURLE_FAILED_INIT;
}
if(passarg) /* we're supposed to skip this */
i++;
}
}
else {
bool used;
/* just add the URL please */
res = getparameter((char *)"--url", argv[i], &used, config);
if(!config->url_list || !config->url_list->url) {
clean_getout(config);
if(NULL == config->useragent) {
/* set non-zero default values: */
snprintf(useragent, sizeof(useragent),
CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
config->useragent= useragent;
Daniel Stenberg
committed
/* On WIN32 (non-cygwin), we can't set the path to curl-ca-bundle.crt
* at compile time. So we look here for the file in two ways:
* 1: look at the environment variable CURL_CA_BUNDLE for a path
* 2: if #1 isn't found, use the windows API function SearchPath()
* to find it along the app's path (includes app's dir and CWD)
*
* We support the environment variable thing for non-Windows platforms
* too. Just for the sake of it.
*/
if (!config->cacert &&
!config->capath &&
!config->insecure_ok) {
Daniel Stenberg
committed
env = curl_getenv("CURL_CA_BUNDLE");
if(env) {
GetStr(&config->cacert, env);
Daniel Stenberg
committed
}
#if defined(WIN32) && !defined(__CYGWIN32__)
else
FindWin32CACert(config, "curl-ca-bundle.crt");
Daniel Stenberg
committed
#endif
Daniel Stenberg
committed
Daniel Stenberg
committed
if (config->postfields) {
if (config->use_httpget) {
/* Use the postfields data for a http get */
httpgetfields = strdup(config->postfields);
free(config->postfields);
config->postfields = NULL;
if(SetHTTPrequest((config->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET),
&config->httpreq)) {
Daniel Stenberg
committed
free(httpgetfields);
return PARAM_BAD_USE;
}
}
else {
if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
return PARAM_BAD_USE;
}
}
/*
* Get a curl handle to use for all forthcoming curl transfers. Cleanup
* when all transfers are done.
if(!curl) {
clean_getout(config);
}
/* After this point, we should call curl_easy_cleanup() if we decide to bail
* out from this function! */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(config->headerfile) {
/* open file for output: */
if(strcmp(config->headerfile,"-")) {
heads.filename = config->headerfile;
headerfilep=NULL;
}
else
headerfilep=stdout;
heads.stream = headerfilep;
heads.config = config;
}
while(urlnode && !res) {
char *dourl;
/* get the full URL (it might be NULL) */
dourl=urlnode->url;
url = dourl;
if(NULL == url) {
/* This node had no URL, skip it and continue to the next */
if(urlnode->outfile)
free(urlnode->outfile);
/* move on to the next URL */
nextnode=urlnode->next;
free(urlnode); /* free the node */
urlnode = nextnode;
continue; /* next please */
}
/* default output stream is stdout */
outs.stream = stdout;
outs.config = config;
Daniel Stenberg
committed
/* save outfile pattern before expansion */
outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;
infiles = urlnode->infile;
if(!config->globoff && infiles) {
/* Unless explicitly shut off */
res = glob_url(&inglob, infiles, &infilenum,
config->showerror?
(config->errors?config->errors:stderr):NULL);
if(res != CURLE_OK) {
clean_getout(config);
break;
}
Daniel Stenberg
committed
}
/* Here's the loop for uploading multiple files within the same
single globbed string. If no upload, we enter the loop once anyway. */
for(up = 0;
(!up && !infiles) ||
(uploadfile = inglob?
glob_next_url(inglob):
(!up?strdup(infiles):NULL));
up++) {
uploadfilesize=-1;
if(!config->globoff) {
/* Unless explicitly shut off, we expand '{...}' and '[...]'
expressions and return total number of URLs in pattern set */
res = glob_url(&urls, dourl, &urlnum,
config->showerror?
(config->errors?config->errors:stderr):NULL);
if(res != CURLE_OK) {
break;
}
}
else
urlnum = 1; /* without globbing, this is a single URL */
/* if multiple files extracted to stdout, insert separators! */
separator= ((!outfiles || curl_strequal(outfiles, "-")) && urlnum > 1);
/* Here's looping around each globbed URL */
for(i = 0;
(url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
i++) {
char *outfile;
outfile = outfiles?strdup(outfiles):NULL;
if((urlnode->flags&GETOUT_USEREMOTE) ||
(outfile && !curl_strequal("-", outfile)) ) {
/*
* We have specified a file name to store the result in, or we have
* decided we want to use the remote file name.
*/
if(!outfile) {
/* Find and get the remote file name */
char * pc =strstr(url, "://");
if(pc)
pc+=3;
else
pc=url;
pc = strrchr(pc, '/');
if(pc) {
/* duplicate the string beyond the slash */
pc++;
outfile = *pc ? strdup(pc): NULL;
}
if(!outfile || !*outfile) {
helpf("Remote file name has no length!\n");
res = CURLE_WRITE_ERROR;
free(url);
break;
}
#if defined(__DJGPP__)
{
/* This is for DOS, and then we do some major replacing of
bad characters in the file name before using it */
char *file1=xmalloc(PATH_MAX);
strcpy(file1, msdosify(outfile));
strcpy(outfile, rename_if_dos_device_name(file1));
xfree(file1);
}
#endif /* __DJGPP__ */
}
else if(urls) {
/* fill '#1' ... '#9' terms from URL pattern */
char *storefile = outfile;
outfile = glob_match_url(storefile, urls);
free(storefile);
if(!outfile) {
/* bad globbing */
fprintf(stderr, "bad output glob!\n");
free(url);
res = CURLE_FAILED_INIT;
break;
}
}
/* Create the directory hierarchy, if not pre-existant to a multiple
file output call */
Daniel Stenberg
committed
if(config->create_dirs)
if (-1 == create_dir_hierarchy(outfile)) {
return CURLE_WRITE_ERROR;
}
Daniel Stenberg
committed
if(config->resume_from_current) {
/* We're told to continue from where we are now. Get the
size of the file as it is now and open it for append instead */
struct stat fileinfo;
/*VMS?? -- Danger, the filesize is only valid for stream files */
if(0 == stat(outfile, &fileinfo))
/* set offset to current file size: */
config->resume_from = fileinfo.st_size;
else
/* let offset be 0 */
config->resume_from = 0;
}
Daniel Stenberg
committed
outs.filename = outfile;
if(config->resume_from) {
/* open file for output: */
outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
if (!outs.stream) {
helpf("Can't open '%s'!\n", outfile);
return CURLE_WRITE_ERROR;
}
}
else {
outs.stream = NULL; /* open when needed */
infdfopen=FALSE;
if(uploadfile && !curl_strequal(uploadfile, "-")) {
/*
* We have specified a file to upload and it isn't "-".
*/
struct stat fileinfo;
/* If no file name part is given in the URL, we add this file name */
char *ptr=strstr(url, "://");
if(ptr)
ptr+=3;
else
ptr=url;
ptr = strrchr(ptr, '/');
if(!ptr || !strlen(++ptr)) {
/* The URL has no file name part, add the local file name. In order
to be able to do so, we have to create a new URL in another
buffer.*/
/* We only want the part of the local path that is on the right
side of the rightmost slash and backslash. */
char *filep = strrchr(uploadfile, '/');
char *file2 = strrchr(filep?filep:uploadfile, '\\');
if(file2)
filep = file2+1;
else if(filep)
filep++;
else
filep = uploadfile;
/* URL encode the file name */
filep = curl_escape(filep, 0 /* use strlen */);
if(filep) {