mirror of
https://github.com/curl/curl.git
synced 2026-08-03 19:40:29 +03:00
tool: add "variable" support
Add support for command line variables. Set variables with --variable
name=content or --variable name@file (where "file" can be stdin if set
to a single dash (-)).
Variable content is expanded in option parameters using "{{name}}"
(without the quotes) if the option name is prefixed with
"--expand-". This gets the contents of the variable "name" inserted, or
a blank if the name does not exist as a variable. Insert "{{" verbatim
in the string by prefixing it with a backslash, like "\\{{".
Import an environment variable with --variable %name. It makes curl exit
with an error if the environment variable is not set. It can also rather
get a default value if the variable does not exist, using =content or
@file like shown above.
Example: get the USER environment variable into the URL:
--variable %USER
--expand-url = "https://example.com/api/{{USER}}/method"
When expanding variables, curl supports a set of functions that can make
the variable contents more convenient to use. It can trim leading and
trailing white space with "trim", output the contents as a JSON quoted
string with "json", URL encode it with "url" and base 64 encode it with
"b64". To apply functions to a variable expansion, add them colon
separated to the right side of the variable. They are then performed in
a left to right order.
Example: get the contents of a file called $HOME/.secret into a variable
called "fix". Make sure that the content is trimmed and percent-encoded
sent as POST data:
--variable %HOME=/home/default
--expand-variable fix@{{HOME}}/.secret
--expand-data "{{fix:trim:url}}"
https://example.com/
Documented. Many new test cases.
Co-brainstormed-by: Emanuele Torre
Assisted-by: Jat Satiro
Closes #11346
This commit is contained in:
parent
47a3e6e577
commit
2e160c9c65
32 changed files with 1562 additions and 258 deletions
|
|
@ -63,7 +63,7 @@ endif()
|
|||
|
||||
# CURL_CFILES, CURLX_CFILES, CURL_HFILES come from Makefile.inc
|
||||
if(BUILD_STATIC_CURL)
|
||||
set(CURLX_CFILES ../lib/dynbuf.c)
|
||||
set(CURLX_CFILES ../lib/dynbuf.c ../lib/base64.c)
|
||||
endif()
|
||||
|
||||
add_executable(
|
||||
|
|
|
|||
|
|
@ -32,13 +32,14 @@
|
|||
# libcurl has sources that provide functions named curlx_* that aren't part of
|
||||
# the official API, but we re-use the code here to avoid duplication.
|
||||
CURLX_CFILES = \
|
||||
../lib/base64.c \
|
||||
../lib/curl_multibyte.c \
|
||||
../lib/dynbuf.c \
|
||||
../lib/nonblock.c \
|
||||
../lib/strtoofft.c \
|
||||
../lib/timediff.c \
|
||||
../lib/nonblock.c \
|
||||
../lib/warnless.c \
|
||||
../lib/curl_multibyte.c \
|
||||
../lib/version_win32.c \
|
||||
../lib/dynbuf.c
|
||||
../lib/warnless.c
|
||||
|
||||
CURLX_HFILES = \
|
||||
../lib/curl_setup.h \
|
||||
|
|
@ -91,7 +92,8 @@ CURL_CFILES = \
|
|||
tool_vms.c \
|
||||
tool_writeout.c \
|
||||
tool_writeout_json.c \
|
||||
tool_xattr.c
|
||||
tool_xattr.c \
|
||||
var.c
|
||||
|
||||
CURL_HFILES = \
|
||||
slist_wc.h \
|
||||
|
|
@ -135,7 +137,8 @@ CURL_HFILES = \
|
|||
tool_vms.h \
|
||||
tool_writeout.h \
|
||||
tool_writeout_json.h \
|
||||
tool_xattr.h
|
||||
tool_xattr.h \
|
||||
var.h
|
||||
|
||||
CURL_RCFILES = curl.rc
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "tool_setup.h"
|
||||
#include "tool_sdecls.h"
|
||||
#include "tool_urlglob.h"
|
||||
#include "var.h"
|
||||
|
||||
struct GlobalConfig;
|
||||
|
||||
|
|
@ -322,6 +323,7 @@ struct GlobalConfig {
|
|||
unsigned short parallel_max; /* MAX_PARALLEL is the maximum */
|
||||
bool parallel_connect;
|
||||
char *help_category; /* The help category, if set */
|
||||
struct var *variables;
|
||||
struct OperationConfig *first;
|
||||
struct OperationConfig *current;
|
||||
struct OperationConfig *last; /* Always last in the struct */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -48,6 +48,7 @@ typedef enum {
|
|||
PARAM_CONTDISP_SHOW_HEADER, /* --include and --remote-header-name */
|
||||
PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */
|
||||
PARAM_READ_ERROR,
|
||||
PARAM_EXPAND_ERROR, /* --expand problem */
|
||||
PARAM_LAST
|
||||
} ParameterError;
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ const char *param2text(int res)
|
|||
return "--continue-at and --remote-header-name cannot be combined";
|
||||
case PARAM_READ_ERROR:
|
||||
return "error encountered when reading a file";
|
||||
case PARAM_EXPAND_ERROR:
|
||||
return "variable expansion failure";
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,12 +246,12 @@ const struct helptxt helptext[] = {
|
|||
{" --happy-eyeballs-timeout-ms <milliseconds>",
|
||||
"Time for IPv6 before trying IPv4",
|
||||
CURLHELP_CONNECTION},
|
||||
{" --haproxy-clientip",
|
||||
"Sets client IP in HAProxy PROXY protocol v1 header",
|
||||
CURLHELP_HTTP | CURLHELP_PROXY},
|
||||
{" --haproxy-protocol",
|
||||
"Send HAProxy PROXY protocol v1 header",
|
||||
CURLHELP_HTTP | CURLHELP_PROXY},
|
||||
{" --haproxy-clientip",
|
||||
"Sets the HAProxy PROXY protocol v1 client IP",
|
||||
CURLHELP_HTTP | CURLHELP_PROXY},
|
||||
{"-I, --head",
|
||||
"Show document info only",
|
||||
CURLHELP_HTTP | CURLHELP_FTP | CURLHELP_FILE},
|
||||
|
|
@ -760,7 +760,7 @@ const struct helptxt helptext[] = {
|
|||
"Like --trace, but without hex output",
|
||||
CURLHELP_VERBOSE},
|
||||
{" --trace-ids",
|
||||
"Add transfer/connection identifiers to trace/verbose output",
|
||||
"Add transfer and connection identifiers to trace/verbose output",
|
||||
CURLHELP_VERBOSE},
|
||||
{" --trace-time",
|
||||
"Add time stamps to trace/verbose output",
|
||||
|
|
@ -786,6 +786,9 @@ const struct helptxt helptext[] = {
|
|||
{"-A, --user-agent <name>",
|
||||
"Send User-Agent <name> to server",
|
||||
CURLHELP_IMPORTANT | CURLHELP_HTTP},
|
||||
{" --variable <name=text/@file>",
|
||||
"Set variable",
|
||||
CURLHELP_CURL},
|
||||
{"-v, --verbose",
|
||||
"Make the operation more talkative",
|
||||
CURLHELP_IMPORTANT | CURLHELP_VERBOSE},
|
||||
|
|
|
|||
|
|
@ -2756,36 +2756,39 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
|
|||
/* Cleanup the libcurl source output */
|
||||
easysrc_cleanup();
|
||||
}
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
||||
if(!result) {
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE,
|
||||
CURL_LOCK_DATA_SSL_SESSION);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
||||
|
||||
/* Get the required arguments for each operation */
|
||||
do {
|
||||
result = get_args(operation, count++);
|
||||
/* Get the required arguments for each operation */
|
||||
do {
|
||||
result = get_args(operation, count++);
|
||||
|
||||
operation = operation->next;
|
||||
} while(!result && operation);
|
||||
operation = operation->next;
|
||||
} while(!result && operation);
|
||||
|
||||
/* Set the current operation pointer */
|
||||
global->current = global->first;
|
||||
/* Set the current operation pointer */
|
||||
global->current = global->first;
|
||||
|
||||
/* now run! */
|
||||
result = run_all_transfers(global, share, result);
|
||||
/* now run! */
|
||||
result = run_all_transfers(global, share, result);
|
||||
|
||||
curl_share_cleanup(share);
|
||||
if(global->libcurl) {
|
||||
/* Cleanup the libcurl source output */
|
||||
easysrc_cleanup();
|
||||
curl_share_cleanup(share);
|
||||
if(global->libcurl) {
|
||||
/* Cleanup the libcurl source output */
|
||||
easysrc_cleanup();
|
||||
|
||||
/* Dump the libcurl code if previously enabled */
|
||||
dumpeasysrc(global);
|
||||
/* Dump the libcurl code if previously enabled */
|
||||
dumpeasysrc(global);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -2793,5 +2796,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
|
|||
}
|
||||
}
|
||||
|
||||
varcleanup(global);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,50 +31,73 @@
|
|||
#include "tool_writeout_json.h"
|
||||
#include "tool_writeout.h"
|
||||
|
||||
void jsonWriteString(FILE *stream, const char *in, bool lowercase)
|
||||
#define MAX_JSON_STRING 100000
|
||||
|
||||
/* provide the given string in dynbuf as a quoted json string, but without the
|
||||
outer quotes. The buffer is not inited by this function.
|
||||
|
||||
Return 0 on success, non-zero on error.
|
||||
*/
|
||||
int jsonquoted(const char *in, size_t len,
|
||||
struct curlx_dynbuf *out, bool lowercase)
|
||||
{
|
||||
const char *i = in;
|
||||
const char *in_end = in + strlen(in);
|
||||
const char *in_end = &in[len];
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
fputc('\"', stream);
|
||||
for(; i < in_end; i++) {
|
||||
for(; (i < in_end) && !result; i++) {
|
||||
switch(*i) {
|
||||
case '\\':
|
||||
fputs("\\\\", stream);
|
||||
result = curlx_dyn_addn(out, "\\\\", 2);
|
||||
break;
|
||||
case '\"':
|
||||
fputs("\\\"", stream);
|
||||
result = curlx_dyn_addn(out, "\\\"", 2);
|
||||
break;
|
||||
case '\b':
|
||||
fputs("\\b", stream);
|
||||
result = curlx_dyn_addn(out, "\\b", 2);
|
||||
break;
|
||||
case '\f':
|
||||
fputs("\\f", stream);
|
||||
result = curlx_dyn_addn(out, "\\f", 2);
|
||||
break;
|
||||
case '\n':
|
||||
fputs("\\n", stream);
|
||||
result = curlx_dyn_addn(out, "\\n", 2);
|
||||
break;
|
||||
case '\r':
|
||||
fputs("\\r", stream);
|
||||
result = curlx_dyn_addn(out, "\\r", 2);
|
||||
break;
|
||||
case '\t':
|
||||
fputs("\\t", stream);
|
||||
result = curlx_dyn_addn(out, "\\t", 2);
|
||||
break;
|
||||
default:
|
||||
if(*i < 32) {
|
||||
fprintf(stream, "\\u%04x", *i);
|
||||
}
|
||||
if(*i < 32)
|
||||
result = curlx_dyn_addf(out, "\\u%04x", *i);
|
||||
else {
|
||||
char out = *i;
|
||||
if(lowercase && (out >= 'A' && out <= 'Z'))
|
||||
char o = *i;
|
||||
if(lowercase && (o >= 'A' && o <= 'Z'))
|
||||
/* do not use tolower() since that's locale specific */
|
||||
out |= ('a' - 'A');
|
||||
fputc(out, stream);
|
||||
o |= ('a' - 'A');
|
||||
result = curlx_dyn_addn(out, &o, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputc('\"', stream);
|
||||
if(result)
|
||||
return (int)result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jsonWriteString(FILE *stream, const char *in, bool lowercase)
|
||||
{
|
||||
struct curlx_dynbuf out;
|
||||
curlx_dyn_init(&out, MAX_JSON_STRING);
|
||||
|
||||
if(!jsonquoted(in, strlen(in), &out, lowercase)) {
|
||||
fputc('\"', stream);
|
||||
if(curlx_dyn_len(&out))
|
||||
fputs(curlx_dyn_ptr(&out), stream);
|
||||
fputc('\"', stream);
|
||||
}
|
||||
curlx_dyn_free(&out);
|
||||
}
|
||||
|
||||
void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
#include "tool_setup.h"
|
||||
#include "tool_writeout.h"
|
||||
|
||||
int jsonquoted(const char *in, size_t len,
|
||||
struct curlx_dynbuf *out, bool lowercase);
|
||||
|
||||
void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
|
||||
struct per_transfer *per, CURLcode per_result);
|
||||
void headerJSON(FILE *stream, struct per_transfer *per);
|
||||
|
|
|
|||
462
src/var.c
Normal file
462
src/var.c
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "tool_setup.h"
|
||||
|
||||
#define ENABLE_CURLX_PRINTF
|
||||
/* use our own printf() functions */
|
||||
#include "curlx.h"
|
||||
|
||||
#include "tool_cfgable.h"
|
||||
#include "tool_getparam.h"
|
||||
#include "tool_helpers.h"
|
||||
#include "tool_findfile.h"
|
||||
#include "tool_msgs.h"
|
||||
#include "tool_parsecfg.h"
|
||||
#include "dynbuf.h"
|
||||
#include "curl_base64.h"
|
||||
#include "tool_paramhlp.h"
|
||||
#include "tool_writeout_json.h"
|
||||
#include "var.h"
|
||||
|
||||
#include "memdebug.h" /* keep this as LAST include */
|
||||
|
||||
#define MAX_EXPAND_CONTENT 10000000
|
||||
|
||||
static char *Memdup(const char *data, size_t len)
|
||||
{
|
||||
char *p = malloc(len + 1);
|
||||
if(!p)
|
||||
return NULL;
|
||||
if(len)
|
||||
memcpy(p, data, len);
|
||||
p[len] = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* free everything */
|
||||
void varcleanup(struct GlobalConfig *global)
|
||||
{
|
||||
struct var *list = global->variables;
|
||||
while(list) {
|
||||
struct var *t = list;
|
||||
list = list->next;
|
||||
free((char *)t->content);
|
||||
free((char *)t->name);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct var *varcontent(struct GlobalConfig *global,
|
||||
const char *name, size_t nlen)
|
||||
{
|
||||
struct var *list = global->variables;
|
||||
while(list) {
|
||||
if((strlen(list->name) == nlen) &&
|
||||
!strncmp(name, list->name, nlen)) {
|
||||
return list;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define ENDOFFUNC(x) (((x) == '}') || ((x) == ':'))
|
||||
#define FUNCMATCH(ptr,name,len) \
|
||||
(!strncmp(ptr, name, len) && ENDOFFUNC(ptr[len]))
|
||||
|
||||
#define FUNC_TRIM "trim"
|
||||
#define FUNC_TRIM_LEN (sizeof(FUNC_TRIM) - 1)
|
||||
#define FUNC_JSON "json"
|
||||
#define FUNC_JSON_LEN (sizeof(FUNC_JSON) - 1)
|
||||
#define FUNC_URL "url"
|
||||
#define FUNC_URL_LEN (sizeof(FUNC_URL) - 1)
|
||||
#define FUNC_B64 "b64"
|
||||
#define FUNC_B64_LEN (sizeof(FUNC_B64) - 1)
|
||||
|
||||
static ParameterError varfunc(struct GlobalConfig *global,
|
||||
char *c, /* content */
|
||||
size_t clen, /* content length */
|
||||
char *f, /* functions */
|
||||
size_t flen, /* function string length */
|
||||
struct curlx_dynbuf *out)
|
||||
{
|
||||
bool alloc = FALSE;
|
||||
ParameterError err = PARAM_OK;
|
||||
const char *finput = f;
|
||||
|
||||
/* The functions are independent and runs left to right */
|
||||
while(*f && !err) {
|
||||
if(*f == '}')
|
||||
/* end of functions */
|
||||
break;
|
||||
/* On entry, this is known to be a colon already. In subsequent laps, it
|
||||
is also known to be a colon since that is part of the FUNCMATCH()
|
||||
checks */
|
||||
f++;
|
||||
if(FUNCMATCH(f, FUNC_TRIM, FUNC_TRIM_LEN)) {
|
||||
size_t len = clen;
|
||||
f += FUNC_TRIM_LEN;
|
||||
if(clen) {
|
||||
/* skip leading white space, including CRLF */
|
||||
while(*c && ISSPACE(*c)) {
|
||||
c++;
|
||||
len--;
|
||||
}
|
||||
while(len && ISSPACE(c[len-1]))
|
||||
len--;
|
||||
}
|
||||
/* put it in the output */
|
||||
curlx_dyn_reset(out);
|
||||
if(curlx_dyn_addn(out, c, len)) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(FUNCMATCH(f, FUNC_JSON, FUNC_JSON_LEN)) {
|
||||
f += FUNC_JSON_LEN;
|
||||
curlx_dyn_reset(out);
|
||||
if(clen) {
|
||||
if(jsonquoted(c, clen, out, FALSE)) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(FUNCMATCH(f, FUNC_URL, FUNC_URL_LEN)) {
|
||||
f += FUNC_URL_LEN;
|
||||
curlx_dyn_reset(out);
|
||||
if(clen) {
|
||||
char *enc = curl_easy_escape(NULL, c, (int)clen);
|
||||
if(!enc) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
|
||||
/* put it in the output */
|
||||
if(curlx_dyn_add(out, enc)) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
curl_free(enc);
|
||||
}
|
||||
}
|
||||
else if(FUNCMATCH(f, FUNC_B64, FUNC_B64_LEN)) {
|
||||
f += FUNC_B64_LEN;
|
||||
curlx_dyn_reset(out);
|
||||
if(clen) {
|
||||
char *enc;
|
||||
size_t elen;
|
||||
CURLcode result = curlx_base64_encode(c, clen, &enc, &elen);
|
||||
if(result) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
|
||||
/* put it in the output */
|
||||
if(curlx_dyn_addn(out, enc, elen))
|
||||
err = PARAM_NO_MEM;
|
||||
curl_free(enc);
|
||||
if(err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* unsupported function */
|
||||
errorf(global, "unknown variable function in '%.*s'",
|
||||
(int)flen, finput);
|
||||
err = PARAM_EXPAND_ERROR;
|
||||
break;
|
||||
}
|
||||
if(alloc)
|
||||
free(c);
|
||||
|
||||
clen = curlx_dyn_len(out);
|
||||
c = Memdup(curlx_dyn_ptr(out), clen);
|
||||
if(!c) {
|
||||
err = PARAM_NO_MEM;
|
||||
break;
|
||||
}
|
||||
alloc = TRUE;
|
||||
}
|
||||
if(alloc)
|
||||
free(c);
|
||||
if(err)
|
||||
curlx_dyn_free(out);
|
||||
return err;
|
||||
}
|
||||
|
||||
ParameterError varexpand(struct GlobalConfig *global,
|
||||
const char *line, struct curlx_dynbuf *out,
|
||||
bool *replaced)
|
||||
{
|
||||
CURLcode result;
|
||||
char *envp;
|
||||
bool added = FALSE;
|
||||
const char *input = line;
|
||||
*replaced = FALSE;
|
||||
curlx_dyn_init(out, MAX_EXPAND_CONTENT);
|
||||
do {
|
||||
envp = strstr(line, "{{");
|
||||
if((envp > line) && envp[-1] == '\\') {
|
||||
/* preceding backslash, we want this verbatim */
|
||||
|
||||
/* insert the text up to this point, minus the backslash */
|
||||
result = curlx_dyn_addn(out, line, envp - line - 1);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
|
||||
/* output '{{' then continue from here */
|
||||
result = curlx_dyn_addn(out, "{{", 2);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
line = &envp[2];
|
||||
}
|
||||
else if(envp) {
|
||||
char name[128];
|
||||
size_t nlen;
|
||||
size_t i;
|
||||
char *funcp;
|
||||
char *clp = strstr(envp, "}}");
|
||||
size_t prefix;
|
||||
|
||||
if(!clp) {
|
||||
/* uneven braces */
|
||||
warnf(global, "missing close '}}' in '%s'", input);
|
||||
break;
|
||||
}
|
||||
|
||||
prefix = 2;
|
||||
envp += 2; /* move over the {{ */
|
||||
|
||||
/* if there is a function, it ends the name with a colon */
|
||||
funcp = memchr(envp, ':', clp - envp);
|
||||
if(funcp)
|
||||
nlen = funcp - envp;
|
||||
else
|
||||
nlen = clp - envp;
|
||||
if(!nlen || (nlen >= sizeof(name))) {
|
||||
warnf(global, "bad variable name length '%s'", input);
|
||||
/* insert the text as-is since this is not an env variable */
|
||||
result = curlx_dyn_addn(out, line, clp - line + prefix);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
}
|
||||
else {
|
||||
/* insert the text up to this point */
|
||||
result = curlx_dyn_addn(out, line, envp - prefix - line);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
|
||||
/* copy the name to separate buffer */
|
||||
memcpy(name, envp, nlen);
|
||||
name[nlen] = 0;
|
||||
|
||||
/* verify that the name looks sensible */
|
||||
for(i = 0; (i < nlen) &&
|
||||
(ISALNUM(name[i]) || (name[i] == '_')); i++);
|
||||
if(i != nlen) {
|
||||
warnf(global, "bad variable name: %s", name);
|
||||
/* insert the text as-is since this is not an env variable */
|
||||
result = curlx_dyn_addn(out, envp - prefix,
|
||||
clp - envp + prefix + 2);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
}
|
||||
else {
|
||||
char *value;
|
||||
size_t vlen = 0;
|
||||
struct curlx_dynbuf buf;
|
||||
const struct var *v = varcontent(global, name, nlen);
|
||||
if(v) {
|
||||
value = (char *)v->content;
|
||||
vlen = v->clen;
|
||||
}
|
||||
else
|
||||
value = NULL;
|
||||
|
||||
curlx_dyn_init(&buf, MAX_EXPAND_CONTENT);
|
||||
if(funcp) {
|
||||
/* apply the list of functions on the value */
|
||||
size_t flen = clp - funcp;
|
||||
ParameterError err = varfunc(global, value, vlen, funcp, flen,
|
||||
&buf);
|
||||
if(err)
|
||||
return err;
|
||||
value = curlx_dyn_ptr(&buf);
|
||||
vlen = curlx_dyn_len(&buf);
|
||||
}
|
||||
|
||||
if(value && *value) {
|
||||
/* A variable might contain null bytes. Such bytes cannot be shown
|
||||
using normal means, this is an error. */
|
||||
char *nb = memchr(value, '\0', vlen);
|
||||
if(nb) {
|
||||
errorf(global, "variable contains null byte");
|
||||
return PARAM_EXPAND_ERROR;
|
||||
}
|
||||
}
|
||||
/* insert the value */
|
||||
result = curlx_dyn_addn(out, value, vlen);
|
||||
curlx_dyn_free(&buf);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
line = &clp[2];
|
||||
}
|
||||
|
||||
} while(envp);
|
||||
if(added && *line) {
|
||||
/* add the "suffix" as well */
|
||||
result = curlx_dyn_add(out, line);
|
||||
if(result)
|
||||
return PARAM_NO_MEM;
|
||||
}
|
||||
*replaced = added;
|
||||
if(!added)
|
||||
curlx_dyn_free(out);
|
||||
return PARAM_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Created in a way that is not revealing how variables is actually stored so
|
||||
* that we can improve this if we want better performance when managing many
|
||||
* at a later point.
|
||||
*/
|
||||
static ParameterError addvariable(struct GlobalConfig *global,
|
||||
const char *name,
|
||||
size_t nlen,
|
||||
const char *content,
|
||||
size_t clen,
|
||||
bool contalloc)
|
||||
{
|
||||
struct var *p;
|
||||
const struct var *check = varcontent(global, name, nlen);
|
||||
if(check)
|
||||
notef(global, "Overwriting variable '%s'", check->name);
|
||||
|
||||
p = calloc(sizeof(struct var), 1);
|
||||
if(!p)
|
||||
return PARAM_NO_MEM;
|
||||
|
||||
p->name = Memdup(name, nlen);
|
||||
if(!p->name)
|
||||
goto err;
|
||||
|
||||
p->content = contalloc ? content: Memdup(content, clen);
|
||||
if(!p->content)
|
||||
goto err;
|
||||
p->clen = clen;
|
||||
|
||||
p->next = global->variables;
|
||||
global->variables = p;
|
||||
return PARAM_OK;
|
||||
err:
|
||||
free((char *)p->content);
|
||||
free((char *)p->name);
|
||||
free(p);
|
||||
return PARAM_NO_MEM;
|
||||
}
|
||||
|
||||
ParameterError setvariable(struct GlobalConfig *global,
|
||||
const char *input)
|
||||
{
|
||||
const char *name;
|
||||
size_t nlen;
|
||||
char *content = NULL;
|
||||
size_t clen = 0;
|
||||
bool contalloc = FALSE;
|
||||
const char *line = input;
|
||||
ParameterError err = PARAM_OK;
|
||||
bool import = FALSE;
|
||||
char *ge = NULL;
|
||||
|
||||
if(*input == '%') {
|
||||
import = TRUE;
|
||||
line++;
|
||||
}
|
||||
name = line;
|
||||
while(*line && (ISALNUM(*line) || (*line == '_')))
|
||||
line++;
|
||||
nlen = line - name;
|
||||
if(!nlen || (nlen > 128)) {
|
||||
warnf(global, "Bad variable name length (%zd), skipping", nlen);
|
||||
return PARAM_OK;
|
||||
}
|
||||
if(import) {
|
||||
ge = curl_getenv(name);
|
||||
if(!*line && !ge) {
|
||||
/* no assign, no variable, fail */
|
||||
errorf(global, "Variable '%s' import fail, not set", name);
|
||||
return PARAM_EXPAND_ERROR;
|
||||
}
|
||||
else if(ge) {
|
||||
/* there is a value to use */
|
||||
content = ge;
|
||||
clen = strlen(ge);
|
||||
}
|
||||
}
|
||||
if(content)
|
||||
;
|
||||
else if(*line == '@') {
|
||||
/* read from file or stdin */
|
||||
FILE *file;
|
||||
bool use_stdin;
|
||||
line++;
|
||||
use_stdin = !strcmp(line, "-");
|
||||
if(use_stdin)
|
||||
file = stdin;
|
||||
else {
|
||||
file = fopen(line, "rb");
|
||||
}
|
||||
if(file) {
|
||||
err = file2memory(&content, &clen, file);
|
||||
/* in case of out of memory, this should fail the entire operation */
|
||||
contalloc = TRUE;
|
||||
}
|
||||
if(!use_stdin)
|
||||
fclose(file);
|
||||
if(err)
|
||||
return err;
|
||||
}
|
||||
else if(*line == '=') {
|
||||
line++;
|
||||
/* this is the exact content */
|
||||
content = (char *)line;
|
||||
clen = strlen(line);
|
||||
}
|
||||
else {
|
||||
warnf(global, "Bad --variable syntax, skipping: %s", input);
|
||||
return PARAM_OK;
|
||||
}
|
||||
err = addvariable(global, name, nlen, content, clen, contalloc);
|
||||
if(err) {
|
||||
if(contalloc)
|
||||
free(content);
|
||||
}
|
||||
curl_free(ge);
|
||||
return err;
|
||||
}
|
||||
48
src/var.h
Normal file
48
src/var.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef HEADER_CURL_VAR_H
|
||||
#define HEADER_CURL_VAR_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "tool_getparam.h"
|
||||
#include "dynbuf.h"
|
||||
|
||||
struct var {
|
||||
struct var *next;
|
||||
const char *name;
|
||||
const char *content;
|
||||
size_t clen; /* content length */
|
||||
};
|
||||
|
||||
struct GlobalConfig;
|
||||
|
||||
ParameterError setvariable(struct GlobalConfig *global, const char *input);
|
||||
ParameterError varexpand(struct GlobalConfig *global,
|
||||
const char *line, struct curlx_dynbuf *out,
|
||||
bool *replaced);
|
||||
|
||||
/* free everything */
|
||||
void varcleanup(struct GlobalConfig *global);
|
||||
|
||||
#endif /* HEADER_CURL_VAR_H */
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue