[Feature][T106]ZXW P56U09 code

Only Configure: Yes
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: No
Doc Update: No

Change-Id: I3cbd8b420271eb20c2b40ebe5c78f83059cd42f3
diff --git a/ap/lib/libcurl/curl-7.86.0/src/tool_formparse.c b/ap/lib/libcurl/curl-7.86.0/src/tool_formparse.c
new file mode 100755
index 0000000..d4fc651
--- /dev/null
+++ b/ap/lib/libcurl/curl-7.86.0/src/tool_formparse.c
@@ -0,0 +1,907 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "tool_setup.h"
+
+#include "strcase.h"
+
+#define ENABLE_CURLX_PRINTF
+/* use our own printf() functions */
+#include "curlx.h"
+
+#include "tool_cfgable.h"
+#include "tool_msgs.h"
+#include "tool_binmode.h"
+#include "tool_getparam.h"
+#include "tool_paramhlp.h"
+#include "tool_formparse.h"
+
+#include "memdebug.h" /* keep this as LAST include */
+
+/* tool_mime functions. */
+static struct tool_mime *tool_mime_new(struct tool_mime *parent,
+                                       toolmimekind kind)
+{
+  struct tool_mime *m = (struct tool_mime *) calloc(1, sizeof(*m));
+
+  if(m) {
+    m->kind = kind;
+    m->parent = parent;
+    if(parent) {
+      m->prev = parent->subparts;
+      parent->subparts = m;
+    }
+  }
+  return m;
+}
+
+static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent)
+{
+  return tool_mime_new(parent, TOOLMIME_PARTS);
+}
+
+static struct tool_mime *tool_mime_new_data(struct tool_mime *parent,
+                                            char *data)
+{
+  struct tool_mime *m = NULL;
+
+  data = strdup(data);
+  if(data) {
+    m = tool_mime_new(parent, TOOLMIME_DATA);
+    if(!m)
+      free(data);
+    else
+      m->data = data;
+  }
+  return m;
+}
+
+static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent,
+                                                const char *filename,
+                                                bool isremotefile,
+                                                CURLcode *errcode)
+{
+  CURLcode result = CURLE_OK;
+  struct tool_mime *m = NULL;
+
+  *errcode = CURLE_OUT_OF_MEMORY;
+  if(strcmp(filename, "-")) {
+    /* This is a normal file. */
+    char *filedup = strdup(filename);
+    if(filedup) {
+      m = tool_mime_new(parent, TOOLMIME_FILE);
+      if(!m)
+        free(filedup);
+      else {
+        m->data = filedup;
+        if(!isremotefile)
+          m->kind = TOOLMIME_FILEDATA;
+       *errcode = CURLE_OK;
+      }
+    }
+  }
+  else {        /* Standard input. */
+    int fd = fileno(stdin);
+    char *data = NULL;
+    curl_off_t size;
+    curl_off_t origin;
+    struct_stat sbuf;
+
+    set_binmode(stdin);
+    origin = ftell(stdin);
+    /* If stdin is a regular file, do not buffer data but read it
+       when needed. */
+    if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) &&
+#ifdef __VMS
+       sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
+#endif
+       S_ISREG(sbuf.st_mode)) {
+      size = sbuf.st_size - origin;
+      if(size < 0)
+        size = 0;
+    }
+    else {  /* Not suitable for direct use, buffer stdin data. */
+      size_t stdinsize = 0;
+
+      switch(file2memory(&data, &stdinsize, stdin)) {
+      case PARAM_NO_MEM:
+        return m;
+      case PARAM_READ_ERROR:
+        result = CURLE_READ_ERROR;
+        break;
+      default:
+        if(!stdinsize) {
+          /* Zero-length data has been freed. Re-create it. */
+          data = strdup("");
+          if(!data)
+            return m;
+        }
+        break;
+      }
+      size = curlx_uztoso(stdinsize);
+      origin = 0;
+    }
+    m = tool_mime_new(parent, TOOLMIME_STDIN);
+    if(!m)
+      Curl_safefree(data);
+    else {
+      m->data = data;
+      m->origin = origin;
+      m->size = size;
+      m->curpos = 0;
+      if(!isremotefile)
+        m->kind = TOOLMIME_STDINDATA;
+      *errcode = result;
+    }
+  }
+  return m;
+}
+
+void tool_mime_free(struct tool_mime *mime)
+{
+  if(mime) {
+    if(mime->subparts)
+      tool_mime_free(mime->subparts);
+    if(mime->prev)
+      tool_mime_free(mime->prev);
+    Curl_safefree(mime->name);
+    Curl_safefree(mime->filename);
+    Curl_safefree(mime->type);
+    Curl_safefree(mime->encoder);
+    Curl_safefree(mime->data);
+    curl_slist_free_all(mime->headers);
+    free(mime);
+  }
+}
+
+
+/* Mime part callbacks for stdin. */
+size_t tool_mime_stdin_read(char *buffer,
+                            size_t size, size_t nitems, void *arg)
+{
+  struct tool_mime *sip = (struct tool_mime *) arg;
+  curl_off_t bytesleft;
+  (void) size;  /* Always 1: ignored. */
+
+  if(sip->size >= 0) {
+    if(sip->curpos >= sip->size)
+      return 0;  /* At eof. */
+    bytesleft = sip->size - sip->curpos;
+    if(curlx_uztoso(nitems) > bytesleft)
+      nitems = curlx_sotouz(bytesleft);
+  }
+  if(nitems) {
+    if(sip->data) {
+      /* Return data from memory. */
+      memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems);
+    }
+    else {
+      /* Read from stdin. */
+      nitems = fread(buffer, 1, nitems, stdin);
+      if(ferror(stdin)) {
+        /* Show error only once. */
+        if(sip->config) {
+          warnf(sip->config, "stdin: %s\n", strerror(errno));
+          sip->config = NULL;
+        }
+        return CURL_READFUNC_ABORT;
+      }
+    }
+    sip->curpos += curlx_uztoso(nitems);
+  }
+  return nitems;
+}
+
+int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence)
+{
+  struct tool_mime *sip = (struct tool_mime *) instream;
+
+  switch(whence) {
+  case SEEK_CUR:
+    offset += sip->curpos;
+    break;
+  case SEEK_END:
+    offset += sip->size;
+    break;
+  }
+  if(offset < 0)
+    return CURL_SEEKFUNC_CANTSEEK;
+  if(!sip->data) {
+    if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
+      return CURL_SEEKFUNC_CANTSEEK;
+  }
+  sip->curpos = offset;
+  return CURL_SEEKFUNC_OK;
+}
+
+/* Translate an internal mime tree into a libcurl mime tree. */
+
+static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m,
+                               curl_mime *mime)
+{
+  CURLcode ret = CURLE_OK;
+  curl_mimepart *part = NULL;
+  curl_mime *submime = NULL;
+  const char *filename = NULL;
+
+  if(m) {
+    ret = tool2curlparts(curl, m->prev, mime);
+    if(!ret) {
+      part = curl_mime_addpart(mime);
+      if(!part)
+        ret = CURLE_OUT_OF_MEMORY;
+    }
+    if(!ret) {
+      filename = m->filename;
+      switch(m->kind) {
+      case TOOLMIME_PARTS:
+        ret = tool2curlmime(curl, m, &submime);
+        if(!ret) {
+          ret = curl_mime_subparts(part, submime);
+          if(ret)
+            curl_mime_free(submime);
+        }
+        break;
+
+      case TOOLMIME_DATA:
+        ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED);
+        break;
+
+      case TOOLMIME_FILE:
+      case TOOLMIME_FILEDATA:
+        ret = curl_mime_filedata(part, m->data);
+        if(!ret && m->kind == TOOLMIME_FILEDATA && !filename)
+          ret = curl_mime_filename(part, NULL);
+        break;
+
+      case TOOLMIME_STDIN:
+        if(!filename)
+          filename = "-";
+        /* FALLTHROUGH */
+      case TOOLMIME_STDINDATA:
+        ret = curl_mime_data_cb(part, m->size,
+                                (curl_read_callback) tool_mime_stdin_read,
+                                (curl_seek_callback) tool_mime_stdin_seek,
+                                NULL, m);
+        break;
+
+      default:
+        /* Other cases not possible in this context. */
+        break;
+      }
+    }
+    if(!ret && filename)
+      ret = curl_mime_filename(part, filename);
+    if(!ret)
+      ret = curl_mime_type(part, m->type);
+    if(!ret)
+      ret = curl_mime_headers(part, m->headers, 0);
+    if(!ret)
+      ret = curl_mime_encoder(part, m->encoder);
+    if(!ret)
+      ret = curl_mime_name(part, m->name);
+  }
+  return ret;
+}
+
+CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime)
+{
+  CURLcode ret = CURLE_OK;
+
+  *mime = curl_mime_init(curl);
+  if(!*mime)
+    ret = CURLE_OUT_OF_MEMORY;
+  else
+    ret = tool2curlparts(curl, m->subparts, *mime);
+  if(ret) {
+    curl_mime_free(*mime);
+    *mime = NULL;
+  }
+  return ret;
+}
+
+/*
+ * helper function to get a word from form param
+ * after call get_parm_word, str either point to string end
+ * or point to any of end chars.
+ */
+static char *get_param_word(struct OperationConfig *config, char **str,
+                            char **end_pos, char endchar)
+{
+  char *ptr = *str;
+  /* the first non-space char is here */
+  char *word_begin = ptr;
+  char *ptr2;
+  char *escape = NULL;
+
+  if(*ptr == '"') {
+    ++ptr;
+    while(*ptr) {
+      if(*ptr == '\\') {
+        if(ptr[1] == '\\' || ptr[1] == '"') {
+          /* remember the first escape position */
+          if(!escape)
+            escape = ptr;
+          /* skip escape of back-slash or double-quote */
+          ptr += 2;
+          continue;
+        }
+      }
+      if(*ptr == '"') {
+        bool trailing_data = FALSE;
+        *end_pos = ptr;
+        if(escape) {
+          /* has escape, we restore the unescaped string here */
+          ptr = ptr2 = escape;
+          do {
+            if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
+              ++ptr;
+            *ptr2++ = *ptr++;
+          }
+          while(ptr < *end_pos);
+          *end_pos = ptr2;
+        }
+        ++ptr;
+        while(*ptr && *ptr != ';' && *ptr != endchar) {
+          if(!ISSPACE(*ptr))
+            trailing_data = TRUE;
+          ++ptr;
+        }
+        if(trailing_data)
+          warnf(config->global, "Trailing data after quoted form parameter\n");
+        *str = ptr;
+        return word_begin + 1;
+      }
+      ++ptr;
+    }
+    /* end quote is missing, treat it as non-quoted. */
+    ptr = word_begin;
+  }
+
+  while(*ptr && *ptr != ';' && *ptr != endchar)
+    ++ptr;
+  *str = *end_pos = ptr;
+  return word_begin;
+}
+
+/* Append slist item and return -1 if failed. */
+static int slist_append(struct curl_slist **plist, const char *data)
+{
+  struct curl_slist *s = curl_slist_append(*plist, data);
+
+  if(!s)
+    return -1;
+
+  *plist = s;
+  return 0;
+}
+
+/* Read headers from a file and append to list. */
+static int read_field_headers(struct OperationConfig *config,
+                              const char *filename, FILE *fp,
+                              struct curl_slist **pheaders)
+{
+  size_t hdrlen = 0;
+  size_t pos = 0;
+  bool incomment = FALSE;
+  int lineno = 1;
+  char hdrbuf[999] = ""; /* Max. header length + 1. */
+
+  for(;;) {
+    int c = getc(fp);
+    if(c == EOF || (!pos && !ISSPACE(c))) {
+      /* Strip and flush the current header. */
+      while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
+        hdrlen--;
+      if(hdrlen) {
+        hdrbuf[hdrlen] = '\0';
+        if(slist_append(pheaders, hdrbuf)) {
+          fprintf(config->global->errors,
+                  "Out of memory for field headers!\n");
+          return -1;
+        }
+        hdrlen = 0;
+      }
+    }
+
+    switch(c) {
+    case EOF:
+      if(ferror(fp)) {
+        fprintf(config->global->errors,
+                "Header file %s read error: %s\n", filename, strerror(errno));
+        return -1;
+      }
+      return 0;    /* Done. */
+    case '\r':
+      continue;    /* Ignore. */
+    case '\n':
+      pos = 0;
+      incomment = FALSE;
+      lineno++;
+      continue;
+    case '#':
+      if(!pos)
+        incomment = TRUE;
+      break;
+    }
+
+    pos++;
+    if(!incomment) {
+      if(hdrlen == sizeof(hdrbuf) - 1) {
+        warnf(config->global, "File %s line %d: header too long (truncated)\n",
+              filename, lineno);
+        c = ' ';
+      }
+      if(hdrlen <= sizeof(hdrbuf) - 1)
+        hdrbuf[hdrlen++] = (char) c;
+    }
+  }
+  /* NOTREACHED */
+}
+
+static int get_param_part(struct OperationConfig *config, char endchar,
+                          char **str, char **pdata, char **ptype,
+                          char **pfilename, char **pencoder,
+                          struct curl_slist **pheaders)
+{
+  char *p = *str;
+  char *type = NULL;
+  char *filename = NULL;
+  char *encoder = NULL;
+  char *endpos;
+  char *tp;
+  char sep;
+  char type_major[128] = "";
+  char type_minor[128] = "";
+  char *endct = NULL;
+  struct curl_slist *headers = NULL;
+
+  if(ptype)
+    *ptype = NULL;
+  if(pfilename)
+    *pfilename = NULL;
+  if(pheaders)
+    *pheaders = NULL;
+  if(pencoder)
+    *pencoder = NULL;
+  while(ISSPACE(*p))
+    p++;
+  tp = p;
+  *pdata = get_param_word(config, &p, &endpos, endchar);
+  /* If not quoted, strip trailing spaces. */
+  if(*pdata == tp)
+    while(endpos > *pdata && ISSPACE(endpos[-1]))
+      endpos--;
+  sep = *p;
+  *endpos = '\0';
+  while(sep == ';') {
+    while(p++ && ISSPACE(*p))
+      ;
+
+    if(!endct && checkprefix("type=", p)) {
+      for(p += 5; ISSPACE(*p); p++)
+        ;
+      /* set type pointer */
+      type = p;
+
+      /* verify that this is a fine type specifier */
+      if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
+        warnf(config->global, "Illegally formatted content-type field!\n");
+        curl_slist_free_all(headers);
+        return -1; /* illegal content-type syntax! */
+      }
+
+      /* now point beyond the content-type specifier */
+      p = type + strlen(type_major) + strlen(type_minor) + 1;
+      for(endct = p; *p && *p != ';' && *p != endchar; p++)
+        if(!ISSPACE(*p))
+          endct = p + 1;
+      sep = *p;
+    }
+    else if(checkprefix("filename=", p)) {
+      if(endct) {
+        *endct = '\0';
+        endct = NULL;
+      }
+      for(p += 9; ISSPACE(*p); p++)
+        ;
+      tp = p;
+      filename = get_param_word(config, &p, &endpos, endchar);
+      /* If not quoted, strip trailing spaces. */
+      if(filename == tp)
+        while(endpos > filename && ISSPACE(endpos[-1]))
+          endpos--;
+      sep = *p;
+      *endpos = '\0';
+    }
+    else if(checkprefix("headers=", p)) {
+      if(endct) {
+        *endct = '\0';
+        endct = NULL;
+      }
+      p += 8;
+      if(*p == '@' || *p == '<') {
+        char *hdrfile;
+        FILE *fp;
+        /* Read headers from a file. */
+
+        do {
+          p++;
+        } while(ISSPACE(*p));
+        tp = p;
+        hdrfile = get_param_word(config, &p, &endpos, endchar);
+        /* If not quoted, strip trailing spaces. */
+        if(hdrfile == tp)
+          while(endpos > hdrfile && ISSPACE(endpos[-1]))
+            endpos--;
+        sep = *p;
+        *endpos = '\0';
+        fp = fopen(hdrfile, FOPEN_READTEXT);
+        if(!fp)
+          warnf(config->global, "Cannot read from %s: %s\n", hdrfile,
+                strerror(errno));
+        else {
+          int i = read_field_headers(config, hdrfile, fp, &headers);
+
+          fclose(fp);
+          if(i) {
+            curl_slist_free_all(headers);
+            return -1;
+          }
+        }
+      }
+      else {
+        char *hdr;
+
+        while(ISSPACE(*p))
+          p++;
+        tp = p;
+        hdr = get_param_word(config, &p, &endpos, endchar);
+        /* If not quoted, strip trailing spaces. */
+        if(hdr == tp)
+          while(endpos > hdr && ISSPACE(endpos[-1]))
+            endpos--;
+        sep = *p;
+        *endpos = '\0';
+        if(slist_append(&headers, hdr)) {
+          fprintf(config->global->errors, "Out of memory for field header!\n");
+          curl_slist_free_all(headers);
+          return -1;
+        }
+      }
+    }
+    else if(checkprefix("encoder=", p)) {
+      if(endct) {
+        *endct = '\0';
+        endct = NULL;
+      }
+      for(p += 8; ISSPACE(*p); p++)
+        ;
+      tp = p;
+      encoder = get_param_word(config, &p, &endpos, endchar);
+      /* If not quoted, strip trailing spaces. */
+      if(encoder == tp)
+        while(endpos > encoder && ISSPACE(endpos[-1]))
+          endpos--;
+      sep = *p;
+      *endpos = '\0';
+    }
+    else if(endct) {
+      /* This is part of content type. */
+      for(endct = p; *p && *p != ';' && *p != endchar; p++)
+        if(!ISSPACE(*p))
+          endct = p + 1;
+      sep = *p;
+    }
+    else {
+      /* unknown prefix, skip to next block */
+      char *unknown = get_param_word(config, &p, &endpos, endchar);
+
+      sep = *p;
+      *endpos = '\0';
+      if(*unknown)
+        warnf(config->global, "skip unknown form field: %s\n", unknown);
+    }
+  }
+
+  /* Terminate content type. */
+  if(endct)
+    *endct = '\0';
+
+  if(ptype)
+    *ptype = type;
+  else if(type)
+    warnf(config->global, "Field content type not allowed here: %s\n", type);
+
+  if(pfilename)
+    *pfilename = filename;
+  else if(filename)
+    warnf(config->global,
+          "Field file name not allowed here: %s\n", filename);
+
+  if(pencoder)
+    *pencoder = encoder;
+  else if(encoder)
+    warnf(config->global,
+          "Field encoder not allowed here: %s\n", encoder);
+
+  if(pheaders)
+    *pheaders = headers;
+  else if(headers) {
+    warnf(config->global,
+          "Field headers not allowed here: %s\n", headers->data);
+    curl_slist_free_all(headers);
+  }
+
+  *str = p;
+  return sep & 0xFF;
+}
+
+
+/***************************************************************************
+ *
+ * formparse()
+ *
+ * Reads a 'name=value' parameter and builds the appropriate linked list.
+ *
+ * If the value is of the form '<filename', field data is read from the
+ * given file.
+
+ * Specify files to upload with 'name=@filename', or 'name=@"filename"'
+ * in case the filename contain ',' or ';'. Supports specified
+ * given Content-Type of the files. Such as ';type=<content-type>'.
+ *
+ * If literal_value is set, any initial '@' or '<' in the value string
+ * loses its special meaning, as does any embedded ';type='.
+ *
+ * You may specify more than one file for a single name (field). Specify
+ * multiple files by writing it like:
+ *
+ * 'name=@filename,filename2,filename3'
+ *
+ * or use double-quotes quote the filename:
+ *
+ * 'name=@"filename","filename2","filename3"'
+ *
+ * If you want content-types specified for each too, write them like:
+ *
+ * 'name=@filename;type=image/gif,filename2,filename3'
+ *
+ * If you want custom headers added for a single part, write them in a separate
+ * file and do like this:
+ *
+ * 'name=foo;headers=@headerfile' or why not
+ * 'name=@filemame;headers=@headerfile'
+ *
+ * To upload a file, but to fake the file name that will be included in the
+ * formpost, do like this:
+ *
+ * 'name=@filename;filename=/dev/null' or quote the faked filename like:
+ * 'name=@filename;filename="play, play, and play.txt"'
+ *
+ * If filename/path contains ',' or ';', it must be quoted by double-quotes,
+ * else curl will fail to figure out the correct filename. if the filename
+ * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
+ *
+ ***************************************************************************/
+
+#define SET_TOOL_MIME_PTR(m, field)                                     \
+  do {                                                                  \
+    if(field) {                                                         \
+      (m)->field = strdup(field);                                       \
+      if(!(m)->field)                                                   \
+        goto fail;                                                      \
+    }                                                                   \
+  } while(0)
+
+int formparse(struct OperationConfig *config,
+              const char *input,
+              struct tool_mime **mimeroot,
+              struct tool_mime **mimecurrent,
+              bool literal_value)
+{
+  /* input MUST be a string in the format 'name=contents' and we'll
+     build a linked list with the info */
+  char *name = NULL;
+  char *contents = NULL;
+  char *contp;
+  char *data;
+  char *type = NULL;
+  char *filename = NULL;
+  char *encoder = NULL;
+  struct curl_slist *headers = NULL;
+  struct tool_mime *part = NULL;
+  CURLcode res;
+  int err = 1;
+
+  /* Allocate the main mime structure if needed. */
+  if(!*mimecurrent) {
+    *mimeroot = tool_mime_new_parts(NULL);
+    if(!*mimeroot)
+      goto fail;
+    *mimecurrent = *mimeroot;
+  }
+
+  /* Make a copy we can overwrite. */
+  contents = strdup(input);
+  if(!contents)
+    goto fail;
+
+  /* Scan for the end of the name. */
+  contp = strchr(contents, '=');
+  if(contp) {
+    int sep = '\0';
+    if(contp > contents)
+      name = contents;
+    *contp++ = '\0';
+
+    if(*contp == '(' && !literal_value) {
+      /* Starting a multipart. */
+      sep = get_param_part(config, '\0',
+                           &contp, &data, &type, NULL, NULL, &headers);
+      if(sep < 0)
+        goto fail;
+      part = tool_mime_new_parts(*mimecurrent);
+      if(!part)
+        goto fail;
+      *mimecurrent = part;
+      part->headers = headers;
+      headers = NULL;
+      SET_TOOL_MIME_PTR(part, type);
+    }
+    else if(!name && !strcmp(contp, ")") && !literal_value) {
+      /* Ending a multipart. */
+      if(*mimecurrent == *mimeroot) {
+        warnf(config->global, "no multipart to terminate!\n");
+        goto fail;
+      }
+      *mimecurrent = (*mimecurrent)->parent;
+    }
+    else if('@' == contp[0] && !literal_value) {
+
+      /* we use the @-letter to indicate file name(s) */
+
+      struct tool_mime *subparts = NULL;
+
+      do {
+        /* since this was a file, it may have a content-type specifier
+           at the end too, or a filename. Or both. */
+        ++contp;
+        sep = get_param_part(config, ',', &contp,
+                             &data, &type, &filename, &encoder, &headers);
+        if(sep < 0) {
+          goto fail;
+        }
+
+        /* now contp point to comma or string end.
+           If more files to come, make sure we have multiparts. */
+        if(!subparts) {
+          if(sep != ',')    /* If there is a single file. */
+            subparts = *mimecurrent;
+          else {
+            subparts = tool_mime_new_parts(*mimecurrent);
+            if(!subparts)
+              goto fail;
+          }
+        }
+
+        /* Store that file in a part. */
+        part = tool_mime_new_filedata(subparts, data, TRUE, &res);
+        if(!part)
+          goto fail;
+        part->headers = headers;
+        headers = NULL;
+        part->config = config->global;
+        if(res == CURLE_READ_ERROR) {
+            /* An error occurred while reading stdin: if read has started,
+               issue the error now. Else, delay it until processed by
+               libcurl. */
+          if(part->size > 0) {
+            warnf(config->global,
+                  "error while reading standard input\n");
+            goto fail;
+          }
+          Curl_safefree(part->data);
+          part->data = NULL;
+          part->size = -1;
+          res = CURLE_OK;
+        }
+        SET_TOOL_MIME_PTR(part, filename);
+        SET_TOOL_MIME_PTR(part, type);
+        SET_TOOL_MIME_PTR(part, encoder);
+
+        /* *contp could be '\0', so we just check with the delimiter */
+      } while(sep); /* loop if there's another file name */
+      part = (*mimecurrent)->subparts;  /* Set name on group. */
+    }
+    else {
+      if(*contp == '<' && !literal_value) {
+        ++contp;
+        sep = get_param_part(config, '\0', &contp,
+                             &data, &type, NULL, &encoder, &headers);
+        if(sep < 0)
+          goto fail;
+
+        part = tool_mime_new_filedata(*mimecurrent, data, FALSE,
+                                      &res);
+        if(!part)
+          goto fail;
+        part->headers = headers;
+        headers = NULL;
+        part->config = config->global;
+        if(res == CURLE_READ_ERROR) {
+            /* An error occurred while reading stdin: if read has started,
+               issue the error now. Else, delay it until processed by
+               libcurl. */
+          if(part->size > 0) {
+            warnf(config->global,
+                  "error while reading standard input\n");
+            goto fail;
+          }
+          Curl_safefree(part->data);
+          part->data = NULL;
+          part->size = -1;
+          res = CURLE_OK;
+        }
+      }
+      else {
+        if(literal_value)
+          data = contp;
+        else {
+          sep = get_param_part(config, '\0', &contp,
+                               &data, &type, &filename, &encoder, &headers);
+          if(sep < 0)
+            goto fail;
+        }
+
+        part = tool_mime_new_data(*mimecurrent, data);
+        if(!part)
+          goto fail;
+        part->headers = headers;
+        headers = NULL;
+      }
+
+      SET_TOOL_MIME_PTR(part, filename);
+      SET_TOOL_MIME_PTR(part, type);
+      SET_TOOL_MIME_PTR(part, encoder);
+
+      if(sep) {
+        *contp = (char) sep;
+        warnf(config->global,
+              "garbage at end of field specification: %s\n", contp);
+      }
+    }
+
+    /* Set part name. */
+    SET_TOOL_MIME_PTR(part, name);
+  }
+  else {
+    warnf(config->global, "Illegally formatted input field!\n");
+    goto fail;
+  }
+  err = 0;
+  fail:
+  Curl_safefree(contents);
+  curl_slist_free_all(headers);
+  return err;
+}