]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_operate: split up single_transfer
authorDaniel Stenberg <daniel@haxx.se>
Fri, 23 May 2025 22:30:22 +0000 (00:30 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 24 May 2025 13:43:08 +0000 (15:43 +0200)
Complexity reduced from 124 to 83

Remove whitelisting of this function from the complexity script.

Closes #17437

scripts/top-complexity
src/tool_operate.c

index c7129a1eb8f47728ad298be37b5c49c38376f48b..675d83b1854d3820f87d19345796f5d50421e7cf 100755 (executable)
@@ -75,8 +75,7 @@ my @output=`$cmd`;
 
 # these functions can have these scores, but not higher
 my %whitelist = (
-    'getparameter' => 142,
-    'single_transfer' => 124
+    'getparameter' => 142
     );
 
 # functions with complexity above this level causes the function to return error
index 1406b8790f2c13a4de6fc2134fc3857d4f996d40..af9632c7814049694b24865bf58a2fa6bf31f867 100644 (file)
@@ -796,6 +796,283 @@ static CURLcode append2query(struct GlobalConfig *global,
   return result;
 }
 
+static CURLcode etag_compare(struct GlobalConfig *global,
+                             struct OperationConfig *config)
+{
+  CURLcode result = CURLE_OK;
+  char *etag_from_file = NULL;
+  char *header = NULL;
+  ParameterError pe;
+
+  /* open file for reading: */
+  FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
+  if(!file)
+    warnf(global, "Failed to open %s: %s", config->etag_compare_file,
+          strerror(errno));
+
+  if((PARAM_OK == file2string(&etag_from_file, file)) &&
+     etag_from_file) {
+    header = aprintf("If-None-Match: %s", etag_from_file);
+    tool_safefree(etag_from_file);
+  }
+  else
+    header = aprintf("If-None-Match: \"\"");
+
+  if(!header) {
+    if(file)
+      fclose(file);
+    errorf(global,
+           "Failed to allocate memory for custom etag header");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* add Etag from file to list of custom headers */
+  pe = add2list(&config->headers, header);
+  tool_safefree(header);
+
+  if(file)
+    fclose(file);
+  if(pe != PARAM_OK)
+    result = CURLE_OUT_OF_MEMORY;
+  return result;
+}
+
+static CURLcode etag_store(struct GlobalConfig *global,
+                           struct OperationConfig *config,
+                           struct OutStruct *etag_save,
+                           bool *skip)
+{
+  if(config->create_dirs) {
+    CURLcode result = create_dir_hierarchy(config->etag_save_file, global);
+    if(result)
+      return result;
+  }
+
+  /* open file for output: */
+  if(strcmp(config->etag_save_file, "-")) {
+    FILE *newfile = fopen(config->etag_save_file, "ab");
+    if(!newfile) {
+      struct State *state = &config->state;
+      warnf(global, "Failed creating file for saving etags: \"%s\". "
+            "Skip this transfer", config->etag_save_file);
+      tool_safefree(state->outfiles);
+      glob_cleanup(&state->urls);
+      *skip = TRUE;
+      return CURLE_OK;
+    }
+    else {
+      etag_save->filename = config->etag_save_file;
+      etag_save->s_isreg = TRUE;
+      etag_save->fopened = TRUE;
+      etag_save->stream = newfile;
+    }
+  }
+  else {
+    /* always use binary mode for protocol header output */
+    CURL_SET_BINMODE(etag_save->stream);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode setup_headerfile(struct GlobalConfig *global,
+                                 struct OperationConfig *config,
+                                 struct per_transfer *per,
+                                 struct OutStruct *heads)
+{
+  /* open file for output: */
+  if(!strcmp(config->headerfile, "%")) {
+    heads->stream = stderr;
+    /* use binary mode for protocol header output */
+    CURL_SET_BINMODE(heads->stream);
+  }
+  else if(strcmp(config->headerfile, "-")) {
+    FILE *newfile;
+
+    /*
+     * Since every transfer has its own file handle for dumping
+     * the headers, we need to open it in append mode, since transfers
+     * might finish in any order.
+     * The first transfer just clears the file.
+     *
+     * Consider placing the file handle inside the OperationConfig, so
+     * that it does not need to be opened/closed for every transfer.
+     */
+    if(config->create_dirs) {
+      CURLcode result = create_dir_hierarchy(config->headerfile, global);
+      /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
+      if(result)
+        return result;
+    }
+    if(!per->prev || per->prev->config != config) {
+      newfile = fopen(config->headerfile, "wb");
+      if(newfile)
+        fclose(newfile);
+    }
+    newfile = fopen(config->headerfile, "ab");
+
+    if(!newfile) {
+      errorf(global, "Failed to open %s", config->headerfile);
+      return CURLE_WRITE_ERROR;
+    }
+    else {
+      heads->filename = config->headerfile;
+      heads->s_isreg = TRUE;
+      heads->fopened = TRUE;
+      heads->stream = newfile;
+    }
+  }
+  else {
+    /* always use binary mode for protocol header output */
+    CURL_SET_BINMODE(heads->stream);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode setup_outfile(struct GlobalConfig *global,
+                              struct OperationConfig *config,
+                              struct per_transfer *per,
+                              struct OutStruct *outs,
+                              bool *skipped)
+{
+  /*
+   * We have specified a filename to store the result in, or we have
+   * decided we want to use the remote filename.
+   */
+  struct State *state = &config->state;
+
+  if(!per->outfile) {
+    /* extract the filename from the URL */
+    CURLcode result = get_url_file_name(global, &per->outfile, per->url);
+    if(result) {
+      errorf(global, "Failed to extract a filename"
+             " from the URL to use for storage");
+      return result;
+    }
+  }
+  else if(state->urls) {
+    /* fill '#1' ... '#9' terms from URL pattern */
+    char *storefile = per->outfile;
+    CURLcode result = glob_match_url(&per->outfile, storefile, state->urls);
+    tool_safefree(storefile);
+    if(result) {
+      /* bad globbing */
+      warnf(global, "bad output glob");
+      return result;
+    }
+    if(!*per->outfile) {
+      warnf(global, "output glob produces empty string");
+      return CURLE_WRITE_ERROR;
+    }
+  }
+  DEBUGASSERT(per->outfile);
+
+  if(config->output_dir && *config->output_dir) {
+    char *d = aprintf("%s/%s", config->output_dir, per->outfile);
+    if(!d)
+      return CURLE_WRITE_ERROR;
+    free(per->outfile);
+    per->outfile = d;
+  }
+  /* Create the directory hierarchy, if not pre-existent to a multiple
+     file output call */
+
+  if(config->create_dirs) {
+    CURLcode result = create_dir_hierarchy(per->outfile, global);
+    /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
+    if(result)
+      return result;
+  }
+
+  if(config->skip_existing) {
+    struct_stat fileinfo;
+    if(!stat(per->outfile, &fileinfo)) {
+      /* file is present */
+      notef(global, "skips transfer, \"%s\" exists locally",
+            per->outfile);
+      per->skip = TRUE;
+      *skipped = TRUE;
+    }
+  }
+
+  if(config->resume_from_current) {
+    /* We are told to continue from where we are now. Get the size
+       of the file as it is now and open it for append instead */
+    struct_stat fileinfo;
+    /* VMS -- Danger, the filesize is only valid for stream files */
+    if(0 == stat(per->outfile, &fileinfo))
+      /* set offset to current file size: */
+      config->resume_from = fileinfo.st_size;
+    else
+      /* let offset be 0 */
+      config->resume_from = 0;
+  }
+
+  if(config->resume_from) {
+#ifdef __VMS
+    /* open file for output, forcing VMS output format into stream
+       mode which is needed for stat() call above to always work. */
+    FILE *file = fopen(outfile, "ab",
+                       "ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
+#else
+    /* open file for output: */
+    FILE *file = fopen(per->outfile, "ab");
+#endif
+    if(!file) {
+      errorf(global, "cannot open '%s'", per->outfile);
+      return CURLE_WRITE_ERROR;
+    }
+    outs->fopened = TRUE;
+    outs->stream = file;
+    outs->init = config->resume_from;
+  }
+  else {
+    outs->stream = NULL; /* open when needed */
+  }
+  outs->filename = per->outfile;
+  outs->s_isreg = TRUE;
+  return CURLE_OK;
+}
+
+static void check_stdin_upload(struct GlobalConfig *global,
+                               struct OperationConfig *config,
+                               struct per_transfer *per)
+{
+  /* count to see if there are more than one auth bit set
+     in the authtype field */
+  int authbits = 0;
+  int bitcheck = 0;
+  while(bitcheck < 32) {
+    if(config->authtype & (1UL << bitcheck++)) {
+      authbits++;
+      if(authbits > 1) {
+        /* more than one, we are done! */
+        break;
+      }
+    }
+  }
+
+  /*
+   * If the user has also selected --anyauth or --proxy-anyauth
+   * we should warn them.
+   */
+  if(config->proxyanyauth || (authbits > 1)) {
+    warnf(global,
+          "Using --anyauth or --proxy-anyauth with upload from stdin"
+          " involves a big risk of it not working. Use a temporary"
+          " file or a fixed auth type instead");
+  }
+
+  DEBUGASSERT(per->infdopen == FALSE);
+  DEBUGASSERT(per->infd == STDIN_FILENO);
+
+  CURL_SET_BINMODE(stdin);
+  if(!strcmp(per->uploadfile, ".")) {
+    if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
+      warnf(global,
+            "fcntl failed on fd=%d: %s", per->infd, strerror(errno));
+  }
+}
+
 /* create the next (singular) transfer */
 static CURLcode single_transfer(struct GlobalConfig *global,
                                 struct OperationConfig *config,
@@ -931,73 +1208,16 @@ static CURLcode single_transfer(struct GlobalConfig *global,
 
       /* --etag-compare */
       if(config->etag_compare_file) {
-        char *etag_from_file = NULL;
-        char *header = NULL;
-        ParameterError pe;
-
-        /* open file for reading: */
-        FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
-        if(!file)
-          warnf(global, "Failed to open %s: %s", config->etag_compare_file,
-                strerror(errno));
-
-        if((PARAM_OK == file2string(&etag_from_file, file)) &&
-           etag_from_file) {
-          header = aprintf("If-None-Match: %s", etag_from_file);
-          tool_safefree(etag_from_file);
-        }
-        else
-          header = aprintf("If-None-Match: \"\"");
-
-        if(!header) {
-          if(file)
-            fclose(file);
-          errorf(global,
-                 "Failed to allocate memory for custom etag header");
-          result = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-
-        /* add Etag from file to list of custom headers */
-        pe = add2list(&config->headers, header);
-        tool_safefree(header);
-
-        if(file)
-          fclose(file);
-        if(pe != PARAM_OK) {
-          result = CURLE_OUT_OF_MEMORY;
+        result = etag_compare(global, config);
+        if(result)
           break;
-        }
       }
 
       if(config->etag_save_file) {
-        if(config->create_dirs) {
-          result = create_dir_hierarchy(config->etag_save_file, global);
-          if(result)
-            break;
-        }
-
-        /* open file for output: */
-        if(strcmp(config->etag_save_file, "-")) {
-          FILE *newfile = fopen(config->etag_save_file, "ab");
-          if(!newfile) {
-            warnf(global, "Failed creating file for saving etags: \"%s\". "
-                  "Skip this transfer", config->etag_save_file);
-            tool_safefree(state->outfiles);
-            glob_cleanup(&state->urls);
-            return CURLE_OK;
-          }
-          else {
-            etag_save->filename = config->etag_save_file;
-            etag_save->s_isreg = TRUE;
-            etag_save->fopened = TRUE;
-            etag_save->stream = newfile;
-          }
-        }
-        else {
-          /* always use binary mode for protocol header output */
-          CURL_SET_BINMODE(etag_save->stream);
-        }
+        bool badetag = FALSE;
+        result = etag_store(global, config, etag_save, &badetag);
+        if(result || badetag)
+          break;
       }
 
       curl = curl_easy_init();
@@ -1037,55 +1257,10 @@ static CURLcode single_transfer(struct GlobalConfig *global,
 
       /* Single header file for all URLs */
       if(config->headerfile) {
-        /* open file for output: */
-        if(!strcmp(config->headerfile, "%")) {
-          heads->stream = stderr;
-          /* use binary mode for protocol header output */
-          CURL_SET_BINMODE(heads->stream);
-        }
-        else if(strcmp(config->headerfile, "-")) {
-          FILE *newfile;
-
-          /*
-           * Since every transfer has its own file handle for dumping
-           * the headers, we need to open it in append mode, since transfers
-           * might finish in any order.
-           * The first transfer just clears the file.
-           *
-           * Consider placing the file handle inside the OperationConfig, so
-           * that it does not need to be opened/closed for every transfer.
-           */
-          if(config->create_dirs) {
-            result = create_dir_hierarchy(config->headerfile, global);
-            /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
-            if(result)
-              break;
-          }
-          if(!per->prev || per->prev->config != config) {
-            newfile = fopen(config->headerfile, "wb");
-            if(newfile)
-              fclose(newfile);
-          }
-          newfile = fopen(config->headerfile, "ab");
-
-          if(!newfile) {
-            errorf(global, "Failed to open %s", config->headerfile);
-            result = CURLE_WRITE_ERROR;
-            break;
-          }
-          else {
-            heads->filename = config->headerfile;
-            heads->s_isreg = TRUE;
-            heads->fopened = TRUE;
-            heads->stream = newfile;
-          }
-        }
-        else {
-          /* always use binary mode for protocol header output */
-          CURL_SET_BINMODE(heads->stream);
-        }
+        result = setup_headerfile(global, config, per, heads);
+        if(result)
+          break;
       }
-
       hdrcbdata = &per->hdrcbdata;
 
       outs = &per->outs;
@@ -1124,155 +1299,23 @@ static CURLcode single_transfer(struct GlobalConfig *global,
 
       if((urlnode->useremote ||
           (per->outfile && strcmp("-", per->outfile)))) {
-
-        /*
-         * We have specified a filename to store the result in, or we have
-         * decided we want to use the remote filename.
-         */
-
-        if(!per->outfile) {
-          /* extract the filename from the URL */
-          result = get_url_file_name(global, &per->outfile, per->url);
-          if(result) {
-            errorf(global, "Failed to extract a filename"
-                   " from the URL to use for storage");
-            break;
-          }
-        }
-        else if(state->urls) {
-          /* fill '#1' ... '#9' terms from URL pattern */
-          char *storefile = per->outfile;
-          result = glob_match_url(&per->outfile, storefile, state->urls);
-          tool_safefree(storefile);
-          if(result) {
-            /* bad globbing */
-            warnf(global, "bad output glob");
-            break;
-          }
-          if(!*per->outfile) {
-            warnf(global, "output glob produces empty string");
-            result = CURLE_WRITE_ERROR;
-            break;
-          }
-        }
-        DEBUGASSERT(per->outfile);
-
-        if(config->output_dir && *config->output_dir) {
-          char *d = aprintf("%s/%s", config->output_dir, per->outfile);
-          if(!d) {
-            result = CURLE_WRITE_ERROR;
-            break;
-          }
-          free(per->outfile);
-          per->outfile = d;
-        }
-        /* Create the directory hierarchy, if not pre-existent to a multiple
-           file output call */
-
-        if(config->create_dirs) {
-          result = create_dir_hierarchy(per->outfile, global);
-          /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
-          if(result)
-            break;
-        }
-
-        if(config->skip_existing) {
-          struct_stat fileinfo;
-          if(!stat(per->outfile, &fileinfo)) {
-            /* file is present */
-            notef(global, "skips transfer, \"%s\" exists locally",
-                  per->outfile);
-            per->skip = TRUE;
-            *skipped = TRUE;
-          }
-        }
-        if(urlnode->useremote && config->content_disposition) {
-          /* Our header callback MIGHT set the filename */
-          DEBUGASSERT(!outs->filename);
-        }
-
-        if(config->resume_from_current) {
-          /* We are told to continue from where we are now. Get the size
-             of the file as it is now and open it for append instead */
-          struct_stat fileinfo;
-          /* VMS -- Danger, the filesize is only valid for stream files */
-          if(0 == stat(per->outfile, &fileinfo))
-            /* set offset to current file size: */
-            config->resume_from = fileinfo.st_size;
-          else
-            /* let offset be 0 */
-            config->resume_from = 0;
-        }
-
-        if(config->resume_from) {
-#ifdef __VMS
-          /* open file for output, forcing VMS output format into stream
-             mode which is needed for stat() call above to always work. */
-          FILE *file = fopen(outfile, "ab",
-                             "ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
-#else
-          /* open file for output: */
-          FILE *file = fopen(per->outfile, "ab");
-#endif
-          if(!file) {
-            errorf(global, "cannot open '%s'", per->outfile);
-            result = CURLE_WRITE_ERROR;
-            break;
-          }
-          outs->fopened = TRUE;
-          outs->stream = file;
-          outs->init = config->resume_from;
-        }
-        else {
-          outs->stream = NULL; /* open when needed */
-        }
-        outs->filename = per->outfile;
-        outs->s_isreg = TRUE;
-      }
-
-      if(per->uploadfile && !stdin_upload(per->uploadfile)) {
-        /*
-         * We have specified a file to upload and it is not "-".
-         */
-        result = add_file_name_to_url(per->curl, &per->url,
-                                      per->uploadfile);
+        result = setup_outfile(global, config, per, outs, skipped);
         if(result)
           break;
       }
-      else if(per->uploadfile && stdin_upload(per->uploadfile)) {
-        /* count to see if there are more than one auth bit set
-           in the authtype field */
-        int authbits = 0;
-        int bitcheck = 0;
-        while(bitcheck < 32) {
-          if(config->authtype & (1UL << bitcheck++)) {
-            authbits++;
-            if(authbits > 1) {
-              /* more than one, we are done! */
-              break;
-            }
-          }
-        }
-
-        /*
-         * If the user has also selected --anyauth or --proxy-anyauth
-         * we should warn them.
-         */
-        if(config->proxyanyauth || (authbits > 1)) {
-          warnf(global,
-                "Using --anyauth or --proxy-anyauth with upload from stdin"
-                " involves a big risk of it not working. Use a temporary"
-                " file or a fixed auth type instead");
-        }
 
-        DEBUGASSERT(per->infdopen == FALSE);
-        DEBUGASSERT(per->infd == STDIN_FILENO);
+      if(per->uploadfile) {
 
-        CURL_SET_BINMODE(stdin);
-        if(!strcmp(per->uploadfile, ".")) {
-          if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
-            warnf(global,
-                  "fcntl failed on fd=%d: %s", per->infd, strerror(errno));
+        if(stdin_upload(per->uploadfile))
+          check_stdin_upload(global, config, per);
+        else {
+          /*
+           * We have specified a file to upload and it is not "-".
+           */
+          result = add_file_name_to_url(per->curl, &per->url,
+                                        per->uploadfile);
+          if(result)
+            break;
         }
       }