tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

wchar_filename.h (8939B)


      1 /*
      2 * This file is part of FFmpeg.
      3 *
      4 * FFmpeg is free software; you can redistribute it and/or
      5 * modify it under the terms of the GNU Lesser General Public
      6 * License as published by the Free Software Foundation; either
      7 * version 2.1 of the License, or (at your option) any later version.
      8 *
      9 * FFmpeg is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 * Lesser General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU Lesser General Public
     15 * License along with FFmpeg; if not, write to the Free Software
     16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     17 */
     18 
     19 #ifndef AVUTIL_WCHAR_FILENAME_H
     20 #define AVUTIL_WCHAR_FILENAME_H
     21 
     22 #ifdef _WIN32
     23 
     24 #include <errno.h>
     25 #include <stddef.h>
     26 #include <windows.h>
     27 #include "mem.h"
     28 
     29 av_warn_unused_result
     30 static inline int utf8towchar(const char *filename_utf8, wchar_t **filename_w)
     31 {
     32    int num_chars;
     33    num_chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename_utf8, -1, NULL, 0);
     34    if (num_chars <= 0) {
     35        *filename_w = NULL;
     36        errno = EINVAL;
     37        return -1;
     38    }
     39    *filename_w = (wchar_t *)av_calloc(num_chars, sizeof(wchar_t));
     40    if (!*filename_w) {
     41        errno = ENOMEM;
     42        return -1;
     43    }
     44    MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, *filename_w, num_chars);
     45    return 0;
     46 }
     47 
     48 av_warn_unused_result
     49 static inline int wchartocp(unsigned int code_page, const wchar_t *filename_w,
     50                            char **filename)
     51 {
     52    DWORD flags = code_page == CP_UTF8 ? WC_ERR_INVALID_CHARS : 0;
     53    int num_chars = WideCharToMultiByte(code_page, flags, filename_w, -1,
     54                                        NULL, 0, NULL, NULL);
     55    if (num_chars <= 0) {
     56        *filename = NULL;
     57        errno = EINVAL;
     58        return -1;
     59    }
     60    *filename = (char *)av_malloc_array(num_chars, sizeof **filename);
     61    if (!*filename) {
     62        errno = ENOMEM;
     63        return -1;
     64    }
     65    WideCharToMultiByte(code_page, flags, filename_w, -1,
     66                        *filename, num_chars, NULL, NULL);
     67    return 0;
     68 }
     69 
     70 av_warn_unused_result
     71 static inline int wchartoutf8(const wchar_t *filename_w, char **filename)
     72 {
     73    return wchartocp(CP_UTF8, filename_w, filename);
     74 }
     75 
     76 av_warn_unused_result
     77 static inline int wchartoansi(const wchar_t *filename_w, char **filename)
     78 {
     79    return wchartocp(CP_ACP, filename_w, filename);
     80 }
     81 
     82 av_warn_unused_result
     83 static inline int utf8toansi(const char *filename_utf8, char **filename)
     84 {
     85    wchar_t *filename_w = NULL;
     86    int ret = -1;
     87    if (utf8towchar(filename_utf8, &filename_w))
     88        return -1;
     89 
     90    if (!filename_w) {
     91        *filename = NULL;
     92        return 0;
     93    }
     94 
     95    ret = wchartoansi(filename_w, filename);
     96    av_free(filename_w);
     97    return ret;
     98 }
     99 
    100 /**
    101 * Checks for extended path prefixes for which normalization needs to be skipped.
    102 * see .NET6: PathInternal.IsExtended()
    103 * https://github.com/dotnet/runtime/blob/9260c249140ef90b4299d0fe1aa3037e25228518/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L165
    104 */
    105 static inline int path_is_extended(const wchar_t *path)
    106 {
    107    if (path[0] == L'\\' && (path[1] == L'\\' || path[1] == L'?') && path[2] == L'?' && path[3] == L'\\')
    108        return 1;
    109 
    110    return 0;
    111 }
    112 
    113 /**
    114 * Checks for a device path prefix.
    115 * see .NET6: PathInternal.IsDevice()
    116 * we don't check forward slashes and extended paths (as already done)
    117 * https://github.com/dotnet/runtime/blob/9260c249140ef90b4299d0fe1aa3037e25228518/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L132
    118 */
    119 static inline int path_is_device_path(const wchar_t *path)
    120 {
    121    if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'.' && path[3] == L'\\')
    122        return 1;
    123 
    124    return 0;
    125 }
    126 
    127 /**
    128 * Performs path normalization by calling GetFullPathNameW().
    129 * see .NET6: PathHelper.GetFullPathName()
    130 * https://github.com/dotnet/runtime/blob/2a99e18eedabcf1add064c099da59d9301ce45e0/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs#L70
    131 */
    132 static inline int get_full_path_name(wchar_t **ppath_w)
    133 {
    134    int num_chars;
    135    wchar_t *temp_w;
    136 
    137    num_chars = GetFullPathNameW(*ppath_w, 0, NULL, NULL);
    138    if (num_chars <= 0) {
    139        errno = EINVAL;
    140        return -1;
    141    }
    142 
    143    temp_w = (wchar_t *)av_calloc(num_chars, sizeof(wchar_t));
    144    if (!temp_w) {
    145        errno = ENOMEM;
    146        return -1;
    147    }
    148 
    149    num_chars = GetFullPathNameW(*ppath_w, num_chars, temp_w, NULL);
    150    if (num_chars <= 0) {
    151        av_free(temp_w);
    152        errno = EINVAL;
    153        return -1;
    154    }
    155 
    156    av_freep(ppath_w);
    157    *ppath_w = temp_w;
    158 
    159    return 0;
    160 }
    161 
    162 /**
    163 * Normalizes a Windows file or folder path.
    164 * Expansion of short paths (with 8.3 path components) is currently omitted
    165 * as it is not required for accessing long paths.
    166 * see .NET6: PathHelper.Normalize()
    167 * https://github.com/dotnet/runtime/blob/2a99e18eedabcf1add064c099da59d9301ce45e0/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs#L25
    168 */
    169 static inline int path_normalize(wchar_t **ppath_w)
    170 {
    171    int ret;
    172 
    173    if ((ret = get_full_path_name(ppath_w)) < 0)
    174        return ret;
    175 
    176    /* What .NET does at this point is to call PathHelper.TryExpandShortFileName()
    177     * in case the path contains a '~' character.
    178     * We don't need to do this as we don't need to normalize the file name
    179     * for presentation, and the extended path prefix works with 8.3 path
    180     * components as well
    181     */
    182    return 0;
    183 }
    184 
    185 /**
    186 * Adds an extended path or UNC prefix to longs paths or paths ending
    187 * with a space or a dot. (' ' or '.').
    188 * This function expects that the path has been normalized before by
    189 * calling path_normalize() and it doesn't check whether the path is
    190 * actually long (> MAX_PATH).
    191 * see .NET6: PathInternal.EnsureExtendedPrefix()
    192 * https://github.com/dotnet/runtime/blob/9260c249140ef90b4299d0fe1aa3037e25228518/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L107
    193 */
    194 static inline int add_extended_prefix(wchar_t **ppath_w)
    195 {
    196    const wchar_t *unc_prefix           = L"\\\\?\\UNC\\";
    197    const wchar_t *extended_path_prefix = L"\\\\?\\";
    198    const wchar_t *path_w               = *ppath_w;
    199    const size_t len                    = wcslen(path_w);
    200    wchar_t *temp_w;
    201 
    202    /* We're skipping the check IsPartiallyQualified() because
    203     * we expect to have called GetFullPathNameW() already. */
    204    if (len < 2 || path_is_extended(*ppath_w) || path_is_device_path(*ppath_w)) {
    205        return 0;
    206    }
    207 
    208    if (path_w[0] == L'\\' && path_w[1] == L'\\') {
    209        /* unc_prefix length is 8 plus 1 for terminating zeros,
    210         * we subtract 2 for the leading '\\' of the original path */
    211        temp_w = (wchar_t *)av_calloc(len - 2 + 8 + 1, sizeof(wchar_t));
    212        if (!temp_w) {
    213            errno = ENOMEM;
    214            return -1;
    215        }
    216        wcscpy(temp_w, unc_prefix);
    217        wcscat(temp_w, path_w + 2);
    218    } else {
    219        // The length of extended_path_prefix is 4 plus 1 for terminating zeros
    220        temp_w = (wchar_t *)av_calloc(len + 4 + 1, sizeof(wchar_t));
    221        if (!temp_w) {
    222            errno = ENOMEM;
    223            return -1;
    224        }
    225        wcscpy(temp_w, extended_path_prefix);
    226        wcscat(temp_w, path_w);
    227    }
    228 
    229    av_freep(ppath_w);
    230    *ppath_w = temp_w;
    231 
    232    return 0;
    233 }
    234 
    235 /**
    236 * Converts a file or folder path to wchar_t for use with Windows file
    237 * APIs. Paths with extended path prefix (either '\\?\' or \??\') are
    238 * left unchanged.
    239 * All other paths are normalized and converted to absolute paths.
    240 * Longs paths (>= MAX_PATH) are prefixed with the extended path or extended
    241 * UNC path prefix.
    242 * see .NET6: Path.GetFullPath() and Path.GetFullPathInternal()
    243 * https://github.com/dotnet/runtime/blob/2a99e18eedabcf1add064c099da59d9301ce45e0/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs#L126
    244 */
    245 static inline int get_extended_win32_path(const char *path, wchar_t **ppath_w)
    246 {
    247    int ret;
    248    size_t len;
    249 
    250    if ((ret = utf8towchar(path, ppath_w)) < 0)
    251        return ret;
    252 
    253    if (path_is_extended(*ppath_w)) {
    254        /* Paths prefixed with '\\?\' or \??\' are considered normalized by definition.
    255         * Windows doesn't normalize those paths and neither should we.
    256         */
    257        return 0;
    258    }
    259 
    260    if ((ret = path_normalize(ppath_w)) < 0) {
    261        av_freep(ppath_w);
    262        return ret;
    263    }
    264 
    265    /* see .NET6: PathInternal.EnsureExtendedPrefixIfNeeded()
    266     * https://github.com/dotnet/runtime/blob/9260c249140ef90b4299d0fe1aa3037e25228518/src/libraries/Common/src/System/IO/PathInternal.Windows.cs#L92
    267     */
    268    len = wcslen(*ppath_w);
    269    if (len >= MAX_PATH) {
    270        if ((ret = add_extended_prefix(ppath_w)) < 0) {
    271            av_freep(ppath_w);
    272            return ret;
    273        }
    274    }
    275 
    276    return 0;
    277 }
    278 
    279 #endif
    280 
    281 #endif /* AVUTIL_WCHAR_FILENAME_H */