FFmpeg
Loading...
Searching...
No Matches
wchar_filename.h
Go to the documentation of this file.
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
30static 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
49static 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
71static inline int wchartoutf8(const wchar_t *filename_w, char **filename)
72{
73 return wchartocp(CP_UTF8, filename_w, filename);
74}
75
77static inline int wchartoansi(const wchar_t *filename_w, char **filename)
78{
79 return wchartocp(CP_ACP, filename_w, filename);
80}
81
83static 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 */
105static 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 */
119static 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 */
132static 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 */
169static 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 */
194static 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 */
245static 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 */
#define L(x)
Definition vpx_arith.h:36
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define NULL
Definition coverity.c:32
#define av_warn_unused_result
Definition attributes.h:91
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
#define av_free(p)
#define av_malloc_array(a, b)
#define av_freep(p)
int len