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 all 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
127 changes: 126 additions & 1 deletion tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -2230,5 +2230,130 @@
"stderr": true,
"returncode": 9
}
}
},
{
"input": {
"arguments": [
"http://test.org/?key=val",
"--replace",
"key=foo"
]
},
"expected": {
"stdout": "http://test.org/?key=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing&key=val",
"--replace",
"key=foo"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing&key",
"--replace",
"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",
"key"
]
},
"expected": {
"stdout": "http://test.org/?that=thing&key\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"https://example.com?a=123&b=321&b=987",
"--replace",
"b=foo"
]
},
"expected": {
"stdout": "https://example.com/?a=123&b=foo\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"example.org/?quest=best",
"--replace",
"quest=%00",
"--json"
]
},
"expected": {
"stdout": [{
"url": "http://example.org/?quest=%00",
"parts": {
"scheme": "http",
"host": "example.org",
"path": "/"
},
"params": [
{
"key": "quest",
"value": "\u0000"
}
]
}],
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"example.com",
"--replace"
]
},
"expected": {
"stderr": "trurl error: No data passed to replace component\ntrurl error: Try trurl -h for help\n",
"stdout":"",
"returncode": 12
}
},
{
"input": {
"arguments": [
"http://test.org/?that=thing",
"--force-replace",
"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
}
}
]
9 changes: 9 additions & 0 deletions trurl.1
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ 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 [data]"
Replaces a URL query.

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 ignore that item.
.IP "--force-replace [data]"
Works the same as \fI--replace\fP, but trurl will append a missing query string if
it is not in the query list already.
.IP "-s, --set [component][:]=[data]"
Set this URL component. Setting blank string ("") will clear the component
from the URL.
Expand Down
84 changes: 84 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,8 @@ 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 [data] - replaces a query [data]\n"
" --force-replace [data] - appends a new query if not found\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 +265,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 All @@ -280,6 +284,7 @@ struct option {
bool urlencode;
bool end_of_options;
bool quiet_warnings;
bool force_replace;

/* -- stats -- */
unsigned int urls;
Expand Down Expand Up @@ -311,6 +316,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 +464,18 @@ 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 = NULL;
if(replace_list)
n = curl_slist_append(o->replace_list, replace_list);
else
errorf(o, ERROR_REPL, "No data passed to replace component");

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 +592,15 @@ 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 if(!strcmp("--force-replace", flag)) {
replaceadd(o, arg);
o->force_replace = true;
*usedarg = true;
}
else
return 1; /* unrecognized option */
return 0;
Expand Down Expand Up @@ -1222,6 +1249,60 @@ 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) {
struct string key;
struct string value;
bool replaced = false;
int i;
key.str = node->data;
value.str = strchr(key.str, '=');
if(value.str) {
key.len = value.str++ - key.str;
value.len = strlen(value.str);
}
else {
key.len = strlen(key.str);
value.str = NULL;
value.len = 0;
}
for(i = 0; i < nqpairs; i++) {
char *q = qpairs[i].str;
/* not the correct query, move on */
if(strncmp(q, key.str, key.len))
continue;
free(qpairs[i].str);
curl_free(qpairsdec[i].str);
/* this is a duplicate remove it. */
if(replaced) {
qpairs[i].len = 0;
qpairs[i].str = strdup("");
qpairsdec[i].len = 0;
qpairsdec[i].str = strdup("");
continue;
}
struct string *pdec =
memdupdec(key.str, key.len + value.len + 1, o->jsonout);
struct string *p = memdupzero(key.str, key.len + value.len + 1);
qpairs[i].len = p->len;
qpairs[i].str = p->str;
qpairsdec[i].len = pdec->len;
qpairsdec[i].str = pdec->str;
free(pdec);
free(p);
replaced = true;
}

if(!replaced && o->force_replace) {
trurl_warnf(o, "key '%.*s' not in url, appending to query",
(int) (key.len),
key.str);
addqpair(key.str, strlen(key.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 +1452,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
Loading