From 4464c9f2c6444142b5cc6cd756eec039410c1721 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 10 Jun 2024 13:33:16 +0200 Subject: [PATCH] tool_writeout: bsearch the variable name As the list of variable names grows, doing a simple loop to find the name get increasingly worse. This switches to a bsearch. Also: do a case sensitive check for the variable name. The names have not been documented to be case insensitive and there is no point in having them so. Closes #13914 --- src/tool_writeout.c | 123 ++++++++++++++++++++++----------------- src/tool_writeout_json.c | 5 +- src/tool_writeout_json.h | 1 + tests/data/test970 | 2 +- tests/data/test972 | 2 +- 5 files changed, 76 insertions(+), 57 deletions(-) diff --git a/src/tool_writeout.c b/src/tool_writeout.c index ca8424a3c3..9ef6946755 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -70,12 +70,12 @@ static const struct httpmap http_version[] = { Yes: "http_version": "1.1" No: "http_version": 1.1 - Variable names should be in alphabetical order. + Variable names MUST be in alphabetical order. */ static const struct writeoutvar variables[] = { {"certs", VAR_CERT, CURLINFO_NONE, writeString}, - {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString}, {"conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset}, + {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString}, {"errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString}, {"exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong}, {"filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString}, @@ -125,30 +125,29 @@ static const struct writeoutvar variables[] = { writeTime}, {"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime}, {"url", VAR_INPUT_URL, CURLINFO_NONE, writeString}, - {"url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString}, - {"url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString}, - {"url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString}, - {"url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString}, + {"url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString}, {"url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString}, - {"url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString}, + {"url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString}, + {"url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString}, {"url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString}, + {"url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString}, {"url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString}, - {"url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString}, + {"url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString}, + {"url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString}, {"url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString}, - {"urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString}, - {"urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString}, - {"urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString}, - {"urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString}, + {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString}, + {"urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString}, {"urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString}, - {"urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString}, + {"urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString}, + {"urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString}, {"urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString}, + {"urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString}, {"urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString}, - {"urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString}, + {"urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString}, + {"urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString}, {"urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString}, - {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString}, {"urlnum", VAR_URLNUM, CURLINFO_NONE, writeLong}, - {"xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset}, - {NULL, VAR_NONE, CURLINFO_NONE, NULL} + {"xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset} }; static int writeTime(FILE *stream, const struct writeoutvar *wovar, @@ -509,6 +508,17 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, return 1; /* return 1 if anything was written */ } +static int +matchvar(const void *m1, const void *m2) +{ + const struct writeoutvar *v1 = m1; + const struct writeoutvar *v2 = m2; + + return strcmp(v1->name, v2->name); +} + +#define MAX_WRITEOUT_NAME_LENGTH 24 + void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, CURLcode per_result) { @@ -519,6 +529,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, struct curl_certinfo *certinfo; CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo); bool fclose_stream = FALSE; + struct dynbuf name; if(!writeinfo) return; @@ -526,6 +537,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, if(!res && certinfo) per->certinfo = certinfo; + curlx_dyn_init(&name, MAX_WRITEOUT_NAME_LENGTH); while(ptr && *ptr && !done) { if('%' == *ptr && ptr[1]) { if('%' == ptr[1]) { @@ -538,8 +550,8 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, char *end; size_t vlen; if('{' == ptr[1]) { - int i; - bool match = FALSE; + struct writeoutvar *wv = NULL; + struct writeoutvar find = { 0 }; end = strchr(ptr, '}'); ptr += 2; /* pass the % and the { */ if(!end) { @@ -547,43 +559,47 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, continue; } vlen = end - ptr; - for(i = 0; variables[i].name; i++) { - if((strlen(variables[i].name) == vlen) && - curl_strnequal(ptr, variables[i].name, vlen)) { - match = TRUE; - switch(variables[i].id) { - case VAR_ONERROR: - if(per_result == CURLE_OK) - /* this isn't error so skip the rest */ - done = TRUE; - break; - case VAR_STDOUT: - if(fclose_stream) - fclose(stream); - fclose_stream = FALSE; - stream = stdout; - break; - case VAR_STDERR: - if(fclose_stream) - fclose(stream); - fclose_stream = FALSE; - stream = tool_stderr; - break; - case VAR_JSON: - ourWriteOutJSON(stream, variables, per, per_result); - break; - case VAR_HEADER_JSON: - headerJSON(stream, per); - break; - default: - (void)variables[i].writefunc(stream, &variables[i], - per, per_result, false); - break; - } + + curlx_dyn_reset(&name); + if(!curlx_dyn_addn(&name, ptr, vlen)) { + find.name = curlx_dyn_ptr(&name); + wv = bsearch(&find, + variables, sizeof(variables)/sizeof(variables[0]), + sizeof(variables[0]), matchvar); + } + if(wv) { + switch(wv->id) { + case VAR_ONERROR: + if(per_result == CURLE_OK) + /* this isn't error so skip the rest */ + done = TRUE; + break; + case VAR_STDOUT: + if(fclose_stream) + fclose(stream); + fclose_stream = FALSE; + stream = stdout; + break; + case VAR_STDERR: + if(fclose_stream) + fclose(stream); + fclose_stream = FALSE; + stream = tool_stderr; + break; + case VAR_JSON: + ourWriteOutJSON(stream, variables, + sizeof(variables)/sizeof(variables[0]), + per, per_result); + break; + case VAR_HEADER_JSON: + headerJSON(stream, per); + break; + default: + (void)wv->writefunc(stream, wv, per, per_result, false); break; } } - if(!match) { + else { fprintf(tool_stderr, "curl: unknown --write-out variable: '%.*s'\n", (int)vlen, ptr); @@ -673,4 +689,5 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, } if(fclose_stream) fclose(stream); + curlx_dyn_free(&name); } diff --git a/src/tool_writeout_json.c b/src/tool_writeout_json.c index 1a7c1bc118..ab1caa6a5c 100644 --- a/src/tool_writeout_json.c +++ b/src/tool_writeout_json.c @@ -101,13 +101,14 @@ void jsonWriteString(FILE *stream, const char *in, bool lowercase) } void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[], + size_t nentries, struct per_transfer *per, CURLcode per_result) { - int i; + size_t i; fputs("{", stream); - for(i = 0; mappings[i].name != NULL; i++) { + for(i = 0; i < nentries; i++) { if(mappings[i].writefunc && mappings[i].writefunc(stream, &mappings[i], per, per_result, true)) fputs(",", stream); diff --git a/src/tool_writeout_json.h b/src/tool_writeout_json.h index 49a28194ff..91f5d93dc8 100644 --- a/src/tool_writeout_json.h +++ b/src/tool_writeout_json.h @@ -30,6 +30,7 @@ int jsonquoted(const char *in, size_t len, struct curlx_dynbuf *out, bool lowercase); void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[], + size_t nentries, struct per_transfer *per, CURLcode per_result); void headerJSON(FILE *stream, struct per_transfer *per); void jsonWriteString(FILE *stream, const char *in, bool lowercase); diff --git a/tests/data/test970 b/tests/data/test970 index c87a296faf..2f336e9f10 100644 --- a/tests/data/test970 +++ b/tests/data/test970 @@ -59,7 +59,7 @@ Accept: */* -{"certs":"","content_type":"text/html","conn_id":0,"errormsg":null,"exitcode":0,"filename_effective":"%LOGDIR/out%TESTNUMBER","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"proxy_used":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"http","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url.scheme":"http","url.user":null,"url.password":null,"url.options":null,"url.host":"%HOSTIP","url.port":"%HTTPPORT","url.path":"/%TESTNUMBER","url.query":null,"url.fragment":null,"url.zoneid":null,"urle.scheme":"http","urle.user":null,"urle.password":null,"urle.options":null,"urle.host":"%HOSTIP","urle.port":"%HTTPPORT","urle.path":"/%TESTNUMBER","urle.query":null,"urle.fragment":null,"urle.zoneid":null,"url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"xfer_id":0,"curl_version":"curl-unit-test-fake-version"} +{"certs":"","conn_id":0,"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"%LOGDIR/out%TESTNUMBER","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"proxy_used":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"http","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url.fragment":null,"url.host":"127.0.0.1","url.options":null,"url.password":null,"url.path":"/%TESTNUMBER","url.port":"%HTTPPORT","url.query":null,"url.scheme":"http","url.user":null,"url.zoneid":null,"url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urle.fragment":null,"urle.host":"127.0.0.1","urle.options":null,"urle.password":null,"urle.path":"/%TESTNUMBER","urle.port":"%HTTPPORT","urle.query":null,"urle.scheme":"http","urle.user":null,"urle.zoneid":null,"urlnum":0,"xfer_id":0,"curl_version":"curl-unit-test-fake-version"} diff --git a/tests/data/test972 b/tests/data/test972 index ebaf4a346e..367eb39d86 100644 --- a/tests/data/test972 +++ b/tests/data/test972 @@ -60,7 +60,7 @@ Accept: */* -{"certs":"","content_type":"text/html","conn_id":0,"errormsg":null,"exitcode":0,"filename_effective":"%LOGDIR/out972","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"%HOSTIP","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"proxy_used":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"http","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url.scheme":"http","url.user":null,"url.password":null,"url.options":null,"url.host":"%HOSTIP","url.port":"%HTTPPORT","url.path":"/%TESTNUMBER","url.query":null,"url.fragment":null,"url.zoneid":null,"urle.scheme":"http","urle.user":null,"urle.password":null,"urle.options":null,"urle.host":"%HOSTIP","urle.port":"%HTTPPORT","urle.path":"/%TESTNUMBER","urle.query":null,"urle.fragment":null,"urle.zoneid":null,"url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"xfer_id":0,"curl_version":"curl-unit-test-fake-version"} +{"certs":"","conn_id":0,"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"%LOGDIR/out%TESTNUMBER","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"proxy_used":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"http","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url.fragment":null,"url.host":"127.0.0.1","url.options":null,"url.password":null,"url.path":"/%TESTNUMBER","url.port":"%HTTPPORT","url.query":null,"url.scheme":"http","url.user":null,"url.zoneid":null,"url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urle.fragment":null,"urle.host":"127.0.0.1","urle.options":null,"urle.password":null,"urle.path":"/%TESTNUMBER","urle.port":"%HTTPPORT","urle.query":null,"urle.scheme":"http","urle.user":null,"urle.zoneid":null,"urlnum":0,"xfer_id":0,"curl_version":"curl-unit-test-fake-version"} -- 2.47.3