113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci#include "tool_setup.h"
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci#if defined(_WIN32) || defined(MSDOS)
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
2913498266Sopenharmony_ci#  include <libgen.h>
3013498266Sopenharmony_ci#endif
3113498266Sopenharmony_ci
3213498266Sopenharmony_ci#ifdef _WIN32
3313498266Sopenharmony_ci#  include <stdlib.h>
3413498266Sopenharmony_ci#  include <tlhelp32.h>
3513498266Sopenharmony_ci#  include "tool_cfgable.h"
3613498266Sopenharmony_ci#  include "tool_libinfo.h"
3713498266Sopenharmony_ci#endif
3813498266Sopenharmony_ci
3913498266Sopenharmony_ci#include "tool_bname.h"
4013498266Sopenharmony_ci#include "tool_doswin.h"
4113498266Sopenharmony_ci
4213498266Sopenharmony_ci#include "curlx.h"
4313498266Sopenharmony_ci#include "memdebug.h" /* keep this as LAST include */
4413498266Sopenharmony_ci
4513498266Sopenharmony_ci#ifdef _WIN32
4613498266Sopenharmony_ci#  undef  PATH_MAX
4713498266Sopenharmony_ci#  define PATH_MAX MAX_PATH
4813498266Sopenharmony_ci#endif
4913498266Sopenharmony_ci
5013498266Sopenharmony_ci#ifndef S_ISCHR
5113498266Sopenharmony_ci#  ifdef S_IFCHR
5213498266Sopenharmony_ci#    define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
5313498266Sopenharmony_ci#  else
5413498266Sopenharmony_ci#    define S_ISCHR(m) (0) /* cannot tell if file is a device */
5513498266Sopenharmony_ci#  endif
5613498266Sopenharmony_ci#endif
5713498266Sopenharmony_ci
5813498266Sopenharmony_ci#ifdef _WIN32
5913498266Sopenharmony_ci#  define _use_lfn(f) (1)   /* long file names always available */
6013498266Sopenharmony_ci#elif !defined(__DJGPP__) || (__DJGPP__ < 2)  /* DJGPP 2.0 has _use_lfn() */
6113498266Sopenharmony_ci#  define _use_lfn(f) (0)  /* long file names never available */
6213498266Sopenharmony_ci#elif defined(__DJGPP__)
6313498266Sopenharmony_ci#  include <fcntl.h>                /* _use_lfn(f) prototype */
6413498266Sopenharmony_ci#endif
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci#ifndef UNITTESTS
6713498266Sopenharmony_cistatic SANITIZEcode truncate_dryrun(const char *path,
6813498266Sopenharmony_ci                                    const size_t truncate_pos);
6913498266Sopenharmony_ci#ifdef MSDOS
7013498266Sopenharmony_cistatic SANITIZEcode msdosify(char **const sanitized, const char *file_name,
7113498266Sopenharmony_ci                             int flags);
7213498266Sopenharmony_ci#endif
7313498266Sopenharmony_cistatic SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
7413498266Sopenharmony_ci                                                       const char *file_name,
7513498266Sopenharmony_ci                                                       int flags);
7613498266Sopenharmony_ci#endif /* !UNITTESTS (static declarations used if no unit tests) */
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci
7913498266Sopenharmony_ci/*
8013498266Sopenharmony_ciSanitize a file or path name.
8113498266Sopenharmony_ci
8213498266Sopenharmony_ciAll banned characters are replaced by underscores, for example:
8313498266Sopenharmony_cif?*foo => f__foo
8413498266Sopenharmony_cif:foo::$DATA => f_foo__$DATA
8513498266Sopenharmony_cif:\foo:bar => f__foo_bar
8613498266Sopenharmony_cif:\foo:bar => f:\foo:bar   (flag SANITIZE_ALLOW_PATH)
8713498266Sopenharmony_ci
8813498266Sopenharmony_ciThis function was implemented according to the guidelines in 'Naming Files,
8913498266Sopenharmony_ciPaths, and Namespaces' section 'Naming Conventions'.
9013498266Sopenharmony_cihttps://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
9113498266Sopenharmony_ci
9213498266Sopenharmony_ciFlags
9313498266Sopenharmony_ci-----
9413498266Sopenharmony_ciSANITIZE_ALLOW_COLONS:     Allow colons.
9513498266Sopenharmony_ciWithout this flag colons are sanitized.
9613498266Sopenharmony_ci
9713498266Sopenharmony_ciSANITIZE_ALLOW_PATH:       Allow path separators and colons.
9813498266Sopenharmony_ciWithout this flag path separators and colons are sanitized.
9913498266Sopenharmony_ci
10013498266Sopenharmony_ciSANITIZE_ALLOW_RESERVED:   Allow reserved device names.
10113498266Sopenharmony_ciWithout this flag a reserved device name is renamed (COM1 => _COM1) unless it's
10213498266Sopenharmony_ciin a UNC prefixed path.
10313498266Sopenharmony_ci
10413498266Sopenharmony_ciSANITIZE_ALLOW_TRUNCATE:   Allow truncating a long filename.
10513498266Sopenharmony_ciWithout this flag if the sanitized filename or path will be too long an error
10613498266Sopenharmony_cioccurs. With this flag the filename --and not any other parts of the path-- may
10713498266Sopenharmony_cibe truncated to at least a single character. A filename followed by an
10813498266Sopenharmony_cialternate data stream (ADS) cannot be truncated in any case.
10913498266Sopenharmony_ci
11013498266Sopenharmony_ciSuccess: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
11113498266Sopenharmony_ciFailure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
11213498266Sopenharmony_ci*/
11313498266Sopenharmony_ciSANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
11413498266Sopenharmony_ci                                int flags)
11513498266Sopenharmony_ci{
11613498266Sopenharmony_ci  char *p, *target;
11713498266Sopenharmony_ci  size_t len;
11813498266Sopenharmony_ci  SANITIZEcode sc;
11913498266Sopenharmony_ci  size_t max_sanitized_len;
12013498266Sopenharmony_ci
12113498266Sopenharmony_ci  if(!sanitized)
12213498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
12313498266Sopenharmony_ci
12413498266Sopenharmony_ci  *sanitized = NULL;
12513498266Sopenharmony_ci
12613498266Sopenharmony_ci  if(!file_name)
12713498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
12813498266Sopenharmony_ci
12913498266Sopenharmony_ci  if((flags & SANITIZE_ALLOW_PATH)) {
13013498266Sopenharmony_ci#ifndef MSDOS
13113498266Sopenharmony_ci    if(file_name[0] == '\\' && file_name[1] == '\\')
13213498266Sopenharmony_ci      /* UNC prefixed path \\ (eg \\?\C:\foo) */
13313498266Sopenharmony_ci      max_sanitized_len = 32767-1;
13413498266Sopenharmony_ci    else
13513498266Sopenharmony_ci#endif
13613498266Sopenharmony_ci      max_sanitized_len = PATH_MAX-1;
13713498266Sopenharmony_ci  }
13813498266Sopenharmony_ci  else
13913498266Sopenharmony_ci    /* The maximum length of a filename.
14013498266Sopenharmony_ci       FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and
14113498266Sopenharmony_ci       does not discount the path information therefore we shouldn't use it. */
14213498266Sopenharmony_ci    max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
14313498266Sopenharmony_ci
14413498266Sopenharmony_ci  len = strlen(file_name);
14513498266Sopenharmony_ci  if(len > max_sanitized_len) {
14613498266Sopenharmony_ci    if(!(flags & SANITIZE_ALLOW_TRUNCATE) ||
14713498266Sopenharmony_ci       truncate_dryrun(file_name, max_sanitized_len))
14813498266Sopenharmony_ci      return SANITIZE_ERR_INVALID_PATH;
14913498266Sopenharmony_ci
15013498266Sopenharmony_ci    len = max_sanitized_len;
15113498266Sopenharmony_ci  }
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci  target = malloc(len + 1);
15413498266Sopenharmony_ci  if(!target)
15513498266Sopenharmony_ci    return SANITIZE_ERR_OUT_OF_MEMORY;
15613498266Sopenharmony_ci
15713498266Sopenharmony_ci  strncpy(target, file_name, len);
15813498266Sopenharmony_ci  target[len] = '\0';
15913498266Sopenharmony_ci
16013498266Sopenharmony_ci#ifndef MSDOS
16113498266Sopenharmony_ci  if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
16213498266Sopenharmony_ci    /* Skip the literal path prefix \\?\ */
16313498266Sopenharmony_ci    p = target + 4;
16413498266Sopenharmony_ci  else
16513498266Sopenharmony_ci#endif
16613498266Sopenharmony_ci    p = target;
16713498266Sopenharmony_ci
16813498266Sopenharmony_ci  /* replace control characters and other banned characters */
16913498266Sopenharmony_ci  for(; *p; ++p) {
17013498266Sopenharmony_ci    const char *banned;
17113498266Sopenharmony_ci
17213498266Sopenharmony_ci    if((1 <= *p && *p <= 31) ||
17313498266Sopenharmony_ci       (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
17413498266Sopenharmony_ci       (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
17513498266Sopenharmony_ci      *p = '_';
17613498266Sopenharmony_ci      continue;
17713498266Sopenharmony_ci    }
17813498266Sopenharmony_ci
17913498266Sopenharmony_ci    for(banned = "|<>\"?*"; *banned; ++banned) {
18013498266Sopenharmony_ci      if(*p == *banned) {
18113498266Sopenharmony_ci        *p = '_';
18213498266Sopenharmony_ci        break;
18313498266Sopenharmony_ci      }
18413498266Sopenharmony_ci    }
18513498266Sopenharmony_ci  }
18613498266Sopenharmony_ci
18713498266Sopenharmony_ci  /* remove trailing spaces and periods if not allowing paths */
18813498266Sopenharmony_ci  if(!(flags & SANITIZE_ALLOW_PATH) && len) {
18913498266Sopenharmony_ci    char *clip = NULL;
19013498266Sopenharmony_ci
19113498266Sopenharmony_ci    p = &target[len];
19213498266Sopenharmony_ci    do {
19313498266Sopenharmony_ci      --p;
19413498266Sopenharmony_ci      if(*p != ' ' && *p != '.')
19513498266Sopenharmony_ci        break;
19613498266Sopenharmony_ci      clip = p;
19713498266Sopenharmony_ci    } while(p != target);
19813498266Sopenharmony_ci
19913498266Sopenharmony_ci    if(clip) {
20013498266Sopenharmony_ci      *clip = '\0';
20113498266Sopenharmony_ci      len = clip - target;
20213498266Sopenharmony_ci    }
20313498266Sopenharmony_ci  }
20413498266Sopenharmony_ci
20513498266Sopenharmony_ci#ifdef MSDOS
20613498266Sopenharmony_ci  sc = msdosify(&p, target, flags);
20713498266Sopenharmony_ci  free(target);
20813498266Sopenharmony_ci  if(sc)
20913498266Sopenharmony_ci    return sc;
21013498266Sopenharmony_ci  target = p;
21113498266Sopenharmony_ci  len = strlen(target);
21213498266Sopenharmony_ci
21313498266Sopenharmony_ci  if(len > max_sanitized_len) {
21413498266Sopenharmony_ci    free(target);
21513498266Sopenharmony_ci    return SANITIZE_ERR_INVALID_PATH;
21613498266Sopenharmony_ci  }
21713498266Sopenharmony_ci#endif
21813498266Sopenharmony_ci
21913498266Sopenharmony_ci  if(!(flags & SANITIZE_ALLOW_RESERVED)) {
22013498266Sopenharmony_ci    sc = rename_if_reserved_dos_device_name(&p, target, flags);
22113498266Sopenharmony_ci    free(target);
22213498266Sopenharmony_ci    if(sc)
22313498266Sopenharmony_ci      return sc;
22413498266Sopenharmony_ci    target = p;
22513498266Sopenharmony_ci    len = strlen(target);
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci    if(len > max_sanitized_len) {
22813498266Sopenharmony_ci      free(target);
22913498266Sopenharmony_ci      return SANITIZE_ERR_INVALID_PATH;
23013498266Sopenharmony_ci    }
23113498266Sopenharmony_ci  }
23213498266Sopenharmony_ci
23313498266Sopenharmony_ci  *sanitized = target;
23413498266Sopenharmony_ci  return SANITIZE_ERR_OK;
23513498266Sopenharmony_ci}
23613498266Sopenharmony_ci
23713498266Sopenharmony_ci
23813498266Sopenharmony_ci/*
23913498266Sopenharmony_ciTest if truncating a path to a file will leave at least a single character in
24013498266Sopenharmony_cithe filename. Filenames suffixed by an alternate data stream can't be
24113498266Sopenharmony_citruncated. This performs a dry run, nothing is modified.
24213498266Sopenharmony_ci
24313498266Sopenharmony_ciGood truncate_pos 9:    C:\foo\bar  =>  C:\foo\ba
24413498266Sopenharmony_ciGood truncate_pos 6:    C:\foo      =>  C:\foo
24513498266Sopenharmony_ciGood truncate_pos 5:    C:\foo      =>  C:\fo
24613498266Sopenharmony_ciBad* truncate_pos 5:    C:foo       =>  C:foo
24713498266Sopenharmony_ciBad truncate_pos 5:     C:\foo:ads  =>  C:\fo
24813498266Sopenharmony_ciBad truncate_pos 9:     C:\foo:ads  =>  C:\foo:ad
24913498266Sopenharmony_ciBad truncate_pos 5:     C:\foo\bar  =>  C:\fo
25013498266Sopenharmony_ciBad truncate_pos 5:     C:\foo\     =>  C:\fo
25113498266Sopenharmony_ciBad truncate_pos 7:     C:\foo\     =>  C:\foo\
25213498266Sopenharmony_ciError truncate_pos 7:   C:\foo      =>  (pos out of range)
25313498266Sopenharmony_ciBad truncate_pos 1:     C:\foo\     =>  C
25413498266Sopenharmony_ci
25513498266Sopenharmony_ci* C:foo is ambiguous, C could end up being a drive or file therefore something
25613498266Sopenharmony_ci  like C:superlongfilename can't be truncated.
25713498266Sopenharmony_ci
25813498266Sopenharmony_ciReturns
25913498266Sopenharmony_ciSANITIZE_ERR_OK: Good -- 'path' can be truncated
26013498266Sopenharmony_ciSANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
26113498266Sopenharmony_ci!= SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
26213498266Sopenharmony_ci*/
26313498266Sopenharmony_ciSANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
26413498266Sopenharmony_ci{
26513498266Sopenharmony_ci  size_t len;
26613498266Sopenharmony_ci
26713498266Sopenharmony_ci  if(!path)
26813498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
26913498266Sopenharmony_ci
27013498266Sopenharmony_ci  len = strlen(path);
27113498266Sopenharmony_ci
27213498266Sopenharmony_ci  if(truncate_pos > len)
27313498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
27413498266Sopenharmony_ci
27513498266Sopenharmony_ci  if(!len || !truncate_pos)
27613498266Sopenharmony_ci    return SANITIZE_ERR_INVALID_PATH;
27713498266Sopenharmony_ci
27813498266Sopenharmony_ci  if(strpbrk(&path[truncate_pos - 1], "\\/:"))
27913498266Sopenharmony_ci    return SANITIZE_ERR_INVALID_PATH;
28013498266Sopenharmony_ci
28113498266Sopenharmony_ci  /* C:\foo can be truncated but C:\foo:ads can't */
28213498266Sopenharmony_ci  if(truncate_pos > 1) {
28313498266Sopenharmony_ci    const char *p = &path[truncate_pos - 1];
28413498266Sopenharmony_ci    do {
28513498266Sopenharmony_ci      --p;
28613498266Sopenharmony_ci      if(*p == ':')
28713498266Sopenharmony_ci        return SANITIZE_ERR_INVALID_PATH;
28813498266Sopenharmony_ci    } while(p != path && *p != '\\' && *p != '/');
28913498266Sopenharmony_ci  }
29013498266Sopenharmony_ci
29113498266Sopenharmony_ci  return SANITIZE_ERR_OK;
29213498266Sopenharmony_ci}
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function
29513498266Sopenharmony_ci * were taken with modification from the DJGPP port of tar 1.12. They use
29613498266Sopenharmony_ci * algorithms originally from DJTAR.
29713498266Sopenharmony_ci */
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci/*
30013498266Sopenharmony_ciExtra sanitization MSDOS for file_name.
30113498266Sopenharmony_ci
30213498266Sopenharmony_ciThis is a supporting function for sanitize_file_name.
30313498266Sopenharmony_ci
30413498266Sopenharmony_ciWarning: This is an MSDOS legacy function and was purposely written in a way
30513498266Sopenharmony_cithat some path information may pass through. For example drive letter names
30613498266Sopenharmony_ci(C:, D:, etc) are allowed to pass through. For sanitizing a filename use
30713498266Sopenharmony_cisanitize_file_name.
30813498266Sopenharmony_ci
30913498266Sopenharmony_ciSuccess: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
31013498266Sopenharmony_ciFailure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
31113498266Sopenharmony_ci*/
31213498266Sopenharmony_ci#if defined(MSDOS) || defined(UNITTESTS)
31313498266Sopenharmony_ciSANITIZEcode msdosify(char **const sanitized, const char *file_name,
31413498266Sopenharmony_ci                      int flags)
31513498266Sopenharmony_ci{
31613498266Sopenharmony_ci  char dos_name[PATH_MAX];
31713498266Sopenharmony_ci  static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
31813498266Sopenharmony_ci    "|<>/\\\":?*"; /* illegal in DOS & W95 */
31913498266Sopenharmony_ci  static const char *illegal_chars_w95 = &illegal_chars_dos[8];
32013498266Sopenharmony_ci  int idx, dot_idx;
32113498266Sopenharmony_ci  const char *s = file_name;
32213498266Sopenharmony_ci  char *d = dos_name;
32313498266Sopenharmony_ci  const char *const dlimit = dos_name + sizeof(dos_name) - 1;
32413498266Sopenharmony_ci  const char *illegal_aliens = illegal_chars_dos;
32513498266Sopenharmony_ci  size_t len = sizeof(illegal_chars_dos) - 1;
32613498266Sopenharmony_ci
32713498266Sopenharmony_ci  if(!sanitized)
32813498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
32913498266Sopenharmony_ci
33013498266Sopenharmony_ci  *sanitized = NULL;
33113498266Sopenharmony_ci
33213498266Sopenharmony_ci  if(!file_name)
33313498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
33413498266Sopenharmony_ci
33513498266Sopenharmony_ci  if(strlen(file_name) > PATH_MAX-1 &&
33613498266Sopenharmony_ci     (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
33713498266Sopenharmony_ci      truncate_dryrun(file_name, PATH_MAX-1)))
33813498266Sopenharmony_ci    return SANITIZE_ERR_INVALID_PATH;
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  /* Support for Windows 9X VFAT systems, when available. */
34113498266Sopenharmony_ci  if(_use_lfn(file_name)) {
34213498266Sopenharmony_ci    illegal_aliens = illegal_chars_w95;
34313498266Sopenharmony_ci    len -= (illegal_chars_w95 - illegal_chars_dos);
34413498266Sopenharmony_ci  }
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci  /* Get past the drive letter, if any. */
34713498266Sopenharmony_ci  if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
34813498266Sopenharmony_ci    *d++ = *s++;
34913498266Sopenharmony_ci    *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
35013498266Sopenharmony_ci    ++d; ++s;
35113498266Sopenharmony_ci  }
35213498266Sopenharmony_ci
35313498266Sopenharmony_ci  for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
35413498266Sopenharmony_ci    if(memchr(illegal_aliens, *s, len)) {
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci      if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
35713498266Sopenharmony_ci        *d = ':';
35813498266Sopenharmony_ci      else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
35913498266Sopenharmony_ci        *d = *s;
36013498266Sopenharmony_ci      /* Dots are special: DOS doesn't allow them as the leading character,
36113498266Sopenharmony_ci         and a file name cannot have more than a single dot.  We leave the
36213498266Sopenharmony_ci         first non-leading dot alone, unless it comes too close to the
36313498266Sopenharmony_ci         beginning of the name: we want sh.lex.c to become sh_lex.c, not
36413498266Sopenharmony_ci         sh.lex-c.  */
36513498266Sopenharmony_ci      else if(*s == '.') {
36613498266Sopenharmony_ci        if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
36713498266Sopenharmony_ci           (s[1] == '/' || s[1] == '\\' ||
36813498266Sopenharmony_ci            (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
36913498266Sopenharmony_ci          /* Copy "./" and "../" verbatim.  */
37013498266Sopenharmony_ci          *d++ = *s++;
37113498266Sopenharmony_ci          if(d == dlimit)
37213498266Sopenharmony_ci            break;
37313498266Sopenharmony_ci          if(*s == '.') {
37413498266Sopenharmony_ci            *d++ = *s++;
37513498266Sopenharmony_ci            if(d == dlimit)
37613498266Sopenharmony_ci              break;
37713498266Sopenharmony_ci          }
37813498266Sopenharmony_ci          *d = *s;
37913498266Sopenharmony_ci        }
38013498266Sopenharmony_ci        else if(idx == 0)
38113498266Sopenharmony_ci          *d = '_';
38213498266Sopenharmony_ci        else if(dot_idx >= 0) {
38313498266Sopenharmony_ci          if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
38413498266Sopenharmony_ci            d[dot_idx - idx] = '_'; /* replace previous dot */
38513498266Sopenharmony_ci            *d = '.';
38613498266Sopenharmony_ci          }
38713498266Sopenharmony_ci          else
38813498266Sopenharmony_ci            *d = '-';
38913498266Sopenharmony_ci        }
39013498266Sopenharmony_ci        else
39113498266Sopenharmony_ci          *d = '.';
39213498266Sopenharmony_ci
39313498266Sopenharmony_ci        if(*s == '.')
39413498266Sopenharmony_ci          dot_idx = idx;
39513498266Sopenharmony_ci      }
39613498266Sopenharmony_ci      else if(*s == '+' && s[1] == '+') {
39713498266Sopenharmony_ci        if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
39813498266Sopenharmony_ci          *d++ = 'x';
39913498266Sopenharmony_ci          if(d == dlimit)
40013498266Sopenharmony_ci            break;
40113498266Sopenharmony_ci          *d   = 'x';
40213498266Sopenharmony_ci        }
40313498266Sopenharmony_ci        else {
40413498266Sopenharmony_ci          /* libg++ etc.  */
40513498266Sopenharmony_ci          if(dlimit - d < 4) {
40613498266Sopenharmony_ci            *d++ = 'x';
40713498266Sopenharmony_ci            if(d == dlimit)
40813498266Sopenharmony_ci              break;
40913498266Sopenharmony_ci            *d   = 'x';
41013498266Sopenharmony_ci          }
41113498266Sopenharmony_ci          else {
41213498266Sopenharmony_ci            memcpy(d, "plus", 4);
41313498266Sopenharmony_ci            d += 3;
41413498266Sopenharmony_ci          }
41513498266Sopenharmony_ci        }
41613498266Sopenharmony_ci        s++;
41713498266Sopenharmony_ci        idx++;
41813498266Sopenharmony_ci      }
41913498266Sopenharmony_ci      else
42013498266Sopenharmony_ci        *d = '_';
42113498266Sopenharmony_ci    }
42213498266Sopenharmony_ci    else
42313498266Sopenharmony_ci      *d = *s;
42413498266Sopenharmony_ci    if(*s == '/' || *s == '\\') {
42513498266Sopenharmony_ci      idx = 0;
42613498266Sopenharmony_ci      dot_idx = -1;
42713498266Sopenharmony_ci    }
42813498266Sopenharmony_ci    else
42913498266Sopenharmony_ci      idx++;
43013498266Sopenharmony_ci  }
43113498266Sopenharmony_ci  *d = '\0';
43213498266Sopenharmony_ci
43313498266Sopenharmony_ci  if(*s) {
43413498266Sopenharmony_ci    /* dos_name is truncated, check that truncation requirements are met,
43513498266Sopenharmony_ci       specifically truncating a filename suffixed by an alternate data stream
43613498266Sopenharmony_ci       or truncating the entire filename is not allowed. */
43713498266Sopenharmony_ci    if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") ||
43813498266Sopenharmony_ci       truncate_dryrun(dos_name, d - dos_name))
43913498266Sopenharmony_ci      return SANITIZE_ERR_INVALID_PATH;
44013498266Sopenharmony_ci  }
44113498266Sopenharmony_ci
44213498266Sopenharmony_ci  *sanitized = strdup(dos_name);
44313498266Sopenharmony_ci  return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
44413498266Sopenharmony_ci}
44513498266Sopenharmony_ci#endif /* MSDOS || UNITTESTS */
44613498266Sopenharmony_ci
44713498266Sopenharmony_ci/*
44813498266Sopenharmony_ciRename file_name if it's a reserved dos device name.
44913498266Sopenharmony_ci
45013498266Sopenharmony_ciThis is a supporting function for sanitize_file_name.
45113498266Sopenharmony_ci
45213498266Sopenharmony_ciWarning: This is an MSDOS legacy function and was purposely written in a way
45313498266Sopenharmony_cithat some path information may pass through. For example drive letter names
45413498266Sopenharmony_ci(C:, D:, etc) are allowed to pass through. For sanitizing a filename use
45513498266Sopenharmony_cisanitize_file_name.
45613498266Sopenharmony_ci
45713498266Sopenharmony_ciSuccess: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
45813498266Sopenharmony_ciFailure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
45913498266Sopenharmony_ci*/
46013498266Sopenharmony_ciSANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
46113498266Sopenharmony_ci                                                const char *file_name,
46213498266Sopenharmony_ci                                                int flags)
46313498266Sopenharmony_ci{
46413498266Sopenharmony_ci  /* We could have a file whose name is a device on MS-DOS.  Trying to
46513498266Sopenharmony_ci   * retrieve such a file would fail at best and wedge us at worst.  We need
46613498266Sopenharmony_ci   * to rename such files. */
46713498266Sopenharmony_ci  char *p, *base;
46813498266Sopenharmony_ci  char fname[PATH_MAX];
46913498266Sopenharmony_ci#ifdef MSDOS
47013498266Sopenharmony_ci  struct_stat st_buf;
47113498266Sopenharmony_ci#endif
47213498266Sopenharmony_ci
47313498266Sopenharmony_ci  if(!sanitized)
47413498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
47513498266Sopenharmony_ci
47613498266Sopenharmony_ci  *sanitized = NULL;
47713498266Sopenharmony_ci
47813498266Sopenharmony_ci  if(!file_name)
47913498266Sopenharmony_ci    return SANITIZE_ERR_BAD_ARGUMENT;
48013498266Sopenharmony_ci
48113498266Sopenharmony_ci  /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
48213498266Sopenharmony_ci#ifndef MSDOS
48313498266Sopenharmony_ci  if((flags & SANITIZE_ALLOW_PATH) &&
48413498266Sopenharmony_ci     file_name[0] == '\\' && file_name[1] == '\\') {
48513498266Sopenharmony_ci    size_t len = strlen(file_name);
48613498266Sopenharmony_ci    *sanitized = malloc(len + 1);
48713498266Sopenharmony_ci    if(!*sanitized)
48813498266Sopenharmony_ci      return SANITIZE_ERR_OUT_OF_MEMORY;
48913498266Sopenharmony_ci    strncpy(*sanitized, file_name, len + 1);
49013498266Sopenharmony_ci    return SANITIZE_ERR_OK;
49113498266Sopenharmony_ci  }
49213498266Sopenharmony_ci#endif
49313498266Sopenharmony_ci
49413498266Sopenharmony_ci  if(strlen(file_name) > PATH_MAX-1 &&
49513498266Sopenharmony_ci     (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
49613498266Sopenharmony_ci      truncate_dryrun(file_name, PATH_MAX-1)))
49713498266Sopenharmony_ci    return SANITIZE_ERR_INVALID_PATH;
49813498266Sopenharmony_ci
49913498266Sopenharmony_ci  strncpy(fname, file_name, PATH_MAX-1);
50013498266Sopenharmony_ci  fname[PATH_MAX-1] = '\0';
50113498266Sopenharmony_ci  base = basename(fname);
50213498266Sopenharmony_ci
50313498266Sopenharmony_ci  /* Rename reserved device names that are known to be accessible without \\.\
50413498266Sopenharmony_ci     Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
50513498266Sopenharmony_ci     https://support.microsoft.com/en-us/kb/74496
50613498266Sopenharmony_ci     https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
50713498266Sopenharmony_ci     */
50813498266Sopenharmony_ci  for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
50913498266Sopenharmony_ci    size_t p_len;
51013498266Sopenharmony_ci    int x = (curl_strnequal(p, "CON", 3) ||
51113498266Sopenharmony_ci             curl_strnequal(p, "PRN", 3) ||
51213498266Sopenharmony_ci             curl_strnequal(p, "AUX", 3) ||
51313498266Sopenharmony_ci             curl_strnequal(p, "NUL", 3)) ? 3 :
51413498266Sopenharmony_ci            (curl_strnequal(p, "CLOCK$", 6)) ? 6 :
51513498266Sopenharmony_ci            (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
51613498266Sopenharmony_ci              (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
51713498266Sopenharmony_ci
51813498266Sopenharmony_ci    if(!x)
51913498266Sopenharmony_ci      continue;
52013498266Sopenharmony_ci
52113498266Sopenharmony_ci    /* the devices may be accessible with an extension or ADS, for
52213498266Sopenharmony_ci       example CON.AIR and 'CON . AIR' and CON:AIR access console */
52313498266Sopenharmony_ci
52413498266Sopenharmony_ci    for(; p[x] == ' '; ++x)
52513498266Sopenharmony_ci      ;
52613498266Sopenharmony_ci
52713498266Sopenharmony_ci    if(p[x] == '.') {
52813498266Sopenharmony_ci      p[x] = '_';
52913498266Sopenharmony_ci      continue;
53013498266Sopenharmony_ci    }
53113498266Sopenharmony_ci    else if(p[x] == ':') {
53213498266Sopenharmony_ci      if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
53313498266Sopenharmony_ci        p[x] = '_';
53413498266Sopenharmony_ci        continue;
53513498266Sopenharmony_ci      }
53613498266Sopenharmony_ci      ++x;
53713498266Sopenharmony_ci    }
53813498266Sopenharmony_ci    else if(p[x]) /* no match */
53913498266Sopenharmony_ci      continue;
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci    /* p points to 'CON' or 'CON ' or 'CON:', etc */
54213498266Sopenharmony_ci    p_len = strlen(p);
54313498266Sopenharmony_ci
54413498266Sopenharmony_ci    /* Prepend a '_' */
54513498266Sopenharmony_ci    if(strlen(fname) == PATH_MAX-1) {
54613498266Sopenharmony_ci      --p_len;
54713498266Sopenharmony_ci      if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
54813498266Sopenharmony_ci        return SANITIZE_ERR_INVALID_PATH;
54913498266Sopenharmony_ci      p[p_len] = '\0';
55013498266Sopenharmony_ci    }
55113498266Sopenharmony_ci    memmove(p + 1, p, p_len + 1);
55213498266Sopenharmony_ci    p[0] = '_';
55313498266Sopenharmony_ci    ++p_len;
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci    /* if fname was just modified then the basename pointer must be updated */
55613498266Sopenharmony_ci    if(p == fname)
55713498266Sopenharmony_ci      base = basename(fname);
55813498266Sopenharmony_ci  }
55913498266Sopenharmony_ci
56013498266Sopenharmony_ci  /* This is the legacy portion from rename_if_dos_device_name that checks for
56113498266Sopenharmony_ci     reserved device names. It only works on MSDOS. On Windows XP the stat
56213498266Sopenharmony_ci     check errors with EINVAL if the device name is reserved. On Windows
56313498266Sopenharmony_ci     Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN
56413498266Sopenharmony_ci     stat doc the latter behavior is correct, but that doesn't help us identify
56513498266Sopenharmony_ci     whether it's a reserved device name and not a regular file name. */
56613498266Sopenharmony_ci#ifdef MSDOS
56713498266Sopenharmony_ci  if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
56813498266Sopenharmony_ci    /* Prepend a '_' */
56913498266Sopenharmony_ci    size_t blen = strlen(base);
57013498266Sopenharmony_ci    if(blen) {
57113498266Sopenharmony_ci      if(strlen(fname) == PATH_MAX-1) {
57213498266Sopenharmony_ci        --blen;
57313498266Sopenharmony_ci        if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
57413498266Sopenharmony_ci          return SANITIZE_ERR_INVALID_PATH;
57513498266Sopenharmony_ci        base[blen] = '\0';
57613498266Sopenharmony_ci      }
57713498266Sopenharmony_ci      memmove(base + 1, base, blen + 1);
57813498266Sopenharmony_ci      base[0] = '_';
57913498266Sopenharmony_ci    }
58013498266Sopenharmony_ci  }
58113498266Sopenharmony_ci#endif
58213498266Sopenharmony_ci
58313498266Sopenharmony_ci  *sanitized = strdup(fname);
58413498266Sopenharmony_ci  return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
58513498266Sopenharmony_ci}
58613498266Sopenharmony_ci
58713498266Sopenharmony_ci#if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
58813498266Sopenharmony_ci
58913498266Sopenharmony_ci/*
59013498266Sopenharmony_ci * Disable program default argument globbing. We do it on our own.
59113498266Sopenharmony_ci */
59213498266Sopenharmony_cichar **__crt0_glob_function(char *arg)
59313498266Sopenharmony_ci{
59413498266Sopenharmony_ci  (void)arg;
59513498266Sopenharmony_ci  return (char **)0;
59613498266Sopenharmony_ci}
59713498266Sopenharmony_ci
59813498266Sopenharmony_ci#endif /* MSDOS && (__DJGPP__ || __GO32__) */
59913498266Sopenharmony_ci
60013498266Sopenharmony_ci#ifdef _WIN32
60113498266Sopenharmony_ci
60213498266Sopenharmony_ci/*
60313498266Sopenharmony_ci * Function to find CACert bundle on a Win32 platform using SearchPath.
60413498266Sopenharmony_ci * (SearchPath is already declared via inclusions done in setup header file)
60513498266Sopenharmony_ci * (Use the ASCII version instead of the unicode one!)
60613498266Sopenharmony_ci * The order of the directories it searches is:
60713498266Sopenharmony_ci *  1. application's directory
60813498266Sopenharmony_ci *  2. current working directory
60913498266Sopenharmony_ci *  3. Windows System directory (e.g. C:\windows\system32)
61013498266Sopenharmony_ci *  4. Windows Directory (e.g. C:\windows)
61113498266Sopenharmony_ci *  5. all directories along %PATH%
61213498266Sopenharmony_ci *
61313498266Sopenharmony_ci * For WinXP and later search order actually depends on registry value:
61413498266Sopenharmony_ci * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
61513498266Sopenharmony_ci */
61613498266Sopenharmony_ci
61713498266Sopenharmony_ciCURLcode FindWin32CACert(struct OperationConfig *config,
61813498266Sopenharmony_ci                         curl_sslbackend backend,
61913498266Sopenharmony_ci                         const TCHAR *bundle_file)
62013498266Sopenharmony_ci{
62113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci  /* Search and set cert file only if libcurl supports SSL.
62413498266Sopenharmony_ci   *
62513498266Sopenharmony_ci   * If Schannel is the selected SSL backend then these locations are
62613498266Sopenharmony_ci   * ignored. We allow setting CA location for schannel only when explicitly
62713498266Sopenharmony_ci   * specified by the user via CURLOPT_CAINFO / --cacert.
62813498266Sopenharmony_ci   */
62913498266Sopenharmony_ci  if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) {
63013498266Sopenharmony_ci
63113498266Sopenharmony_ci    DWORD res_len;
63213498266Sopenharmony_ci    TCHAR buf[PATH_MAX];
63313498266Sopenharmony_ci    TCHAR *ptr = NULL;
63413498266Sopenharmony_ci
63513498266Sopenharmony_ci    buf[0] = TEXT('\0');
63613498266Sopenharmony_ci
63713498266Sopenharmony_ci    res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
63813498266Sopenharmony_ci    if(res_len > 0) {
63913498266Sopenharmony_ci      char *mstr = curlx_convert_tchar_to_UTF8(buf);
64013498266Sopenharmony_ci      Curl_safefree(config->cacert);
64113498266Sopenharmony_ci      if(mstr)
64213498266Sopenharmony_ci        config->cacert = strdup(mstr);
64313498266Sopenharmony_ci      curlx_unicodefree(mstr);
64413498266Sopenharmony_ci      if(!config->cacert)
64513498266Sopenharmony_ci        result = CURLE_OUT_OF_MEMORY;
64613498266Sopenharmony_ci    }
64713498266Sopenharmony_ci  }
64813498266Sopenharmony_ci
64913498266Sopenharmony_ci  return result;
65013498266Sopenharmony_ci}
65113498266Sopenharmony_ci
65213498266Sopenharmony_ci
65313498266Sopenharmony_ci/* Get a list of all loaded modules with full paths.
65413498266Sopenharmony_ci * Returns slist on success or NULL on error.
65513498266Sopenharmony_ci */
65613498266Sopenharmony_cistruct curl_slist *GetLoadedModulePaths(void)
65713498266Sopenharmony_ci{
65813498266Sopenharmony_ci  HANDLE hnd = INVALID_HANDLE_VALUE;
65913498266Sopenharmony_ci  MODULEENTRY32 mod = {0};
66013498266Sopenharmony_ci  struct curl_slist *slist = NULL;
66113498266Sopenharmony_ci
66213498266Sopenharmony_ci  mod.dwSize = sizeof(MODULEENTRY32);
66313498266Sopenharmony_ci
66413498266Sopenharmony_ci  do {
66513498266Sopenharmony_ci    hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
66613498266Sopenharmony_ci  } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
66713498266Sopenharmony_ci
66813498266Sopenharmony_ci  if(hnd == INVALID_HANDLE_VALUE)
66913498266Sopenharmony_ci    goto error;
67013498266Sopenharmony_ci
67113498266Sopenharmony_ci  if(!Module32First(hnd, &mod))
67213498266Sopenharmony_ci    goto error;
67313498266Sopenharmony_ci
67413498266Sopenharmony_ci  do {
67513498266Sopenharmony_ci    char *path; /* points to stack allocated buffer */
67613498266Sopenharmony_ci    struct curl_slist *temp;
67713498266Sopenharmony_ci
67813498266Sopenharmony_ci#ifdef UNICODE
67913498266Sopenharmony_ci    /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total
68013498266Sopenharmony_ci       bytes of multibyte chars won't be more than twice that. */
68113498266Sopenharmony_ci    char buffer[sizeof(mod.szExePath) * 2];
68213498266Sopenharmony_ci    if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
68313498266Sopenharmony_ci                            buffer, sizeof(buffer), NULL, NULL))
68413498266Sopenharmony_ci      goto error;
68513498266Sopenharmony_ci    path = buffer;
68613498266Sopenharmony_ci#else
68713498266Sopenharmony_ci    path = mod.szExePath;
68813498266Sopenharmony_ci#endif
68913498266Sopenharmony_ci    temp = curl_slist_append(slist, path);
69013498266Sopenharmony_ci    if(!temp)
69113498266Sopenharmony_ci      goto error;
69213498266Sopenharmony_ci    slist = temp;
69313498266Sopenharmony_ci  } while(Module32Next(hnd, &mod));
69413498266Sopenharmony_ci
69513498266Sopenharmony_ci  goto cleanup;
69613498266Sopenharmony_ci
69713498266Sopenharmony_cierror:
69813498266Sopenharmony_ci  curl_slist_free_all(slist);
69913498266Sopenharmony_ci  slist = NULL;
70013498266Sopenharmony_cicleanup:
70113498266Sopenharmony_ci  if(hnd != INVALID_HANDLE_VALUE)
70213498266Sopenharmony_ci    CloseHandle(hnd);
70313498266Sopenharmony_ci  return slist;
70413498266Sopenharmony_ci}
70513498266Sopenharmony_ci
70613498266Sopenharmony_ci/* The terminal settings to restore on exit */
70713498266Sopenharmony_cistatic struct TerminalSettings {
70813498266Sopenharmony_ci  HANDLE hStdOut;
70913498266Sopenharmony_ci  DWORD dwOutputMode;
71013498266Sopenharmony_ci  LONG valid;
71113498266Sopenharmony_ci} TerminalSettings;
71213498266Sopenharmony_ci
71313498266Sopenharmony_ci#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
71413498266Sopenharmony_ci#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
71513498266Sopenharmony_ci#endif
71613498266Sopenharmony_ci
71713498266Sopenharmony_cibool tool_term_has_bold;
71813498266Sopenharmony_ci
71913498266Sopenharmony_cistatic void restore_terminal(void)
72013498266Sopenharmony_ci{
72113498266Sopenharmony_ci  if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE))
72213498266Sopenharmony_ci    SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode);
72313498266Sopenharmony_ci}
72413498266Sopenharmony_ci
72513498266Sopenharmony_ci/* This is the console signal handler.
72613498266Sopenharmony_ci * The system calls it in a separate thread.
72713498266Sopenharmony_ci */
72813498266Sopenharmony_cistatic BOOL WINAPI signal_handler(DWORD type)
72913498266Sopenharmony_ci{
73013498266Sopenharmony_ci  if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
73113498266Sopenharmony_ci    restore_terminal();
73213498266Sopenharmony_ci  return FALSE;
73313498266Sopenharmony_ci}
73413498266Sopenharmony_ci
73513498266Sopenharmony_cistatic void init_terminal(void)
73613498266Sopenharmony_ci{
73713498266Sopenharmony_ci  TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
73813498266Sopenharmony_ci
73913498266Sopenharmony_ci  /*
74013498266Sopenharmony_ci   * Enable VT (Virtual Terminal) output.
74113498266Sopenharmony_ci   * Note: VT mode flag can be set on any version of Windows, but VT
74213498266Sopenharmony_ci   * processing only performed on Win10 >= version 1709 (OS build 16299)
74313498266Sopenharmony_ci   * Creator's Update. Also, ANSI bold on/off supported since then.
74413498266Sopenharmony_ci   */
74513498266Sopenharmony_ci  if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE ||
74613498266Sopenharmony_ci     !GetConsoleMode(TerminalSettings.hStdOut,
74713498266Sopenharmony_ci                     &TerminalSettings.dwOutputMode) ||
74813498266Sopenharmony_ci     !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT,
74913498266Sopenharmony_ci                                   VERSION_GREATER_THAN_EQUAL))
75013498266Sopenharmony_ci    return;
75113498266Sopenharmony_ci
75213498266Sopenharmony_ci  if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
75313498266Sopenharmony_ci    tool_term_has_bold = true;
75413498266Sopenharmony_ci  else {
75513498266Sopenharmony_ci    /* The signal handler is set before attempting to change the console mode
75613498266Sopenharmony_ci       because otherwise a signal would not be caught after the change but
75713498266Sopenharmony_ci       before the handler was installed. */
75813498266Sopenharmony_ci    (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE);
75913498266Sopenharmony_ci    if(SetConsoleCtrlHandler(signal_handler, TRUE)) {
76013498266Sopenharmony_ci      if(SetConsoleMode(TerminalSettings.hStdOut,
76113498266Sopenharmony_ci                        (TerminalSettings.dwOutputMode |
76213498266Sopenharmony_ci                         ENABLE_VIRTUAL_TERMINAL_PROCESSING))) {
76313498266Sopenharmony_ci        tool_term_has_bold = true;
76413498266Sopenharmony_ci        atexit(restore_terminal);
76513498266Sopenharmony_ci      }
76613498266Sopenharmony_ci      else {
76713498266Sopenharmony_ci        SetConsoleCtrlHandler(signal_handler, FALSE);
76813498266Sopenharmony_ci        (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE);
76913498266Sopenharmony_ci      }
77013498266Sopenharmony_ci    }
77113498266Sopenharmony_ci  }
77213498266Sopenharmony_ci}
77313498266Sopenharmony_ci
77413498266Sopenharmony_ciLARGE_INTEGER tool_freq;
77513498266Sopenharmony_cibool tool_isVistaOrGreater;
77613498266Sopenharmony_ci
77713498266Sopenharmony_ciCURLcode win32_init(void)
77813498266Sopenharmony_ci{
77913498266Sopenharmony_ci  /* curlx_verify_windows_version must be called during init at least once
78013498266Sopenharmony_ci     because it has its own initialization routine. */
78113498266Sopenharmony_ci  if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
78213498266Sopenharmony_ci                                  VERSION_GREATER_THAN_EQUAL))
78313498266Sopenharmony_ci    tool_isVistaOrGreater = true;
78413498266Sopenharmony_ci  else
78513498266Sopenharmony_ci    tool_isVistaOrGreater = false;
78613498266Sopenharmony_ci
78713498266Sopenharmony_ci  QueryPerformanceFrequency(&tool_freq);
78813498266Sopenharmony_ci
78913498266Sopenharmony_ci  init_terminal();
79013498266Sopenharmony_ci
79113498266Sopenharmony_ci  return CURLE_OK;
79213498266Sopenharmony_ci}
79313498266Sopenharmony_ci
79413498266Sopenharmony_ci#endif /* _WIN32 */
79513498266Sopenharmony_ci
79613498266Sopenharmony_ci#endif /* _WIN32 || MSDOS */
797