mirror of
https://github.com/curl/curl.git
synced 2026-08-25 08:43:31 +03:00
tool_urlglob: add named globs
Idea-by: Bastian Jesuiter Verified by test 2408 - 2411 Closes #21409
This commit is contained in:
parent
2a2104f3cf
commit
cb0636980b
13 changed files with 432 additions and 58 deletions
|
|
@ -1067,7 +1067,12 @@ static CURLcode setup_outfile(struct OperationConfig *config,
|
|||
}
|
||||
else if(result) {
|
||||
/* bad globbing */
|
||||
warnf("bad output glob");
|
||||
if(state->urlglob.error) {
|
||||
glob_show_error(&state->urlglob, u->outfile, tool_stderr, result);
|
||||
config->synthetic_error = TRUE;
|
||||
}
|
||||
else
|
||||
warnf("bad output glob");
|
||||
return result;
|
||||
}
|
||||
if(!*per->outfile) {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ static CURLcode glob_fixed(struct URLGlob *glob, char *fixed, size_t len)
|
|||
|
||||
pat->c.set.palloc = 1;
|
||||
pat->c.set.size = 1;
|
||||
pat->name = NULL; /* unnamed */
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +90,7 @@ static int multiply(curl_off_t *amount, curl_off_t with)
|
|||
|
||||
static CURLcode glob_set(struct URLGlob *glob, const char **patternp,
|
||||
size_t *posp, curl_off_t *amount,
|
||||
int globindex)
|
||||
int globindex, const struct Curl_str *name)
|
||||
{
|
||||
/* processes a set expression with the point behind the opening '{'
|
||||
','-separated elements are collected until the next closing '}'
|
||||
|
|
@ -103,6 +104,7 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp,
|
|||
size_t size = 0;
|
||||
char **elem = NULL;
|
||||
size_t palloc = 0; /* start with this */
|
||||
DEBUGASSERT(name);
|
||||
|
||||
while(!done) {
|
||||
switch(*pattern) {
|
||||
|
|
@ -198,6 +200,15 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp,
|
|||
pat->c.set.size = size;
|
||||
pat->c.set.idx = 0;
|
||||
pat->c.set.palloc = palloc;
|
||||
if(curlx_strlen(name)) {
|
||||
pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name));
|
||||
if(!pat->name) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else
|
||||
pat->name = NULL; /* no name */
|
||||
|
||||
return CURLE_OK;
|
||||
error:
|
||||
|
|
@ -212,7 +223,7 @@ error:
|
|||
|
||||
static CURLcode glob_range(struct URLGlob *glob, const char **patternp,
|
||||
size_t *posp, curl_off_t *amount,
|
||||
int globindex)
|
||||
int globindex, const struct Curl_str *name)
|
||||
{
|
||||
/* processes a range expression with the point behind the opening '['
|
||||
- char range: e.g. "a-z]", "B-Q]"
|
||||
|
|
@ -224,8 +235,10 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp,
|
|||
const char *pattern = *patternp;
|
||||
const char *c;
|
||||
|
||||
DEBUGASSERT(name);
|
||||
pat = &glob->pattern[glob->pnum];
|
||||
pat->globindex = globindex;
|
||||
pat->name = NULL; /* no name (so far) */
|
||||
|
||||
if(ISALPHA(*pattern)) {
|
||||
/* character range detected */
|
||||
|
|
@ -340,6 +353,11 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp,
|
|||
return globerror(glob, "bad range specification", *posp,
|
||||
CURLE_URL_MALFORMAT);
|
||||
|
||||
if(curlx_strlen(name)) {
|
||||
pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name));
|
||||
if(!pat->name)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
*patternp = pattern;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
|
@ -407,12 +425,31 @@ static CURLcode add_glob(struct URLGlob *glob, size_t pos)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* returns the named glob pattern (case sensitively) if it exists, otherwise
|
||||
NULL
|
||||
*/
|
||||
static struct URLPattern *glob_find_name(struct URLGlob *glob,
|
||||
struct Curl_str *name)
|
||||
{
|
||||
size_t i;
|
||||
/* find the correct glob entry */
|
||||
for(i = 0; i < glob->pnum; i++) {
|
||||
if(glob->pattern[i].name &&
|
||||
curlx_str_cmp(name, glob->pattern[i].name))
|
||||
return &glob->pattern[i];
|
||||
}
|
||||
return NULL; /* no match */
|
||||
}
|
||||
|
||||
#define MAX_GLOBNAME_LEN 64
|
||||
|
||||
static CURLcode glob_parse(struct URLGlob *glob, const char *pattern,
|
||||
size_t pos, curl_off_t *amount)
|
||||
{
|
||||
/* processes a literal string component of a URL
|
||||
special characters '{' and '[' branch to set/range processing functions
|
||||
*/
|
||||
const char *ipattern = pattern; /* start position */
|
||||
CURLcode result = CURLE_OK;
|
||||
int globindex = 0; /* count "actual" globs */
|
||||
|
||||
|
|
@ -464,21 +501,39 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern,
|
|||
curlx_dyn_reset(&glob->buf);
|
||||
}
|
||||
else {
|
||||
struct Curl_str name;
|
||||
if(!*pattern) /* done */
|
||||
break;
|
||||
else if(*pattern == '{') {
|
||||
/* process set pattern */
|
||||
else if((*pattern == '{') || (*pattern == '[')) {
|
||||
bool set = (*pattern == '{');
|
||||
const char *start;
|
||||
pattern++;
|
||||
pos++;
|
||||
result = glob_set(glob, &pattern, &pos, amount, globindex++);
|
||||
if(!result)
|
||||
result = add_glob(glob, pos);
|
||||
}
|
||||
else if(*pattern == '[') {
|
||||
/* process range pattern */
|
||||
pattern++;
|
||||
pos++;
|
||||
result = glob_range(glob, &pattern, &pos, amount, globindex++);
|
||||
start = pattern;
|
||||
/* fetch the name, if provided */
|
||||
if(curlx_str_single(&pattern, '<') ||
|
||||
curlx_str_until(&pattern, &name, MAX_GLOBNAME_LEN, '>') ||
|
||||
curlx_str_single(&pattern, '>')) {
|
||||
/* Not a proper name. This is not reporting errors on syntax errors
|
||||
on purpose: it means that if there is an existing use case that
|
||||
uses what looks like a broken named-glob syntax (now introduced)
|
||||
we let that function like before. */
|
||||
curlx_str_init(&name);
|
||||
pattern = start; /* reset any partial patch */
|
||||
}
|
||||
else {
|
||||
/* check that the name is not already used */
|
||||
struct URLPattern *p = glob_find_name(glob, &name);
|
||||
if(p)
|
||||
return globerror(glob, "Duplicate glob name", 2 + start - ipattern,
|
||||
CURLE_URL_MALFORMAT);
|
||||
}
|
||||
if(set)
|
||||
result = glob_set(glob, &pattern, &pos, amount, globindex++, &name);
|
||||
else
|
||||
result = glob_range(glob, &pattern, &pos, amount, globindex++,
|
||||
&name);
|
||||
|
||||
if(!result)
|
||||
result = add_glob(glob, pos);
|
||||
}
|
||||
|
|
@ -492,6 +547,26 @@ bool glob_inuse(struct URLGlob *glob)
|
|||
return glob->palloc ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* a glob error has been confirmed, this outputs details about it to the set
|
||||
error stream */
|
||||
void glob_show_error(struct URLGlob *glob, const char *url, FILE *error,
|
||||
CURLcode result)
|
||||
{
|
||||
char text[512];
|
||||
const char *t;
|
||||
if(glob->pos) {
|
||||
curl_msnprintf(text, sizeof(text), "%s in position %zu:\n%s\n%*s^",
|
||||
glob->error,
|
||||
glob->pos, url, (int)glob->pos - 1, " ");
|
||||
t = text;
|
||||
}
|
||||
else
|
||||
t = glob->error;
|
||||
|
||||
/* send error description to the error-stream */
|
||||
curl_mfprintf(error, "curl: (%d) %s\n", result, t);
|
||||
}
|
||||
|
||||
CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum,
|
||||
FILE *error)
|
||||
{
|
||||
|
|
@ -511,21 +586,8 @@ CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum,
|
|||
|
||||
result = glob_parse(glob, url, 1, &amount);
|
||||
if(result) {
|
||||
if(error && glob->error) {
|
||||
char text[512];
|
||||
const char *t;
|
||||
if(glob->pos) {
|
||||
curl_msnprintf(text, sizeof(text), "%s in URL position %zu:\n%s\n%*s^",
|
||||
glob->error,
|
||||
glob->pos, url, (int)glob->pos - 1, " ");
|
||||
t = text;
|
||||
}
|
||||
else
|
||||
t = glob->error;
|
||||
|
||||
/* send error description to the error-stream */
|
||||
curl_mfprintf(error, "curl: (%d) %s\n", result, t);
|
||||
}
|
||||
if(error && glob->error)
|
||||
glob_show_error(glob, url, error, result);
|
||||
*urlnum = 1;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -547,6 +609,7 @@ void glob_cleanup(struct URLGlob *glob)
|
|||
curlx_safefree(glob->pattern[i].c.set.elem[elem]);
|
||||
curlx_safefree(glob->pattern[i].c.set.elem);
|
||||
}
|
||||
curlx_safefree(glob->pattern[i].name);
|
||||
}
|
||||
curlx_safefree(glob->pattern);
|
||||
glob->palloc = 0;
|
||||
|
|
@ -643,6 +706,7 @@ CURLcode glob_match_url(char **output, const char *filename,
|
|||
struct URLGlob *glob, SANITIZEcode *sc)
|
||||
{
|
||||
struct dynbuf dyn;
|
||||
const char *ifilename = filename;
|
||||
*output = NULL;
|
||||
*sc = SANITIZE_ERR_OK;
|
||||
|
||||
|
|
@ -650,11 +714,11 @@ CURLcode glob_match_url(char **output, const char *filename,
|
|||
|
||||
while(*filename) {
|
||||
CURLcode result = CURLE_OK;
|
||||
struct URLPattern *pat = NULL;
|
||||
if(*filename == '#' && ISDIGIT(filename[1])) {
|
||||
const char *ptr = filename;
|
||||
/* a numbered glob reference */
|
||||
const char *ptr = filename++;
|
||||
curl_off_t num;
|
||||
struct URLPattern *pat = NULL;
|
||||
filename++;
|
||||
if(!curlx_str_number(&filename, &num, glob->pnum) && num) {
|
||||
size_t i;
|
||||
num--; /* make it zero based */
|
||||
|
|
@ -666,31 +730,49 @@ CURLcode glob_match_url(char **output, const char *filename,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pat) {
|
||||
switch(pat->type) {
|
||||
case GLOB_SET:
|
||||
if(pat->c.set.elem)
|
||||
result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]);
|
||||
break;
|
||||
case GLOB_ASCII: {
|
||||
char letter = (char)pat->c.ascii.letter;
|
||||
result = curlx_dyn_addn(&dyn, &letter, 1);
|
||||
break;
|
||||
}
|
||||
case GLOB_NUM:
|
||||
result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T,
|
||||
pat->c.num.npad, pat->c.num.idx);
|
||||
break;
|
||||
default:
|
||||
DEBUGASSERT(0);
|
||||
if(!pat)
|
||||
filename = ptr;
|
||||
}
|
||||
else if(*filename == '#' && (filename[1] == '<')) {
|
||||
/* a named glob reference */
|
||||
struct Curl_str name;
|
||||
const char *ptr = filename;
|
||||
filename += 2; /* pass both leading bytes */
|
||||
if(!curlx_str_until(&filename, &name, MAX_GLOBNAME_LEN, '>') &&
|
||||
!curlx_str_single(&filename, '>')) {
|
||||
/* find the correct glob entry */
|
||||
pat = glob_find_name(glob, &name);
|
||||
if(!pat) {
|
||||
/* when the name is given correctly, it needs to be an existing glob
|
||||
name, which makes this an error */
|
||||
curlx_dyn_free(&dyn);
|
||||
return CURLE_FAILED_INIT;
|
||||
return globerror(glob, "no glob exists with this name",
|
||||
filename - ifilename, CURLE_BAD_FUNCTION_ARGUMENT);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* #[num] out of range, use the #[num] in the output */
|
||||
result = curlx_dyn_addn(&dyn, ptr, filename - ptr);
|
||||
if(!pat)
|
||||
filename = ptr;
|
||||
}
|
||||
if(pat) {
|
||||
switch(pat->type) {
|
||||
case GLOB_SET:
|
||||
if(pat->c.set.elem)
|
||||
result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]);
|
||||
break;
|
||||
case GLOB_ASCII: {
|
||||
char letter = (char)pat->c.ascii.letter;
|
||||
result = curlx_dyn_addn(&dyn, &letter, 1);
|
||||
break;
|
||||
}
|
||||
case GLOB_NUM:
|
||||
result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T,
|
||||
pat->c.num.npad, pat->c.num.idx);
|
||||
break;
|
||||
default:
|
||||
DEBUGASSERT(0);
|
||||
curlx_dyn_free(&dyn);
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = curlx_dyn_addn(&dyn, filename++, 1);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ typedef enum {
|
|||
|
||||
struct URLPattern {
|
||||
globtype type;
|
||||
char *name; /* if not NULL */
|
||||
int globindex; /* the number of this particular glob or -1 if not used
|
||||
within {} or [] */
|
||||
union {
|
||||
|
|
@ -71,6 +72,9 @@ struct URLGlob {
|
|||
size_t pos; /* column position of error or 0 */
|
||||
};
|
||||
|
||||
void glob_show_error(struct URLGlob *glob, const char *url, FILE *error,
|
||||
CURLcode result);
|
||||
|
||||
CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum,
|
||||
FILE *error);
|
||||
CURLcode glob_next_url(char **globbed, struct URLGlob *glob);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue