Skip to content

Commit

Permalink
trurl: make must: prefix for gets return error for missing part
Browse files Browse the repository at this point in the history
Normally a missing component just causes a blank output. This currently
does not work "scheme" (it defaults to the "guessed" scheme).

"must:" deos not return error if query/fragment was there but empty

(this latter part requires libcurl 8.8.0 or later. The feature is named
'get-empty' in the version output.)

Added tests

Closes #336
  • Loading branch information
bagder committed Aug 27, 2024
1 parent bada531 commit a1e18d4
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 40 deletions.
43 changes: 43 additions & 0 deletions tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -2726,5 +2726,48 @@
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"https://example.com/",
"--get",
"{must:query}"
]
},
"expected": {
"stdout": "",
"stderr": "trurl error: missing must:query\ntrurl error: Try trurl -h for help\n",
"returncode": 10
}
},
{
"input": {
"arguments": [
"https://example.com/?",
"--get",
"{must:query}"
]
},
"required": ["get-empty"],
"expected": {
"stdout": "\n",
"stderr": "",
"returncode": 0
}
},
{
"input": {
"arguments": [
"https://example.com/",
"--get",
"{must:fragment}"
]
},
"expected": {
"stdout": "",
"stderr": "trurl error: missing must:fragment\ntrurl error: Try trurl -h for help\n",
"returncode": 10
}
}
]
70 changes: 45 additions & 25 deletions trurl.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ typedef enum {
#else
#define CURLU_NO_GUESS_SCHEME 0
#endif
#if CURL_AT_LEAST_VERSION(8,8,0)
#define SUPPORTS_GET_EMPTY
#else
#define CURLU_GET_EMPTY 0
#endif

#define OUTPUT_URL 0 /* default */
#define OUTPUT_SCHEME 1
Expand All @@ -101,6 +106,7 @@ enum {
VARMODIFIER_DEFAULT = 1 << 2,
VARMODIFIER_PUNY = 1 << 3,
VARMODIFIER_PUNY2IDN = 1 << 4,
VARMODIFIER_EMPTY = 1 << 8,
};

struct var {
Expand Down Expand Up @@ -280,6 +286,9 @@ static void show_version(void)
fprintf(stdout, "%s version %s libcurl/%s [built-with %s]\n",
PROGNAME, TRURL_VERSION_TXT, data->version, LIBCURL_VERSION);
fprintf(stdout, "features:");
#ifdef SUPPORTS_GET_EMPTY
fprintf(stdout, " get-empty");
#endif
#ifdef SUPPORTS_IMAP_OPTIONS
if(supports_imap)
fprintf(stdout, " imap-options");
Expand Down Expand Up @@ -744,39 +753,43 @@ static const struct var *comp2var(const char *name, size_t vlen)
static CURLUcode geturlpart(struct option *o, int modifiers, CURLU *uh,
CURLUPart part, char **out)
{
CURLUcode rc = curl_url_get(uh, part, out,
(((modifiers & VARMODIFIER_DEFAULT) ||
o->default_port) ?
CURLU_DEFAULT_PORT :
((part != CURLUPART_URL || o->keep_port) ?
0 : CURLU_NO_DEFAULT_PORT))|
CURLUcode rc =
curl_url_get(uh, part, out,
(((modifiers & VARMODIFIER_DEFAULT) ||
o->default_port) ?
CURLU_DEFAULT_PORT :
((part != CURLUPART_URL || o->keep_port) ?
0 : CURLU_NO_DEFAULT_PORT))|
#ifdef SUPPORTS_PUNYCODE
(((modifiers & VARMODIFIER_PUNY) || o->punycode) ?
CURLU_PUNYCODE : 0)|
(((modifiers & VARMODIFIER_PUNY) || o->punycode) ?
CURLU_PUNYCODE : 0)|
#endif
#ifdef SUPPORTS_PUNY2IDN
(((modifiers & VARMODIFIER_PUNY2IDN) || o->puny2idn) ?
CURLU_PUNY2IDN : 0) |
(((modifiers & VARMODIFIER_PUNY2IDN) || o->puny2idn) ?
CURLU_PUNY2IDN : 0) |
#endif
(o->curl ? 0 : CURLU_NON_SUPPORT_SCHEME)|
(((modifiers & VARMODIFIER_URLENCODED) ||
o->urlencode) ?
0 :CURLU_URLDECODE));
#ifdef SUPPORTS_GET_EMPTY
((modifiers & VARMODIFIER_EMPTY) ? CURLU_GET_EMPTY : 0) |
#endif
(o->curl ? 0 : CURLU_NON_SUPPORT_SCHEME)|
(((modifiers & VARMODIFIER_URLENCODED) ||
o->urlencode) ?
0 :CURLU_URLDECODE));

#ifdef SUPPORTS_PUNY2IDN
/* retry get w/ out puny2idn to handle invalid punycode conversions */
if(rc == CURLUE_BAD_HOSTNAME &&
(o->puny2idn || (modifiers & VARMODIFIER_PUNY2IDN))) {
curl_free(*out);
modifiers &= ~VARMODIFIER_PUNY2IDN;
o->puny2idn = false;
trurl_warnf(o,
/* retry get w/ out puny2idn to handle invalid punycode conversions */
if(rc == CURLUE_BAD_HOSTNAME &&
(o->puny2idn || (modifiers & VARMODIFIER_PUNY2IDN))) {
curl_free(*out);
modifiers &= ~VARMODIFIER_PUNY2IDN;
o->puny2idn = false;
trurl_warnf(o,
"Error converting url to IDN [%s]",
curl_url_strerror(rc));
return geturlpart(o, modifiers, uh, part, out);
}
return geturlpart(o, modifiers, uh, part, out);
}
#endif
return rc;
return rc;
}

static bool is_valid_trurl_error(CURLUcode rc)
Expand Down Expand Up @@ -847,6 +860,7 @@ static void get(struct option *o, CURLU *uh)
bool isquery = false;
bool queryall = false;
bool strict = false; /* strict mode, fail on URL decode problems */
bool must = false; /* must mode, fail on missing component */
int mods = 0;
end = strchr(ptr, endbyte);
ptr++; /* pass the { */
Expand Down Expand Up @@ -886,6 +900,10 @@ static void get(struct option *o, CURLU *uh)
}
else if(!strncmp(ptr, "strict:", wordlen))
strict = true;
else if(!strncmp(ptr, "must:", wordlen)) {
must = true;
mods |= VARMODIFIER_EMPTY;
}
else if(!strncmp(ptr, "url:", wordlen))
mods |= VARMODIFIER_URLENCODED;
else {
Expand Down Expand Up @@ -925,7 +943,9 @@ static void get(struct option *o, CURLU *uh)
fputs(nurl, stream);
curl_free(nurl);
}
else if(is_valid_trurl_error(rc)) {
else if(!is_valid_trurl_error(rc) && must)
errorf(o, ERROR_GET, "missing must:%s", v->name);
else if(is_valid_trurl_error(rc) || strict) {
if((rc == CURLUE_URLDECODE) && strict)
errorf(o, ERROR_GET, "problems URL decoding %s", v->name);
else
Expand Down
32 changes: 17 additions & 15 deletions trurl.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,30 +114,32 @@ component prefixed with `url:` like `{url:path}`, it gets output URL encoded.
As a shortcut, `url:` also works written as a single colon: `{:path}`.

URL decoding a component may cause problems to display it. Such problems make
a warning get displayed unless **--quiet** is used. URL decode problems can
optionally be turned into errors by prefixing the component name with
`strict:`, as in `{strict:path}`. In this stricter mode, a URL decode problem
makes trurl stop what it is doing and return with exit code 10.
a warning get displayed unless **--quiet** is used.

You may also prefix components with **default:** and/or **puny:** or
**idn:**, in any order.
If **strict:** is specified, like `{strict:path}`, URL decode problems are
turned into errors. In this stricter mode, a URL decode problem makes trurl
stop what it is doing and return with exit code 10.

If **default:** is specified, like `{default:url}` or
`{default:port}`, and the port is not explicitly specified in the URL,
the scheme's default port is output if it is known.
If **must:** is specified, like `{must:query}`, it makes trurl return an error
if the requested component does not exist in the URL. By default a missing
component will just be shown blank.

If **default:** is specified, like `{default:url}` or `{default:port}`, and
the port is not explicitly specified in the URL, the scheme's default port is
output if it is known.

If **puny:** is specified, like `{puny:url}` or `{puny:host}`, the punycoded
version of the host name is used in the output. This option is mutually
exclusive with **idn:**.

If **idn:** is specified like `{idn:url}` or `{idn:host}`, the International
Domain Name version of the host name is used in the output if it is provided as
a correctly encoded punycode version. This option is mutually exclusive with **puny:**.
Domain Name version of the host name is used in the output if it is provided
as a correctly encoded punycode version. This option is mutually exclusive
with **puny:**.

If *--default-port* is specified, all formats are expanded as if
they used *default:*; and if *--punycode* is used, all formats
are expanded as if they used *puny:*. Also note that `{url}` is
affected by the *--keep-port* option.
If *--default-port* is specified, all formats are expanded as if they used
*default:*; and if *--punycode* is used, all formats are expanded as if they
used *puny:*. Also note that `{url}` is affected by the *--keep-port* option.

Hosts provided as IPv6 numerical addresses are provided within square
brackets. Like `[fe80::20c:29ff:fe9c:409b]`.
Expand Down

0 comments on commit a1e18d4

Please sign in to comment.