FreeRDP
Loading...
Searching...
No Matches
path.c
1
20#include <winpr/config.h>
21#include <winpr/version.h>
22#include <winpr/build-config.h>
23
24#include <winpr/crt.h>
25#include <winpr/tchar.h>
26
27#include <winpr/path.h>
28#include <winpr/file.h>
29
30#include "../utils.h"
31#include "path.h"
32
33#if defined(WITH_CWALK)
34#include <cwalk.h>
35#endif
36
37#ifndef PATHCCH_MAX_CCH
38#define PATHCCH_MAX_CCH 0x8000
39#endif
40
41static const char PATH_SLASH_CHR = '/';
42static const char PATH_SLASH_STR[] = "/";
43
44static const char PATH_BACKSLASH_CHR = '\\';
45
46#ifdef _WIN32
47static const char PATH_BACKSLASH_STR[] = "\\";
48static const WCHAR PATH_BACKSLASH_STR_W[] = L"\\";
49static const WCHAR PATH_SLASH_CHR_W = L'/';
50static const WCHAR PATH_BACKSLASH_CHR_W = L'\\';
51static const WCHAR PATH_SLASH_STR_W[] = L"/";
52#else
53#if defined(__BIG_ENDIAN__)
54static const WCHAR PATH_SLASH_CHR_W = 0x2f00;
55static const WCHAR PATH_BACKSLASH_CHR_W = 0x5c00;
56static const WCHAR PATH_SLASH_STR_W[] = { 0x2f00, '\0' };
57#else
58static const WCHAR PATH_SLASH_CHR_W = '/';
59static const WCHAR PATH_BACKSLASH_CHR_W = '\\';
60static const WCHAR PATH_SLASH_STR_W[] = { '/', '\0' };
61#endif
62
63#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
64#if defined(__BIG_ENDIAN__)
65static const WCHAR PATH_BACKSLASH_STR_W[] = { 0x5c00, '\0' };
66#else
67static const WCHAR PATH_BACKSLASH_STR_W[] = { '\\', '\0' };
68#endif
69static const char PATH_BACKSLASH_STR[] = "\\";
70#endif
71
72#endif
73
74#ifdef _WIN32
75#define PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
76#define PATH_SEPARATOR_STR PATH_BACKSLASH_STR
77#define PATH_SEPARATOR_CHR_W PATH_BACKSLASH_CHR_W
78#define PATH_SEPARATOR_STR_W PATH_BACKSLASH_STR_W
79#else
80#define PATH_SEPARATOR_CHR PATH_SLASH_CHR
81#define PATH_SEPARATOR_STR PATH_SLASH_STR
82#define PATH_SEPARATOR_CHR_W PATH_SLASH_CHR_W
83#define PATH_SEPARATOR_STR_W PATH_SLASH_STR_W
84#endif
85
86#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
87#include "../log.h"
88#define TAG WINPR_TAG("path")
89#endif
90
91#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
92/*
93 * PathCchAddBackslash
94 */
95
96/* Windows-style Paths */
97
98#define DEFINE_UNICODE FALSE
99#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
100#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashA
101#include "include/PathCchAddSeparator.h"
102#undef DEFINE_UNICODE
103#undef CUR_PATH_SEPARATOR_CHR
104#undef PATH_CCH_ADD_SEPARATOR
105
106#define DEFINE_UNICODE TRUE
107#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
108#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashW
109#include "include/PathCchAddSeparator.h"
110#undef DEFINE_UNICODE
111#undef CUR_PATH_SEPARATOR_CHR
112#undef PATH_CCH_ADD_SEPARATOR
113
114/* Unix-style Paths */
115
116#define DEFINE_UNICODE FALSE
117#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
118#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashA
119#include "include/PathCchAddSeparator.h"
120#undef DEFINE_UNICODE
121#undef CUR_PATH_SEPARATOR_CHR
122#undef PATH_CCH_ADD_SEPARATOR
123
124#define DEFINE_UNICODE TRUE
125#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
126#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashW
127#include "include/PathCchAddSeparator.h"
128#undef DEFINE_UNICODE
129#undef CUR_PATH_SEPARATOR_CHR
130#undef PATH_CCH_ADD_SEPARATOR
131
132/* Native-style Paths */
133
134#define DEFINE_UNICODE FALSE
135#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
136#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorA
137#include "include/PathCchAddSeparator.h"
138#undef DEFINE_UNICODE
139#undef CUR_PATH_SEPARATOR_CHR
140#undef PATH_CCH_ADD_SEPARATOR
141
142#define DEFINE_UNICODE TRUE
143#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
144#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorW
145#include "include/PathCchAddSeparator.h"
146#undef DEFINE_UNICODE
147#undef CUR_PATH_SEPARATOR_CHR
148#undef PATH_CCH_ADD_SEPARATOR
149
150/*
151 * PathCchRemoveBackslash
152 */
153
154HRESULT PathCchRemoveBackslashA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
155{
156 WLog_ERR(TAG, "not implemented");
157 return E_NOTIMPL;
158}
159
160HRESULT PathCchRemoveBackslashW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
161{
162 WLog_ERR(TAG, "not implemented");
163 return E_NOTIMPL;
164}
165
166/*
167 * PathCchAddBackslashEx
168 */
169
170/* Windows-style Paths */
171
172#define DEFINE_UNICODE FALSE
173#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
174#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExA
175#include "include/PathCchAddSeparatorEx.h"
176#undef DEFINE_UNICODE
177#undef CUR_PATH_SEPARATOR_CHR
178#undef PATH_CCH_ADD_SEPARATOR_EX
179
180#define DEFINE_UNICODE TRUE
181#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
182#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExW
183#include "include/PathCchAddSeparatorEx.h"
184#undef DEFINE_UNICODE
185#undef CUR_PATH_SEPARATOR_CHR
186#undef PATH_CCH_ADD_SEPARATOR_EX
187
188/* Unix-style Paths */
189
190#define DEFINE_UNICODE FALSE
191#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
192#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExA
193#include "include/PathCchAddSeparatorEx.h"
194#undef DEFINE_UNICODE
195#undef CUR_PATH_SEPARATOR_CHR
196#undef PATH_CCH_ADD_SEPARATOR_EX
197
198#define DEFINE_UNICODE TRUE
199#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
200#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExW
201#include "include/PathCchAddSeparatorEx.h"
202#undef DEFINE_UNICODE
203#undef CUR_PATH_SEPARATOR_CHR
204#undef PATH_CCH_ADD_SEPARATOR_EX
205
206/* Native-style Paths */
207
208#define DEFINE_UNICODE FALSE
209#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
210#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExA
211#include "include/PathCchAddSeparatorEx.h"
212#undef DEFINE_UNICODE
213#undef CUR_PATH_SEPARATOR_CHR
214#undef PATH_CCH_ADD_SEPARATOR_EX
215
216#define DEFINE_UNICODE TRUE
217#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
218#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExW
219#include "include/PathCchAddSeparatorEx.h"
220#undef DEFINE_UNICODE
221#undef CUR_PATH_SEPARATOR_CHR
222#undef PATH_CCH_ADD_SEPARATOR_EX
223
224HRESULT PathCchRemoveBackslashExA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
225 WINPR_ATTR_UNUSED PSTR* ppszEnd,
226 WINPR_ATTR_UNUSED size_t* pcchRemaining)
227{
228 WLog_ERR(TAG, "not implemented");
229 return E_NOTIMPL;
230}
231
232HRESULT PathCchRemoveBackslashExW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
233 WINPR_ATTR_UNUSED PWSTR* ppszEnd,
234 WINPR_ATTR_UNUSED size_t* pcchRemaining)
235{
236 WLog_ERR(TAG, "not implemented");
237 return E_NOTIMPL;
238}
239
240/*
241 * PathCchAddExtension
242 */
243
244/* Windows-style Paths */
245
246#define DEFINE_UNICODE FALSE
247#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
248#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionA
249#include "include/PathCchAddExtension.h"
250#undef DEFINE_UNICODE
251#undef CUR_PATH_SEPARATOR_CHR
252#undef PATH_CCH_ADD_EXTENSION
253
254#define DEFINE_UNICODE TRUE
255#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
256#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionW
257#include "include/PathCchAddExtension.h"
258#undef DEFINE_UNICODE
259#undef CUR_PATH_SEPARATOR_CHR
260#undef PATH_CCH_ADD_EXTENSION
261
262#endif
263
264/* Unix-style Paths */
265
266#define DEFINE_UNICODE FALSE
267#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
268#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionA
269#include "include/PathCchAddExtension.h"
270#undef DEFINE_UNICODE
271#undef CUR_PATH_SEPARATOR_CHR
272#undef PATH_CCH_ADD_EXTENSION
273
274#define DEFINE_UNICODE TRUE
275#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
276#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionW
277#include "include/PathCchAddExtension.h"
278#undef DEFINE_UNICODE
279#undef CUR_PATH_SEPARATOR_CHR
280#undef PATH_CCH_ADD_EXTENSION
281
282/* Native-style Paths */
283
284#define DEFINE_UNICODE FALSE
285#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
286#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionA
287#include "include/PathCchAddExtension.h"
288#undef DEFINE_UNICODE
289#undef CUR_PATH_SEPARATOR_CHR
290#undef PATH_CCH_ADD_EXTENSION
291
292#define DEFINE_UNICODE TRUE
293#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
294#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionW
295#include "include/PathCchAddExtension.h"
296#undef DEFINE_UNICODE
297#undef CUR_PATH_SEPARATOR_CHR
298#undef PATH_CCH_ADD_EXTENSION
299
300/*
301 * PathCchAppend
302 */
303
304/* Windows-style Paths */
305#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
306#define DEFINE_UNICODE FALSE
307#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
308#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR
309#define PATH_CCH_APPEND PathCchAppendA
310#include "include/PathCchAppend.h"
311#undef DEFINE_UNICODE
312#undef CUR_PATH_SEPARATOR_CHR
313#undef CUR_PATH_SEPARATOR_STR
314#undef PATH_CCH_APPEND
315
316#define DEFINE_UNICODE TRUE
317#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
318#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W
319#define PATH_CCH_APPEND PathCchAppendW
320#include "include/PathCchAppend.h"
321#undef DEFINE_UNICODE
322#undef CUR_PATH_SEPARATOR_CHR
323#undef CUR_PATH_SEPARATOR_STR
324#undef PATH_CCH_APPEND
325
326/* Unix-style Paths */
327
328#define DEFINE_UNICODE FALSE
329#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
330#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR
331#define PATH_CCH_APPEND UnixPathCchAppendA
332#include "include/PathCchAppend.h"
333#undef DEFINE_UNICODE
334#undef CUR_PATH_SEPARATOR_CHR
335#undef CUR_PATH_SEPARATOR_STR
336#undef PATH_CCH_APPEND
337
338#define DEFINE_UNICODE TRUE
339#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
340#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W
341#define PATH_CCH_APPEND UnixPathCchAppendW
342#include "include/PathCchAppend.h"
343#undef DEFINE_UNICODE
344#undef CUR_PATH_SEPARATOR_CHR
345#undef CUR_PATH_SEPARATOR_STR
346#undef PATH_CCH_APPEND
347
348#endif
349/* Native-style Paths */
350
351#define DEFINE_UNICODE FALSE
352#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
353#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR
354#define PATH_CCH_APPEND NativePathCchAppendA
355#include "include/PathCchAppend.h"
356#undef DEFINE_UNICODE
357#undef CUR_PATH_SEPARATOR_CHR
358#undef CUR_PATH_SEPARATOR_STR
359#undef PATH_CCH_APPEND
360
361#define DEFINE_UNICODE TRUE
362#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
363#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W
364#define PATH_CCH_APPEND NativePathCchAppendW
365#include "include/PathCchAppend.h"
366#undef DEFINE_UNICODE
367#undef CUR_PATH_SEPARATOR_CHR
368#undef CUR_PATH_SEPARATOR_STR
369#undef PATH_CCH_APPEND
370
371#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
372/*
373 * PathCchAppendEx
374 */
375
376HRESULT PathCchAppendExA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
377 WINPR_ATTR_UNUSED PCSTR pszMore, WINPR_ATTR_UNUSED unsigned long dwFlags)
378{
379 WLog_ERR(TAG, "not implemented");
380 return E_NOTIMPL;
381}
382
383HRESULT PathCchAppendExW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
384 WINPR_ATTR_UNUSED PCWSTR pszMore, WINPR_ATTR_UNUSED unsigned long dwFlags)
385{
386 WLog_ERR(TAG, "not implemented");
387 return E_NOTIMPL;
388}
389
390#endif
391
392#if !defined(_WIN32)
393
394#if !defined(WITH_CWALK)
395static void replace(char* str, size_t slen, const char* pattern)
396{
397 const size_t len = strlen(pattern);
398 while (TRUE)
399 {
400 char* cur = strstr(str, pattern);
401 if (!cur)
402 return;
403
404 const char* src = &cur[len];
405 /* Ensure terminating '\0' is moved as well */
406 const size_t rem = strnlen(src, slen) + 1;
407 memmove(&cur[1], src, rem);
408 }
409}
410
411WINPR_ATTR_NODISCARD
412static BOOL replace_dotdot(char* str, size_t slen)
413{
414 const char pattern[] = "/../";
415 const size_t len = strlen(pattern);
416 while (TRUE)
417 {
418 char* cur = strstr(str, pattern);
419 if (!cur)
420 return TRUE;
421
422 char* start = cur;
423 const char* end = &cur[len];
424 while (start > str)
425 {
426 start--;
427 if (*start == '/')
428 break;
429 }
430 if (*start != '/')
431 return FALSE;
432
433 /* Ensure terminating '\0' is moved as well */
434 const size_t rem = strnlen(end, slen) + 1;
435 memmove(&start[1], end, rem);
436 }
437}
438
439WINPR_ATTR_NODISCARD
440static BOOL replace_trailing_dotdot(char* str, size_t slen)
441{
442 const size_t len = strnlen(str, slen);
443 if (len < 3)
444 return TRUE;
445
446 if (strcmp(&str[len - 3], "/..") != 0)
447 return TRUE;
448
449 char* start = &str[len - 4];
450 while (start >= str)
451 {
452 if (*start == '/')
453 {
454 start[1] = '\0';
455 return TRUE;
456 }
457 start--;
458 }
459 return FALSE;
460}
461
462#endif
463
464WINPR_ATTR_NODISCARD
465char* winpr_PathCanonicalize(const char* path)
466{
467 WINPR_ASSERT(path);
468
469 const size_t len = strlen(path);
470#if defined(WITH_CWALK)
471 char* str = calloc(len + 1, sizeof(char));
472 if (!str)
473 return nullptr;
474 (void)cwk_path_normalize(path, str, len + 1);
475 return str;
476#else
477 char* str = strndup(path, len);
478 if (!str)
479 return nullptr;
480 replace(str, len, "/./");
481 replace(str, len, "//");
482 if (!replace_dotdot(str, len) || !replace_trailing_dotdot(str, len))
483 {
484 free(str);
485 return nullptr;
486 }
487
488 size_t slen = 0;
489 while ((slen = strnlen(str, len)) > 1)
490 {
491 if ((str[slen - 1] == '.') || (str[slen - 1] == '/'))
492 str[slen - 1] = '\0';
493 else
494 break;
495 }
496
497 return str;
498#endif
499}
500
501#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
502/*
503 * PathCchCanonicalize
504 */
505
506HRESULT PathCchCanonicalizeA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn)
507{
508 char* out = winpr_PathCanonicalize(pszPathIn);
509 if (!out)
510 return E_OUTOFMEMORY;
511 const size_t len = strnlen(out, cchPathOut);
512 if (len >= cchPathOut)
513 {
514 free(out);
515 return E_INVALIDARG;
516 }
517 strncpy(pszPathOut, out, cchPathOut);
518 free(out);
519 return S_OK;
520}
521
522HRESULT PathCchCanonicalizeW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn)
523{
524 if (!pszPathIn)
525 return E_OUTOFMEMORY;
526 char* str = ConvertWCharToUtf8Alloc(pszPathIn, nullptr);
527 if (!str)
528 return E_OUTOFMEMORY;
529
530 char* out = calloc(cchPathOut, sizeof(CHAR));
531 if (!out)
532 {
533 free(str);
534 return E_OUTOFMEMORY;
535 }
536
537 HRESULT hr = PathCchCanonicalizeA(out, cchPathOut, str);
538 free(str);
539 (void)ConvertUtf8NToWChar(out, cchPathOut, pszPathOut, cchPathOut);
540 free(out);
541 return hr;
542}
543
544/*
545 * PathCchCanonicalizeEx
546 */
547
548HRESULT PathCchCanonicalizeExA(WINPR_ATTR_UNUSED PSTR pszPathOut,
549 WINPR_ATTR_UNUSED size_t cchPathOut,
550 WINPR_ATTR_UNUSED PCSTR pszPathIn,
551 WINPR_ATTR_UNUSED unsigned long dwFlags)
552{
553 if (dwFlags != 0)
554 WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
555 return PathCchCanonicalizeA(pszPathOut, cchPathOut, pszPathIn);
556}
557
558HRESULT PathCchCanonicalizeExW(WINPR_ATTR_UNUSED PWSTR pszPathOut,
559 WINPR_ATTR_UNUSED size_t cchPathOut,
560 WINPR_ATTR_UNUSED PCWSTR pszPathIn,
561 WINPR_ATTR_UNUSED unsigned long dwFlags)
562{
563 if (dwFlags != 0)
564 WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
565 return PathCchCanonicalizeW(pszPathOut, cchPathOut, pszPathIn);
566}
567
568/*
569 * PathAllocCanonicalize
570 */
571
572HRESULT PathAllocCanonicalizeA(PCSTR pszPathIn, unsigned long dwFlags, PSTR* ppszPathOut)
573{
574 if (!ppszPathOut)
575 return E_INVALIDARG;
576 if (!pszPathIn)
577 return E_OUTOFMEMORY;
578 if (dwFlags != 0)
579 WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
580 *ppszPathOut = winpr_PathCanonicalize(pszPathIn);
581 if (!*ppszPathOut)
582 return E_OUTOFMEMORY;
583 return S_OK;
584}
585
586HRESULT PathAllocCanonicalizeW(PCWSTR pszPathIn, unsigned long dwFlags, PWSTR* ppszPathOut)
587{
588 if (!ppszPathOut)
589 return E_INVALIDARG;
590 if (!pszPathIn)
591 return E_OUTOFMEMORY;
592 char* str = ConvertWCharToUtf8Alloc(pszPathIn, nullptr);
593 if (!str)
594 return E_OUTOFMEMORY;
595
596 char* out = nullptr;
597 HRESULT hr = PathAllocCanonicalizeA(str, dwFlags, &out);
598 if (out)
599 *ppszPathOut = ConvertUtf8ToWCharAlloc(out, nullptr);
600 free(str);
601 free(out);
602 return hr;
603}
604
605#endif
606#endif
607
608#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
609/*
610 * PathCchCombine
611 */
612
613HRESULT PathCchCombineA(WINPR_ATTR_UNUSED PSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
614 WINPR_ATTR_UNUSED PCSTR pszPathIn, WINPR_ATTR_UNUSED PCSTR pszMore)
615{
616 WLog_ERR(TAG, "not implemented");
617 return E_NOTIMPL;
618}
619
620HRESULT PathCchCombineW(WINPR_ATTR_UNUSED PWSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
621 WINPR_ATTR_UNUSED PCWSTR pszPathIn, WINPR_ATTR_UNUSED PCWSTR pszMore)
622{
623 WLog_ERR(TAG, "not implemented");
624 return E_NOTIMPL;
625}
626
627/*
628 * PathCchCombineEx
629 */
630
631HRESULT PathCchCombineExA(WINPR_ATTR_UNUSED PSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
632 WINPR_ATTR_UNUSED PCSTR pszPathIn, WINPR_ATTR_UNUSED PCSTR pszMore,
633 WINPR_ATTR_UNUSED unsigned long dwFlags)
634{
635 WLog_ERR(TAG, "not implemented");
636 return E_NOTIMPL;
637}
638
639HRESULT PathCchCombineExW(WINPR_ATTR_UNUSED PWSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
640 WINPR_ATTR_UNUSED PCWSTR pszPathIn, WINPR_ATTR_UNUSED PCWSTR pszMore,
641 WINPR_ATTR_UNUSED unsigned long dwFlags)
642{
643 WLog_ERR(TAG, "not implemented");
644 return E_NOTIMPL;
645}
646
647/*
648 * PathAllocCombine
649 */
650
651/* Windows-style Paths */
652
653#define DEFINE_UNICODE FALSE
654#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
655#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR
656#define PATH_ALLOC_COMBINE PathAllocCombineA
657#include "include/PathAllocCombine.h"
658#undef DEFINE_UNICODE
659#undef CUR_PATH_SEPARATOR_CHR
660#undef CUR_PATH_SEPARATOR_STR
661#undef PATH_ALLOC_COMBINE
662
663#define DEFINE_UNICODE TRUE
664#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
665#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W
666#define PATH_ALLOC_COMBINE PathAllocCombineW
667#include "include/PathAllocCombine.h"
668#undef DEFINE_UNICODE
669#undef CUR_PATH_SEPARATOR_CHR
670#undef CUR_PATH_SEPARATOR_STR
671#undef PATH_ALLOC_COMBINE
672
673#endif
674
675/* Unix-style Paths */
676
677#define DEFINE_UNICODE FALSE
678#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
679#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR
680#define PATH_ALLOC_COMBINE UnixPathAllocCombineA
681#include "include/PathAllocCombine.h"
682#undef DEFINE_UNICODE
683#undef CUR_PATH_SEPARATOR_CHR
684#undef CUR_PATH_SEPARATOR_STR
685#undef PATH_ALLOC_COMBINE
686
687#define DEFINE_UNICODE TRUE
688#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
689#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W
690#define PATH_ALLOC_COMBINE UnixPathAllocCombineW
691#include "include/PathAllocCombine.h"
692#undef DEFINE_UNICODE
693#undef CUR_PATH_SEPARATOR_CHR
694#undef CUR_PATH_SEPARATOR_STR
695#undef PATH_ALLOC_COMBINE
696
697/* Native-style Paths */
698
699#define DEFINE_UNICODE FALSE
700#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
701#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR
702#define PATH_ALLOC_COMBINE NativePathAllocCombineA
703#include "include/PathAllocCombine.h"
704#undef DEFINE_UNICODE
705#undef CUR_PATH_SEPARATOR_CHR
706#undef CUR_PATH_SEPARATOR_STR
707#undef PATH_ALLOC_COMBINE
708
709#define DEFINE_UNICODE TRUE
710#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
711#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W
712#define PATH_ALLOC_COMBINE NativePathAllocCombineW
713#include "include/PathAllocCombine.h"
714#undef DEFINE_UNICODE
715#undef CUR_PATH_SEPARATOR_CHR
716#undef CUR_PATH_SEPARATOR_STR
717#undef PATH_ALLOC_COMBINE
718
719#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
720
725HRESULT PathCchFindExtensionA(PCSTR pszPath, size_t cchPath, PCSTR* ppszExt)
726{
727 const char* p = (const char*)pszPath;
728
729 if (!pszPath || !cchPath || !ppszExt)
730 return E_INVALIDARG;
731
732 /* find end of string */
733
734 while (*p && --cchPath)
735 {
736 p++;
737 }
738
739 if (*p)
740 {
741 /* pszPath is not null terminated within the cchPath range */
742 return E_INVALIDARG;
743 }
744
745 /* If no extension is found, ppszExt must point to the string's terminating null */
746 *ppszExt = p;
747
748 /* search backwards for '.' */
749
750 while (p > pszPath)
751 {
752 if (*p == '.')
753 {
754 *ppszExt = (PCSTR)p;
755 break;
756 }
757
758 if ((*p == '\\') || (*p == '/') || (*p == ':'))
759 break;
760
761 p--;
762 }
763
764 return S_OK;
765}
766
767HRESULT PathCchFindExtensionW(WINPR_ATTR_UNUSED PCWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
768 WINPR_ATTR_UNUSED PCWSTR* ppszExt)
769{
770 WLog_ERR(TAG, "not implemented");
771 return E_NOTIMPL;
772}
773
778HRESULT PathCchRenameExtensionA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
779 WINPR_ATTR_UNUSED PCSTR pszExt)
780{
781 WLog_ERR(TAG, "not implemented");
782 return E_NOTIMPL;
783}
784
785HRESULT PathCchRenameExtensionW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
786 WINPR_ATTR_UNUSED PCWSTR pszExt)
787{
788 WLog_ERR(TAG, "not implemented");
789 return E_NOTIMPL;
790}
791
796HRESULT PathCchRemoveExtensionA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
797{
798 WLog_ERR(TAG, "not implemented");
799 return E_NOTIMPL;
800}
801
802HRESULT PathCchRemoveExtensionW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
803{
804 WLog_ERR(TAG, "not implemented");
805 return E_NOTIMPL;
806}
807
812BOOL PathCchIsRootA(WINPR_ATTR_UNUSED PCSTR pszPath)
813{
814 WLog_ERR(TAG, "not implemented");
815 return FALSE;
816}
817
818BOOL PathCchIsRootW(WINPR_ATTR_UNUSED PCWSTR pszPath)
819{
820 WLog_ERR(TAG, "not implemented");
821 return FALSE;
822}
823
828BOOL PathIsUNCExA(PCSTR pszPath, PCSTR* ppszServer)
829{
830 if (!pszPath)
831 return FALSE;
832
833 if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
834 {
835 *ppszServer = &pszPath[2];
836 return TRUE;
837 }
838
839 return FALSE;
840}
841
842BOOL PathIsUNCExW(PCWSTR pszPath, PCWSTR* ppszServer)
843{
844 if (!pszPath)
845 return FALSE;
846
847 if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
848 {
849 *ppszServer = &pszPath[2];
850 return TRUE;
851 }
852
853 return FALSE;
854}
855
860HRESULT PathCchSkipRootA(WINPR_ATTR_UNUSED PCSTR pszPath, WINPR_ATTR_UNUSED PCSTR* ppszRootEnd)
861{
862 WLog_ERR(TAG, "not implemented");
863 return E_NOTIMPL;
864}
865
866HRESULT PathCchSkipRootW(WINPR_ATTR_UNUSED PCWSTR pszPath, WINPR_ATTR_UNUSED PCWSTR* ppszRootEnd)
867{
868 WLog_ERR(TAG, "not implemented");
869 return E_NOTIMPL;
870}
871
876HRESULT PathCchStripToRootA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
877{
878 WLog_ERR(TAG, "not implemented");
879 return E_NOTIMPL;
880}
881
882HRESULT PathCchStripToRootW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
883{
884 WLog_ERR(TAG, "not implemented");
885 return E_NOTIMPL;
886}
887
892HRESULT PathCchStripPrefixA(PSTR pszPath, size_t cchPath)
893{
894 BOOL hasPrefix = 0;
895
896 if (!pszPath)
897 return E_INVALIDARG;
898
899 if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
900 return E_INVALIDARG;
901
902 hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
903 (pszPath[3] == '\\'));
904
905 if (hasPrefix)
906 {
907 if (cchPath < 6)
908 return S_FALSE;
909
910 if (IsCharAlpha(pszPath[4]) && (pszPath[5] == ':')) /* like C: */
911 {
912 if (memmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4) < 0)
913 return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
914
915 /* since the passed pszPath must not necessarily be null terminated
916 * and we always have enough space after the strip we can always
917 * ensure the null termination of the stripped result
918 */
919 pszPath[cchPath - 4] = 0;
920 return S_OK;
921 }
922 }
923
924 return S_FALSE;
925}
926
927HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath)
928{
929 BOOL hasPrefix = 0;
930
931 if (!pszPath)
932 return E_INVALIDARG;
933
934 if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
935 return E_INVALIDARG;
936
937 hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
938 (pszPath[3] == '\\'));
939
940 if (hasPrefix)
941 {
942 if (cchPath < 6)
943 return S_FALSE;
944
945 const size_t rc = (_wcslen(&pszPath[4]) + 1);
946 if (cchPath < rc)
947 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
948
949 if (IsCharAlphaW(pszPath[4]) && (pszPath[5] == L':')) /* like C: */
950 {
951 if (wmemmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4) < 0)
952 return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
953 /* since the passed pszPath must not necessarily be null terminated
954 * and we always have enough space after the strip we can always
955 * ensure the null termination of the stripped result
956 */
957 pszPath[cchPath - 4] = 0;
958 return S_OK;
959 }
960 }
961
962 return S_FALSE;
963}
964
969HRESULT PathCchRemoveFileSpecA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
970{
971 WLog_ERR(TAG, "not implemented");
972 return E_NOTIMPL;
973}
974
975HRESULT PathCchRemoveFileSpecW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
976{
977 WLog_ERR(TAG, "not implemented");
978 return E_NOTIMPL;
979}
980#endif
981
982/*
983 * Path Portability Functions
984 */
985
990HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags)
991{
992 if (dwFlags == PATH_STYLE_WINDOWS)
993 {
994 for (size_t index = 0; index < cchPath; index++)
995 {
996 if (pszPath[index] == PATH_SLASH_CHR)
997 pszPath[index] = PATH_BACKSLASH_CHR;
998 }
999 }
1000 else if (dwFlags == PATH_STYLE_UNIX)
1001 {
1002 for (size_t index = 0; index < cchPath; index++)
1003 {
1004 if (pszPath[index] == PATH_BACKSLASH_CHR)
1005 pszPath[index] = PATH_SLASH_CHR;
1006 }
1007 }
1008 else if (dwFlags == PATH_STYLE_NATIVE)
1009 {
1010 if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR)
1011 {
1012 /* Unix-style to Windows-style */
1013
1014 for (size_t index = 0; index < cchPath; index++)
1015 {
1016 if (pszPath[index] == PATH_SLASH_CHR)
1017 pszPath[index] = PATH_BACKSLASH_CHR;
1018 }
1019 }
1020 else if (PATH_SEPARATOR_CHR == PATH_SLASH_CHR)
1021 {
1022 /* Windows-style to Unix-style */
1023
1024 for (size_t index = 0; index < cchPath; index++)
1025 {
1026 if (pszPath[index] == PATH_BACKSLASH_CHR)
1027 pszPath[index] = PATH_SLASH_CHR;
1028 }
1029 }
1030 else
1031 {
1032 /* Unexpected error */
1033 return E_FAIL;
1034 }
1035 }
1036 else
1037 {
1038 /* Gangnam style? */
1039 return E_FAIL;
1040 }
1041
1042 return S_OK;
1043}
1044
1045HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags)
1046{
1047 if (dwFlags == PATH_STYLE_WINDOWS)
1048 {
1049 for (size_t index = 0; index < cchPath; index++)
1050 {
1051 if (pszPath[index] == PATH_SLASH_CHR_W)
1052 pszPath[index] = PATH_BACKSLASH_CHR_W;
1053 }
1054 }
1055 else if (dwFlags == PATH_STYLE_UNIX)
1056 {
1057 for (size_t index = 0; index < cchPath; index++)
1058 {
1059 if (pszPath[index] == PATH_BACKSLASH_CHR_W)
1060 pszPath[index] = PATH_SLASH_CHR_W;
1061 }
1062 }
1063 else if (dwFlags == PATH_STYLE_NATIVE)
1064 {
1065 if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR_W)
1066 {
1067 /* Unix-style to Windows-style */
1068
1069 for (size_t index = 0; index < cchPath; index++)
1070 {
1071 if (pszPath[index] == PATH_SLASH_CHR_W)
1072 pszPath[index] = PATH_BACKSLASH_CHR_W;
1073 }
1074 }
1075 else if (PATH_SEPARATOR_CHR == PATH_SLASH_CHR_W)
1076 {
1077 /* Windows-style to Unix-style */
1078
1079 for (size_t index = 0; index < cchPath; index++)
1080 {
1081 if (pszPath[index] == PATH_BACKSLASH_CHR_W)
1082 pszPath[index] = PATH_SLASH_CHR_W;
1083 }
1084 }
1085 else
1086 {
1087 /* Unexpected error */
1088 return E_FAIL;
1089 }
1090 }
1091 else
1092 {
1093 /* Gangnam style? */
1094 return E_FAIL;
1095 }
1096
1097 return S_OK;
1098}
1099
1104char PathGetSeparatorA(unsigned long dwFlags)
1105{
1106 if (dwFlags == PATH_STYLE_WINDOWS)
1107 return PATH_BACKSLASH_CHR;
1108 if (dwFlags == PATH_STYLE_UNIX)
1109 return PATH_SLASH_CHR;
1110
1111 return PATH_SEPARATOR_CHR;
1112}
1113
1114WCHAR PathGetSeparatorW(unsigned long dwFlags)
1115{
1116 if (dwFlags == PATH_STYLE_WINDOWS)
1117 return PATH_BACKSLASH_CHR_W;
1118 if (dwFlags == PATH_STYLE_UNIX)
1119 return PATH_SLASH_CHR_W;
1120
1121 return PATH_SEPARATOR_CHR;
1122}
1123
1127static const CHAR SharedLibraryExtensionDllA[] = "dll";
1128static const CHAR SharedLibraryExtensionSoA[] = "so";
1129static const CHAR SharedLibraryExtensionDylibA[] = "dylib";
1130
1131static const CHAR SharedLibraryExtensionDotDllA[] = ".dll";
1132static const CHAR SharedLibraryExtensionDotSoA[] = ".so";
1133static const CHAR SharedLibraryExtensionDotDylibA[] = ".dylib";
1134PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags)
1135{
1136 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
1137 {
1138 if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1139 {
1140 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1141 return SharedLibraryExtensionDotDllA;
1142
1143 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1144 return SharedLibraryExtensionDotSoA;
1145
1146 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1147 return SharedLibraryExtensionDotDylibA;
1148 }
1149 else
1150 {
1151 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1152 return SharedLibraryExtensionDllA;
1153
1154 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1155 return SharedLibraryExtensionSoA;
1156
1157 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1158 return SharedLibraryExtensionDylibA;
1159 }
1160 }
1161
1162 if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1163 {
1164#ifdef _WIN32
1165 return SharedLibraryExtensionDotDllA;
1166#elif defined(__APPLE__)
1167 if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
1168 return SharedLibraryExtensionDotSoA;
1169 else
1170 return SharedLibraryExtensionDotDylibA;
1171#else
1172 return SharedLibraryExtensionDotSoA;
1173#endif
1174 }
1175 else
1176 {
1177#ifdef _WIN32
1178 return SharedLibraryExtensionDllA;
1179#elif defined(__APPLE__)
1180 if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
1181 return SharedLibraryExtensionSoA;
1182 else
1183 return SharedLibraryExtensionDylibA;
1184#else
1185 return SharedLibraryExtensionSoA;
1186#endif
1187 }
1188}
1189
1190PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags)
1191{
1192 static WCHAR buffer[6][16] = WINPR_C_ARRAY_INIT;
1193 const WCHAR* SharedLibraryExtensionDotDllW = InitializeConstWCharFromUtf8(
1194 SharedLibraryExtensionDotDllA, buffer[0], ARRAYSIZE(buffer[0]));
1195 const WCHAR* SharedLibraryExtensionDotSoW =
1196 InitializeConstWCharFromUtf8(SharedLibraryExtensionDotSoA, buffer[1], ARRAYSIZE(buffer[1]));
1197 const WCHAR* SharedLibraryExtensionDotDylibW = InitializeConstWCharFromUtf8(
1198 SharedLibraryExtensionDotDylibA, buffer[2], ARRAYSIZE(buffer[2]));
1199 const WCHAR* SharedLibraryExtensionDllW =
1200 InitializeConstWCharFromUtf8(SharedLibraryExtensionDllA, buffer[3], ARRAYSIZE(buffer[3]));
1201 const WCHAR* SharedLibraryExtensionSoW =
1202 InitializeConstWCharFromUtf8(SharedLibraryExtensionSoA, buffer[4], ARRAYSIZE(buffer[4]));
1203 const WCHAR* SharedLibraryExtensionDylibW =
1204 InitializeConstWCharFromUtf8(SharedLibraryExtensionDylibA, buffer[5], ARRAYSIZE(buffer[5]));
1205
1206 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
1207 {
1208 if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1209 {
1210 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1211 return SharedLibraryExtensionDotDllW;
1212
1213 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1214 return SharedLibraryExtensionDotSoW;
1215
1216 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1217 return SharedLibraryExtensionDotDylibW;
1218 }
1219 else
1220 {
1221 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1222 return SharedLibraryExtensionDllW;
1223
1224 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1225 return SharedLibraryExtensionSoW;
1226
1227 if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1228 return SharedLibraryExtensionDylibW;
1229 }
1230 }
1231
1232 if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1233 {
1234#ifdef _WIN32
1235 return SharedLibraryExtensionDotDllW;
1236#elif defined(__APPLE__)
1237 if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
1238 return SharedLibraryExtensionDotSoW;
1239 else
1240 return SharedLibraryExtensionDotDylibW;
1241#else
1242 return SharedLibraryExtensionDotSoW;
1243#endif
1244 }
1245 else
1246 {
1247#ifdef _WIN32
1248 return SharedLibraryExtensionDllW;
1249#elif defined(__APPLE__)
1250 if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
1251 return SharedLibraryExtensionSoW;
1252 else
1253 return SharedLibraryExtensionDylibW;
1254#else
1255 return SharedLibraryExtensionSoW;
1256#endif
1257 }
1258}
1259
1260const char* GetKnownPathIdString(int id)
1261{
1262 switch (id)
1263 {
1264 case KNOWN_PATH_HOME:
1265 return "KNOWN_PATH_HOME";
1266 case KNOWN_PATH_TEMP:
1267 return "KNOWN_PATH_TEMP";
1268 case KNOWN_PATH_XDG_DATA_HOME:
1269 return "KNOWN_PATH_XDG_DATA_HOME";
1270 case KNOWN_PATH_XDG_CONFIG_HOME:
1271 return "KNOWN_PATH_XDG_CONFIG_HOME";
1272 case KNOWN_PATH_XDG_CACHE_HOME:
1273 return "KNOWN_PATH_XDG_CACHE_HOME";
1274 case KNOWN_PATH_XDG_RUNTIME_DIR:
1275 return "KNOWN_PATH_XDG_RUNTIME_DIR";
1276 case KNOWN_PATH_SYSTEM_CONFIG_HOME:
1277 return "KNOWN_PATH_SYSTEM_CONFIG_HOME";
1278 default:
1279 return "KNOWN_PATH_UNKNOWN_ID";
1280 }
1281}
1282
1283static char* concat(const char* path, size_t pathlen, const char* name, size_t namelen)
1284{
1285 const size_t strsize = pathlen + namelen + 2;
1286 char* str = calloc(strsize, sizeof(char));
1287 if (!str)
1288 return nullptr;
1289
1290 winpr_str_append(path, str, strsize, "");
1291 winpr_str_append(name, str, strsize, "");
1292 return str;
1293}
1294
1295BOOL winpr_RemoveDirectory_RecursiveA(LPCSTR lpPathName)
1296{
1297 BOOL ret = FALSE;
1298
1299 if (!lpPathName)
1300 return FALSE;
1301
1302 const size_t pathnamelen = strlen(lpPathName);
1303 const size_t path_slash_len = pathnamelen + 3;
1304 char* path_slash = calloc(pathnamelen + 4, sizeof(char));
1305 if (!path_slash)
1306 return FALSE;
1307 strncat(path_slash, lpPathName, pathnamelen);
1308
1309 const char star[] = "*";
1310 const HRESULT hr = NativePathCchAppendA(path_slash, path_slash_len, star);
1311 HANDLE dir = INVALID_HANDLE_VALUE;
1312 if (FAILED(hr))
1313 goto fail;
1314
1315 {
1316 WIN32_FIND_DATAA findFileData = WINPR_C_ARRAY_INIT;
1317 dir = FindFirstFileA(path_slash, &findFileData);
1318
1319 if (dir == INVALID_HANDLE_VALUE)
1320 goto fail;
1321
1322 ret = TRUE;
1323 path_slash[path_slash_len - 1] = '\0'; /* remove trailing '*' */
1324 do
1325 {
1326 const size_t len = strnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
1327
1328 if ((len == 1 && findFileData.cFileName[0] == '.') ||
1329 (len == 2 && findFileData.cFileName[0] == '.' && findFileData.cFileName[1] == '.'))
1330 {
1331 continue;
1332 }
1333
1334 char* fullpath = concat(path_slash, path_slash_len, findFileData.cFileName, len);
1335 if (!fullpath)
1336 goto fail;
1337
1338 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1339 ret = winpr_RemoveDirectory_RecursiveA(fullpath);
1340 else
1341 {
1342 WINPR_PRAGMA_DIAG_PUSH
1343 WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
1344 ret = winpr_DeleteFile(fullpath);
1345 WINPR_PRAGMA_DIAG_POP
1346 }
1347
1348 free(fullpath);
1349
1350 if (!ret)
1351 break;
1352 } while (ret && FindNextFileA(dir, &findFileData) != 0);
1353 }
1354
1355 if (ret)
1356 {
1357 if (!winpr_RemoveDirectory(lpPathName))
1358 ret = FALSE;
1359 }
1360
1361fail:
1362 FindClose(dir);
1363 free(path_slash);
1364 return ret;
1365}
1366
1367BOOL winpr_RemoveDirectory_RecursiveW(LPCWSTR lpPathName)
1368{
1369 char* name = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1370 if (!name)
1371 return FALSE;
1372 const BOOL rc = winpr_RemoveDirectory_RecursiveA(name);
1373 free(name);
1374 return rc;
1375}
1376
1377char* winpr_GetConfigFilePathVA(BOOL system, WINPR_FORMAT_ARG const char* filename, va_list ap)
1378{
1379 eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME;
1380 const char* vendor = winpr_getApplicationDetailsVendor();
1381 const char* product = winpr_getApplicationDetailsProduct();
1382 const SSIZE_T version = winpr_getApplicationDetailsVersion();
1383
1384 if (!vendor || !product)
1385 return nullptr;
1386
1387 char* config = GetKnownSubPathV(id, "%s", vendor);
1388 if (!config)
1389 return nullptr;
1390
1391 char* base = nullptr;
1392 if (version < 0)
1393 base = GetCombinedPathV(config, "%s", product);
1394 else
1395 base = GetCombinedPathV(config, "%s%" PRIdz, product, version);
1396 free(config);
1397
1398 if (!base)
1399 return nullptr;
1400 char* path = GetCombinedPathVA(base, filename, ap);
1401 free(base);
1402
1403 return path;
1404}
1405
1406char* winpr_GetConfigFilePath(BOOL system, const char* filename)
1407{
1408 if (!filename)
1409 return winpr_GetConfigFilePathV(system, "%s", "");
1410 return winpr_GetConfigFilePathV(system, "%s", filename);
1411}
1412
1413char* winpr_GetConfigFilePathV(BOOL system, const char* filename, ...)
1414{
1415 va_list ap = WINPR_C_ARRAY_INIT;
1416 va_start(ap, filename);
1417 char* str = winpr_GetConfigFilePathVA(system, filename, ap);
1418 va_end(ap);
1419 return str;
1420}