Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --replace as a command #261

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -2230,5 +2230,103 @@
"stderr": true,
"returncode": 9
}
},
{
"input": {
"arguments": [
"http://test.org/?key=val",
"--replace",
"query=key=foo"
]
},
"expected": {
"stdout": "http://test.org/?key=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing&key=val",
"--replace",
"query=key=foo"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing&key",
"--replace",
"query=key=foo"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing&key=foo",
"--replace",
"query=key"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing",
"--replace",
"query=key=foo"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key=foo\n",
"stderr": "trurl note: key 'key' not in url, appending to query\n",
"returncode": 0
}
},
{
"input": {
"arguments": [
"https://example.com?a=123&b=321&b=987",
"--replace",
"query=b=foo"
]
},
"expected": {
"stdout": "https://example.com/?a=123&b=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing",
"--replace",
"path=some/path"
]
},
"expected": {
"stdout": "",
"stderr": "trurl error: Unsupported replace component: path=some/path\ntrurl error: Try trurl -h for help\n",
"returncode": 12
}
}
]
6 changes: 6 additions & 0 deletions trurl.1
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ properly.
Redirect the URL to this new location.
The redirection is performed on the base URL, so, if no base URL is specified,
no redirection will be performed.
.IP "--replace [component]=[data]"
Replaces this URL component. Only a query component may be replaced.

data can either take the form of a single value, or as a key/value pair in the
shape \fIfoo=bar\fP. If replace is called on an item that isn't in the list of
queries trurl will append it to the end of the query list and throw a warning.
jacobmealey marked this conversation as resolved.
Show resolved Hide resolved
.IP "-s, --set [component][:]=[data]"
Set this URL component. Setting blank string ("") will clear the component
from the URL.
Expand Down
62 changes: 62 additions & 0 deletions trurl.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ static const struct var variables[] = {
#define ERROR_BADURL 9 /* if --verify is set and the URL cannot parse */
#define ERROR_GET 10 /* bad --get syntax */
#define ERROR_ITER 11 /* bad --iterate syntax */
#define ERROR_REPL 12 /* a --replace problem */

#ifndef SUPPORTS_URL_STRERROR
/* provide a fake local mockup */
Expand Down Expand Up @@ -181,6 +182,7 @@ static void help(void)
" --as-idn - encode hostnames in idn\n"
" --query-separator [letter] - if something else than '&'\n"
" --redirect [URL] - redirect to this\n"
" --replace [component]=[data] - replace a component\n"
" -s, --set [component]=[data] - set component content\n"
" --sort-query - alpha-sort the query pairs\n"
" --trim [component]=[what] - trim component\n"
Expand Down Expand Up @@ -262,6 +264,7 @@ struct option {
struct curl_slist *set_list;
struct curl_slist *trim_list;
struct curl_slist *iter_list;
struct curl_slist *replace_list;
const char *redirect;
const char *qsep;
const char *format;
Expand Down Expand Up @@ -311,6 +314,7 @@ static void trurl_cleanup_options(struct option *o)
curl_slist_free_all(o->iter_list);
curl_slist_free_all(o->append_query);
curl_slist_free_all(o->trim_list);
curl_slist_free_all(o->replace_list);
curl_slist_free_all(o->append_path);
}

Expand Down Expand Up @@ -458,6 +462,14 @@ static void trimadd(struct option *o,
o->trim_list = n;
}

static void replaceadd(struct option *o,
const char *replace_list) /* [component]=[data] */
{
struct curl_slist *n;
n = curl_slist_append(o->replace_list, replace_list);
if(n)
o->replace_list = n;
}
static bool checkoptarg(struct option *o, const char *str,
const char *given,
const char *arg)
Expand Down Expand Up @@ -574,6 +586,10 @@ static int getarg(struct option *o,
o->urlencode = true;
else if(!strcmp("--quiet", flag))
o->quiet_warnings = true;
else if(!strcmp("--replace", flag)) {
replaceadd(o, arg);
*usedarg = true;
}
else
return 1; /* unrecognized option */
return 0;
Expand Down Expand Up @@ -1222,6 +1238,49 @@ static void sortquery(struct option *o)
}
}

static void replace(struct option *o)
{
struct curl_slist *node;
for(node = o->replace_list; node; node = node->next) {
char *instr = node->data;
char *repl_str;
char *value;
int i;
bool replaced = false;
if(strncmp(instr, "query", 5))
/* for now we can only replace query components */
errorf(o, ERROR_REPL, "Unsupported replace component: %s", instr);
repl_str = strchr(node->data, '=');
jacobmealey marked this conversation as resolved.
Show resolved Hide resolved
repl_str++; /* trim leading '=' */
value = strchr(repl_str, '=');
for(i = 0 ; i < nqpairs; i++) {
char *q = qpairs[i].str;
if(strncmp(q, repl_str, value ?
(size_t)(value - repl_str): strlen(repl_str)))
/* not the correct query, move on */
continue;
/* this is a duplicate remove it. */
free(qpairs[i].str);
if(replaced) {
qpairs[i].len = 0;
qpairs[i].str = strdup("");
}
else {
qpairs[i].len = strlen(repl_str) + 1;
qpairs[i].str = malloc(sizeof(char) * qpairs[i].len);
memcpy(qpairs[i].str, repl_str, qpairs[i].len);
replaced = true;
}
}

if(!replaced) {
trurl_warnf(o, "key '%.*s' not in url, appending to query",
(int) (value - repl_str),
repl_str);
addqpair(repl_str, strlen(repl_str), o->jsonout);
}
}
}
static CURLUcode seturl(struct option *o, CURLU *uh, const char *url)
{
return curl_url_set(uh, CURLUPART_URL, url,
Expand Down Expand Up @@ -1371,6 +1430,9 @@ static void singleurl(struct option *o,
/* trim parts */
trim(o);

/* replace parts */
replace(o);

/* append query segments */
for(p = o->append_query; p; p = p->next) {
addqpair(p->data, strlen(p->data), o->jsonout);
Expand Down