]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_parsecfg: refactor parseconfig()
authorDaniel Stenberg <daniel@haxx.se>
Wed, 3 Jun 2026 07:43:41 +0000 (09:43 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 3 Jun 2026 08:40:01 +0000 (10:40 +0200)
- introduce helper functions
- remove #ifdef'ed debug code

Closes #21855

src/tool_parsecfg.c

index f5910f404e77a43a7f19f862e298e3cce0031e85..173378d3acd08c44b04cee4599c797668e381f04 100644 (file)
@@ -77,15 +77,18 @@ static int unslashquote(const char *line, struct dynbuf *param)
   return 0; /* ok */
 }
 
-/* return 0 on everything-is-fine, and non-zero otherwise */
-ParameterError parseconfig(const char *filename, int max_recursive,
-                           char **resolved)
+/*
+ * Open the config file. When filename is NULL, tries to find .curlrc in the
+ * home directory (and on Windows, in the executable directory). Updates
+ * *namep to the effective filename and *pathalloc to any allocated path
+ * that must be freed by the caller. Returns the opened FILE or NULL.
+ */
+static FILE *open_config_file(const char *filename,
+                              const char **namep,
+                              char **pathalloc)
 {
   FILE *file = NULL;
-  bool usedarg = FALSE;
-  ParameterError err = PARAM_OK;
-  struct OperationConfig *config = global->last;
-  char *pathalloc = NULL;
+  *pathalloc = NULL;
 
   if(!filename) {
     /* NULL means load .curlrc from homedir! */
@@ -94,9 +97,9 @@ ParameterError parseconfig(const char *filename, int max_recursive,
       file = curlx_fopen(curlrc, FOPEN_READTEXT);
       if(!file) {
         curlx_free(curlrc);
-        return PARAM_READ_ERROR;
+        return NULL;
       }
-      filename = pathalloc = curlrc;
+      *namep = *pathalloc = curlrc;
     }
 #ifdef _WIN32
     else {
@@ -107,16 +110,155 @@ ParameterError parseconfig(const char *filename, int max_recursive,
         file = tool_execpath("_curlrc", &fullp);
       if(file)
         /* this is the filename we read from */
-        filename = fullp;
+        *namep = fullp;
     }
 #endif
   }
   else {
     if(strcmp(filename, "-"))
       file = curlx_fopen(filename, FOPEN_READTEXT);
-    else
+    else {
       file = stdin;
+      *namep = "<stdin>";
+    }
   }
+  return file;
+}
+
+/*
+ * Extract the parameter value from a config line. The line pointer should
+ * be positioned after the option keyword has been null-terminated.
+ * Skips separators and whitespace, then handles quoted and unquoted
+ * parameter values. Sets *param_out to the parameter string; unquoted empty
+ * values set it to NULL, while quoted empty values become an empty string.
+ */
+static ParameterError extract_param(char *line,
+                                    bool dashed_option,
+                                    struct dynbuf *pbuf,
+                                    const char *filename,
+                                    int lineno,
+                                    const char *option,
+                                    char **param_out)
+{
+  /* pass spaces and separator(s) */
+  while(ISBLANK(*line) || ISSEP(*line, dashed_option))
+    line++;
+
+  /* the parameter starts here (unless quoted) */
+  if(*line == '\"') {
+    /* quoted parameter, do the quote dance */
+    int rc = unslashquote(++line, pbuf);
+    if(rc)
+      return PARAM_BAD_USE;
+    *param_out = curlx_dyn_len(pbuf) ? curlx_dyn_ptr(pbuf) : CURL_UNCONST("");
+  }
+  else {
+    if(*line == '\'') {
+      warnf("%s:%d Option '%s' uses argument with leading single quote. "
+            "It is probably a mistake. Consider double quotes.",
+            filename, lineno, option);
+    }
+    *param_out = line; /* parameter starts here */
+    while(*line && !ISSPACE(*line)) /* stop also on CRLF */
+      line++;
+
+    if(*line) {
+      *line = '\0'; /* null-terminate */
+
+      /* to detect mistakes better, see if there is data following */
+      line++;
+      /* pass all spaces */
+      while(ISBLANK(*line))
+        line++;
+
+      switch(*line) {
+      case '\0':
+      case '\r':
+      case '\n':
+      case '#': /* comment */
+        break;
+      default:
+        warnf("%s:%d Option '%s' uses argument with unquoted whitespace. "
+              "This may cause side-effects. Consider double quotes.",
+              filename, lineno, option);
+      }
+    }
+    if(!**param_out)
+      /* do this so getparameter can check for required parameters.
+         Otherwise it always thinks there is a parameter. */
+      *param_out = NULL;
+  }
+  return PARAM_OK;
+}
+
+/*
+ * Process the result from getparameter. Handles PARAM_NEXT_OPERATION
+ * by allocating a new config, and reports errors for other non-OK results.
+ * Updates *configp if a new operation config is allocated.
+ * Returns PARAM_OK if processing should continue, or an error code.
+ */
+static ParameterError
+process_config_result(ParameterError res,
+                      struct OperationConfig **configp,
+                      const char *param,
+                      bool usedarg,
+                      const char *filename,
+                      int lineno,
+                      const char *option)
+{
+  if(!res && param && *param && !usedarg)
+    /* we passed in a parameter that was not used! */
+    res = PARAM_GOT_EXTRA_PARAMETER;
+
+  if(res == PARAM_NEXT_OPERATION) {
+    struct OperationConfig *config = *configp;
+    if(config->url_list && config->url_list->url) {
+      /* Allocate the next config */
+      config->next = config_alloc();
+      if(config->next) {
+        /* Update the last operation pointer */
+        global->last = config->next;
+
+        /* Move onto the new config */
+        config->next->prev = config;
+        *configp = config->next;
+      }
+      else
+        res = PARAM_NO_MEM;
+    }
+  }
+
+  if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) {
+    const char *display = filename;
+    /* the help request is not really an error */
+    if(!strcmp(filename, "-"))
+      display = "<stdin>";
+    if(res != PARAM_HELP_REQUESTED &&
+       res != PARAM_MANUAL_REQUESTED &&
+       res != PARAM_VERSION_INFO_REQUESTED &&
+       res != PARAM_ENGINES_REQUESTED &&
+       res != PARAM_CA_EMBED_REQUESTED) {
+      const char *reason = param2text(res);
+      errorf("%s:%d config file option '%s' %s",
+             display, lineno, option, reason);
+      if(res == PARAM_OPTION_UNKNOWN)
+        res = PARAM_CONFIG_OPTION_UNKNOWN;
+      return res;
+    }
+  }
+  return PARAM_OK;
+}
+
+ParameterError parseconfig(const char *filename, int max_recursive,
+                           char **resolved)
+{
+  FILE *file = NULL;
+  bool usedarg = FALSE;
+  ParameterError err = PARAM_OK;
+  struct OperationConfig *config = global->last;
+  char *pathalloc = NULL;
+
+  file = open_config_file(filename, &filename, &pathalloc);
 
   if(file) {
     char *line;
@@ -151,109 +293,20 @@ ParameterError parseconfig(const char *filename, int max_recursive,
       /* ... and has ended here */
 
       if(*line)
-        *line++ = '\0'; /* null-terminate, we have a local copy of the data */
-
-#ifdef DEBUG_CONFIG
-      curl_mfprintf(tool_stderr, "GOT: %s\n", option);
-#endif
-
-      /* pass spaces and separator(s) */
-      while(ISBLANK(*line) || ISSEP(*line, dashed_option))
-        line++;
-
-      /* the parameter starts here (unless quoted) */
-      if(*line == '\"') {
-        /* quoted parameter, do the quote dance */
-        int rc = unslashquote(++line, &pbuf);
-        if(rc) {
-          err = PARAM_BAD_USE;
-          break;
-        }
-        param = curlx_dyn_len(&pbuf) ? curlx_dyn_ptr(&pbuf) : CURL_UNCONST("");
-      }
-      else {
-        if(*line == '\'') {
-          warnf("%s:%d Option '%s' uses argument with leading single quote. "
-                "It is probably a mistake. Consider double quotes.",
-                filename, lineno, option);
-        }
-        param = line; /* parameter starts here */
-        while(*line && !ISSPACE(*line)) /* stop also on CRLF */
-          line++;
-
-        if(*line) {
-          *line = '\0'; /* null-terminate */
+        *line++ = '\0'; /* null-terminate, we have a local copy */
 
-          /* to detect mistakes better, see if there is data following */
-          line++;
-          /* pass all spaces */
-          while(ISBLANK(*line))
-            line++;
-
-          switch(*line) {
-          case '\0':
-          case '\r':
-          case '\n':
-          case '#': /* comment */
-            break;
-          default:
-            warnf("%s:%d Option '%s' uses argument with unquoted whitespace. "
-                  "This may cause side-effects. Consider double quotes.",
-                  filename, lineno, option);
-          }
-        }
-        if(!*param)
-          /* do this so getparameter can check for required parameters.
-             Otherwise it always thinks there is a parameter. */
-          param = NULL;
-      }
+      /* if there is a parameter for this option, extract it */
+      err = extract_param(line, dashed_option, &pbuf, filename, lineno,
+                          option, &param);
+      if(err)
+        break;
 
-#ifdef DEBUG_CONFIG
-      curl_mfprintf(tool_stderr, "PARAM: \"%s\"\n",
-                    (param ? param : "(null)"));
-#endif
       res = getparameter(option, param, &usedarg, config, max_recursive);
-      config = global->last;
 
-      if(!res && param && *param && !usedarg)
-        /* we passed in a parameter that was not used! */
-        res = PARAM_GOT_EXTRA_PARAMETER;
-
-      if(res == PARAM_NEXT_OPERATION) {
-        if(config->url_list && config->url_list->url) {
-          /* Allocate the next config */
-          config->next = config_alloc();
-          if(config->next) {
-            /* Update the last operation pointer */
-            global->last = config->next;
-
-            /* Move onto the new config */
-            config->next->prev = config;
-            config = config->next;
-          }
-          else
-            res = PARAM_NO_MEM;
-        }
-      }
+      config = global->last;
 
-      if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) {
-        /* the help request is not really an error */
-        if(!strcmp(filename, "-")) {
-          filename = "<stdin>";
-        }
-        if(res != PARAM_HELP_REQUESTED &&
-           res != PARAM_MANUAL_REQUESTED &&
-           res != PARAM_VERSION_INFO_REQUESTED &&
-           res != PARAM_ENGINES_REQUESTED &&
-           res != PARAM_CA_EMBED_REQUESTED) {
-          const char *reason = param2text(res);
-          errorf("%s:%d config file option '%s' %s",
-                 filename, lineno, option, reason);
-          if(res == PARAM_OPTION_UNKNOWN)
-            res = PARAM_CONFIG_OPTION_UNKNOWN;
-          err = res;
-        }
-      }
+      err = process_config_result(res, &config, param, usedarg, filename,
+                                  lineno, option);
     }
     curlx_dyn_free(&buf);
     curlx_dyn_free(&pbuf);