]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_writeout: bsearch the variable name
authorDaniel Stenberg <daniel@haxx.se>
Mon, 10 Jun 2024 11:33:16 +0000 (13:33 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 10 Jun 2024 21:12:05 +0000 (23:12 +0200)
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
src/tool_writeout_json.c
src/tool_writeout_json.h
tests/data/test970
tests/data/test972

index ca8424a3c3d4814b39e34faffcf062fdc7fa0f0b..9ef6946755b37c441b01027652abc091ee492919 100644 (file)
@@ -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);
 }
index 1a7c1bc1183fd468e217101e167a9482d80cac35..ab1caa6a5c64945d50ac78c1be6a0712c7586c4e 100644 (file)
@@ -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);
index 49a28194ff20d4045f2851f41238352406bf046a..91f5d93dc82bf607d1d5d774820ea143e93d02ec 100644 (file)
@@ -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);
index c87a296fafb8ba76973d85a59675e4d1209a8c6d..2f336e9f1064d5e4f8a7c10c53de0eedd0d3da02 100644 (file)
@@ -59,7 +59,7 @@ Accept: */*
 \r
 </protocol>
 <stdout nonewline="yes">
-{"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"}
 </stdout>
 </verify>
 </testcase>
index ebaf4a346e0bc1eba0d289cd6d16577a8671ab09..367eb39d86347994c96dd6f36cfcb91c30a68e60 100644 (file)
@@ -60,7 +60,7 @@ Accept: */*
 \r
 </protocol>
 <stdout mode="text">
-{"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"}
 </stdout>
 </verify>
 </testcase>