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