Coverage Report

Created: 2026-08-31 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/path/path.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Path Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
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
41
static const char PATH_SLASH_CHR = '/';
42
static const char PATH_SLASH_STR[] = "/";
43
44
static const char PATH_BACKSLASH_CHR = '\\';
45
46
#ifdef _WIN32
47
static const char PATH_BACKSLASH_STR[] = "\\";
48
static const WCHAR PATH_BACKSLASH_STR_W[] = L"\\";
49
static const WCHAR PATH_SLASH_CHR_W = L'/';
50
static const WCHAR PATH_BACKSLASH_CHR_W = L'\\';
51
static const WCHAR PATH_SLASH_STR_W[] = L"/";
52
#else
53
#if defined(__BIG_ENDIAN__)
54
static const WCHAR PATH_SLASH_CHR_W = 0x2f00;
55
static const WCHAR PATH_BACKSLASH_CHR_W = 0x5c00;
56
static const WCHAR PATH_SLASH_STR_W[] = { 0x2f00, '\0' };
57
#else
58
static const WCHAR PATH_SLASH_CHR_W = '/';
59
static const WCHAR PATH_BACKSLASH_CHR_W = '\\';
60
static const WCHAR PATH_SLASH_STR_W[] = { '/', '\0' };
61
#endif
62
63
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
64
#if defined(__BIG_ENDIAN__)
65
static const WCHAR PATH_BACKSLASH_STR_W[] = { 0x5c00, '\0' };
66
#else
67
static const WCHAR PATH_BACKSLASH_STR_W[] = { '\\', '\0' };
68
#endif
69
static 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
204k
#define PATH_SEPARATOR_CHR PATH_SLASH_CHR
81
34.0k
#define PATH_SEPARATOR_STR PATH_SLASH_STR
82
0
#define PATH_SEPARATOR_CHR_W PATH_SLASH_CHR_W
83
0
#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
0
#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
0
#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
0
#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
0
#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
0
#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
0
#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
154
HRESULT PathCchRemoveBackslashA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
155
0
{
156
0
  WLog_ERR(TAG, "not implemented");
157
0
  return E_NOTIMPL;
158
0
}
159
160
HRESULT PathCchRemoveBackslashW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
161
0
{
162
0
  WLog_ERR(TAG, "not implemented");
163
0
  return E_NOTIMPL;
164
0
}
165
166
/*
167
 * PathCchAddBackslashEx
168
 */
169
170
/* Windows-style Paths */
171
172
#define DEFINE_UNICODE FALSE
173
0
#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
0
#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
0
#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
0
#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
0
#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
0
#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
224
HRESULT 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
0
{
228
0
  WLog_ERR(TAG, "not implemented");
229
0
  return E_NOTIMPL;
230
0
}
231
232
HRESULT 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
0
{
236
0
  WLog_ERR(TAG, "not implemented");
237
0
  return E_NOTIMPL;
238
0
}
239
240
/*
241
 * PathCchAddExtension
242
 */
243
244
/* Windows-style Paths */
245
246
#define DEFINE_UNICODE FALSE
247
0
#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
0
#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
0
#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
0
#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
0
#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
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
308
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
318
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
330
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
340
0
#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
68.1k
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
353
34.0k
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
363
0
#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
376
HRESULT 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
0
{
379
0
  WLog_ERR(TAG, "not implemented");
380
0
  return E_NOTIMPL;
381
0
}
382
383
HRESULT 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
0
{
386
0
  WLog_ERR(TAG, "not implemented");
387
0
  return E_NOTIMPL;
388
0
}
389
390
#endif
391
392
#if !defined(_WIN32)
393
394
#if !defined(WITH_CWALK)
395
static void replace(char* str, size_t slen, const char* pattern)
396
0
{
397
0
  const size_t len = strlen(pattern);
398
0
  while (TRUE)
399
0
  {
400
0
    char* cur = strstr(str, pattern);
401
0
    if (!cur)
402
0
      return;
403
404
0
    const char* src = &cur[len];
405
    /* Ensure terminating '\0' is moved as well */
406
0
    const size_t rem = strnlen(src, slen) + 1;
407
0
    memmove(&cur[1], src, rem);
408
0
  }
409
0
}
410
411
WINPR_ATTR_NODISCARD
412
static BOOL replace_dotdot(char* str, size_t slen)
413
0
{
414
0
  const char pattern[] = "/../";
415
0
  const size_t len = strlen(pattern);
416
0
  while (TRUE)
417
0
  {
418
0
    char* cur = strstr(str, pattern);
419
0
    if (!cur)
420
0
      return TRUE;
421
422
0
    char* start = cur;
423
0
    const char* end = &cur[len];
424
0
    while (start > str)
425
0
    {
426
0
      start--;
427
0
      if (*start == '/')
428
0
        break;
429
0
    }
430
0
    if (*start != '/')
431
0
      return FALSE;
432
433
    /* Ensure terminating '\0' is moved as well */
434
0
    const size_t rem = strnlen(end, slen) + 1;
435
0
    memmove(&start[1], end, rem);
436
0
  }
437
0
}
438
439
WINPR_ATTR_NODISCARD
440
static BOOL replace_trailing_dotdot(char* str, size_t slen)
441
0
{
442
0
  const size_t len = strnlen(str, slen);
443
0
  if (len < 3)
444
0
    return TRUE;
445
446
0
  if (strcmp(&str[len - 3], "/..") != 0)
447
0
    return TRUE;
448
449
0
  char* start = &str[len - 4];
450
0
  while (start >= str)
451
0
  {
452
0
    if (*start == '/')
453
0
    {
454
0
      start[1] = '\0';
455
0
      return TRUE;
456
0
    }
457
0
    start--;
458
0
  }
459
0
  return FALSE;
460
0
}
461
462
#endif
463
464
WINPR_ATTR_NODISCARD
465
char* winpr_PathCanonicalize(const char* path)
466
0
{
467
0
  WINPR_ASSERT(path);
468
469
0
  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
0
  char* str = strndup(path, len);
478
0
  if (!str)
479
0
    return nullptr;
480
0
  replace(str, len, "/./");
481
0
  replace(str, len, "//");
482
0
  if (!replace_dotdot(str, len) || !replace_trailing_dotdot(str, len))
483
0
  {
484
0
    free(str);
485
0
    return nullptr;
486
0
  }
487
488
0
  size_t slen = 0;
489
0
  while ((slen = strnlen(str, len)) > 1)
490
0
  {
491
0
    if ((str[slen - 1] == '.') || (str[slen - 1] == '/'))
492
0
      str[slen - 1] = '\0';
493
0
    else
494
0
      break;
495
0
  }
496
497
0
  return str;
498
0
#endif
499
0
}
500
501
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
502
/*
503
 * PathCchCanonicalize
504
 */
505
506
HRESULT PathCchCanonicalizeA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn)
507
0
{
508
0
  char* out = winpr_PathCanonicalize(pszPathIn);
509
0
  if (!out)
510
0
    return E_OUTOFMEMORY;
511
0
  const size_t len = strnlen(out, cchPathOut);
512
0
  if (len >= cchPathOut)
513
0
  {
514
0
    free(out);
515
0
    return E_INVALIDARG;
516
0
  }
517
0
  strncpy(pszPathOut, out, cchPathOut);
518
0
  free(out);
519
0
  return S_OK;
520
0
}
521
522
HRESULT PathCchCanonicalizeW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn)
523
0
{
524
0
  if (!pszPathIn)
525
0
    return E_OUTOFMEMORY;
526
0
  char* str = ConvertWCharToUtf8Alloc(pszPathIn, nullptr);
527
0
  if (!str)
528
0
    return E_OUTOFMEMORY;
529
530
0
  char* out = calloc(cchPathOut, sizeof(CHAR));
531
0
  if (!out)
532
0
  {
533
0
    free(str);
534
0
    return E_OUTOFMEMORY;
535
0
  }
536
537
0
  HRESULT hr = PathCchCanonicalizeA(out, cchPathOut, str);
538
0
  free(str);
539
0
  (void)ConvertUtf8NToWChar(out, cchPathOut, pszPathOut, cchPathOut);
540
0
  free(out);
541
0
  return hr;
542
0
}
543
544
/*
545
 * PathCchCanonicalizeEx
546
 */
547
548
HRESULT 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
0
{
553
0
  if (dwFlags != 0)
554
0
    WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
555
0
  return PathCchCanonicalizeA(pszPathOut, cchPathOut, pszPathIn);
556
0
}
557
558
HRESULT 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
0
{
563
0
  if (dwFlags != 0)
564
0
    WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
565
0
  return PathCchCanonicalizeW(pszPathOut, cchPathOut, pszPathIn);
566
0
}
567
568
/*
569
 * PathAllocCanonicalize
570
 */
571
572
HRESULT PathAllocCanonicalizeA(PCSTR pszPathIn, unsigned long dwFlags, PSTR* ppszPathOut)
573
0
{
574
0
  if (!ppszPathOut)
575
0
    return E_INVALIDARG;
576
0
  if (!pszPathIn)
577
0
    return E_OUTOFMEMORY;
578
0
  if (dwFlags != 0)
579
0
    WLog_WARN(TAG, "flags 0x%08lx not implemented", dwFlags);
580
0
  *ppszPathOut = winpr_PathCanonicalize(pszPathIn);
581
0
  if (!*ppszPathOut)
582
0
    return E_OUTOFMEMORY;
583
0
  return S_OK;
584
0
}
585
586
HRESULT PathAllocCanonicalizeW(PCWSTR pszPathIn, unsigned long dwFlags, PWSTR* ppszPathOut)
587
0
{
588
0
  if (!ppszPathOut)
589
0
    return E_INVALIDARG;
590
0
  if (!pszPathIn)
591
0
    return E_OUTOFMEMORY;
592
0
  char* str = ConvertWCharToUtf8Alloc(pszPathIn, nullptr);
593
0
  if (!str)
594
0
    return E_OUTOFMEMORY;
595
596
0
  char* out = nullptr;
597
0
  HRESULT hr = PathAllocCanonicalizeA(str, dwFlags, &out);
598
0
  if (out)
599
0
    *ppszPathOut = ConvertUtf8ToWCharAlloc(out, nullptr);
600
0
  free(str);
601
0
  free(out);
602
0
  return hr;
603
0
}
604
605
#endif
606
#endif
607
608
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
609
/*
610
 * PathCchCombine
611
 */
612
613
HRESULT PathCchCombineA(WINPR_ATTR_UNUSED PSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
614
                        WINPR_ATTR_UNUSED PCSTR pszPathIn, WINPR_ATTR_UNUSED PCSTR pszMore)
615
0
{
616
0
  WLog_ERR(TAG, "not implemented");
617
0
  return E_NOTIMPL;
618
0
}
619
620
HRESULT PathCchCombineW(WINPR_ATTR_UNUSED PWSTR pszPathOut, WINPR_ATTR_UNUSED size_t cchPathOut,
621
                        WINPR_ATTR_UNUSED PCWSTR pszPathIn, WINPR_ATTR_UNUSED PCWSTR pszMore)
622
0
{
623
0
  WLog_ERR(TAG, "not implemented");
624
0
  return E_NOTIMPL;
625
0
}
626
627
/*
628
 * PathCchCombineEx
629
 */
630
631
HRESULT 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
0
{
635
0
  WLog_ERR(TAG, "not implemented");
636
0
  return E_NOTIMPL;
637
0
}
638
639
HRESULT 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
0
{
643
0
  WLog_ERR(TAG, "not implemented");
644
0
  return E_NOTIMPL;
645
0
}
646
647
/*
648
 * PathAllocCombine
649
 */
650
651
/* Windows-style Paths */
652
653
#define DEFINE_UNICODE FALSE
654
0
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
655
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR_W
665
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
679
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR_W
689
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
701
0
#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
0
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR_W
711
0
#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
721
/**
722
 * PathCchFindExtension
723
 */
724
725
HRESULT PathCchFindExtensionA(PCSTR pszPath, size_t cchPath, PCSTR* ppszExt)
726
0
{
727
0
  const char* p = (const char*)pszPath;
728
729
0
  if (!pszPath || !cchPath || !ppszExt)
730
0
    return E_INVALIDARG;
731
732
  /* find end of string */
733
734
0
  while (*p && --cchPath)
735
0
  {
736
0
    p++;
737
0
  }
738
739
0
  if (*p)
740
0
  {
741
    /* pszPath is not null terminated within the cchPath range */
742
0
    return E_INVALIDARG;
743
0
  }
744
745
  /* If no extension is found, ppszExt must point to the string's terminating null */
746
0
  *ppszExt = p;
747
748
  /* search backwards for '.' */
749
750
0
  while (p > pszPath)
751
0
  {
752
0
    if (*p == '.')
753
0
    {
754
0
      *ppszExt = (PCSTR)p;
755
0
      break;
756
0
    }
757
758
0
    if ((*p == '\\') || (*p == '/') || (*p == ':'))
759
0
      break;
760
761
0
    p--;
762
0
  }
763
764
0
  return S_OK;
765
0
}
766
767
HRESULT PathCchFindExtensionW(WINPR_ATTR_UNUSED PCWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
768
                              WINPR_ATTR_UNUSED PCWSTR* ppszExt)
769
0
{
770
0
  WLog_ERR(TAG, "not implemented");
771
0
  return E_NOTIMPL;
772
0
}
773
774
/**
775
 * PathCchRenameExtension
776
 */
777
778
HRESULT PathCchRenameExtensionA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
779
                                WINPR_ATTR_UNUSED PCSTR pszExt)
780
0
{
781
0
  WLog_ERR(TAG, "not implemented");
782
0
  return E_NOTIMPL;
783
0
}
784
785
HRESULT PathCchRenameExtensionW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath,
786
                                WINPR_ATTR_UNUSED PCWSTR pszExt)
787
0
{
788
0
  WLog_ERR(TAG, "not implemented");
789
0
  return E_NOTIMPL;
790
0
}
791
792
/**
793
 * PathCchRemoveExtension
794
 */
795
796
HRESULT PathCchRemoveExtensionA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
797
0
{
798
0
  WLog_ERR(TAG, "not implemented");
799
0
  return E_NOTIMPL;
800
0
}
801
802
HRESULT PathCchRemoveExtensionW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
803
0
{
804
0
  WLog_ERR(TAG, "not implemented");
805
0
  return E_NOTIMPL;
806
0
}
807
808
/**
809
 * PathCchIsRoot
810
 */
811
812
BOOL PathCchIsRootA(WINPR_ATTR_UNUSED PCSTR pszPath)
813
0
{
814
0
  WLog_ERR(TAG, "not implemented");
815
0
  return FALSE;
816
0
}
817
818
BOOL PathCchIsRootW(WINPR_ATTR_UNUSED PCWSTR pszPath)
819
0
{
820
0
  WLog_ERR(TAG, "not implemented");
821
0
  return FALSE;
822
0
}
823
824
/**
825
 * PathIsUNCEx
826
 */
827
828
BOOL PathIsUNCExA(PCSTR pszPath, PCSTR* ppszServer)
829
0
{
830
0
  if (!pszPath)
831
0
    return FALSE;
832
833
0
  if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
834
0
  {
835
0
    *ppszServer = &pszPath[2];
836
0
    return TRUE;
837
0
  }
838
839
0
  return FALSE;
840
0
}
841
842
BOOL PathIsUNCExW(PCWSTR pszPath, PCWSTR* ppszServer)
843
0
{
844
0
  if (!pszPath)
845
0
    return FALSE;
846
847
0
  if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
848
0
  {
849
0
    *ppszServer = &pszPath[2];
850
0
    return TRUE;
851
0
  }
852
853
0
  return FALSE;
854
0
}
855
856
/**
857
 * PathCchSkipRoot
858
 */
859
860
HRESULT PathCchSkipRootA(WINPR_ATTR_UNUSED PCSTR pszPath, WINPR_ATTR_UNUSED PCSTR* ppszRootEnd)
861
0
{
862
0
  WLog_ERR(TAG, "not implemented");
863
0
  return E_NOTIMPL;
864
0
}
865
866
HRESULT PathCchSkipRootW(WINPR_ATTR_UNUSED PCWSTR pszPath, WINPR_ATTR_UNUSED PCWSTR* ppszRootEnd)
867
0
{
868
0
  WLog_ERR(TAG, "not implemented");
869
0
  return E_NOTIMPL;
870
0
}
871
872
/**
873
 * PathCchStripToRoot
874
 */
875
876
HRESULT PathCchStripToRootA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
877
0
{
878
0
  WLog_ERR(TAG, "not implemented");
879
0
  return E_NOTIMPL;
880
0
}
881
882
HRESULT PathCchStripToRootW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
883
0
{
884
0
  WLog_ERR(TAG, "not implemented");
885
0
  return E_NOTIMPL;
886
0
}
887
888
/**
889
 * PathCchStripPrefix
890
 */
891
892
HRESULT PathCchStripPrefixA(PSTR pszPath, size_t cchPath)
893
0
{
894
0
  BOOL hasPrefix = 0;
895
896
0
  if (!pszPath)
897
0
    return E_INVALIDARG;
898
899
0
  if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
900
0
    return E_INVALIDARG;
901
902
0
  hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
903
0
               (pszPath[3] == '\\'));
904
905
0
  if (hasPrefix)
906
0
  {
907
0
    if (cchPath < 6)
908
0
      return S_FALSE;
909
910
0
    if (IsCharAlpha(pszPath[4]) && (pszPath[5] == ':')) /* like C: */
911
0
    {
912
0
      if (memmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4) < 0)
913
0
        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
0
      pszPath[cchPath - 4] = 0;
920
0
      return S_OK;
921
0
    }
922
0
  }
923
924
0
  return S_FALSE;
925
0
}
926
927
HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath)
928
0
{
929
0
  BOOL hasPrefix = 0;
930
931
0
  if (!pszPath)
932
0
    return E_INVALIDARG;
933
934
0
  if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
935
0
    return E_INVALIDARG;
936
937
0
  hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
938
0
               (pszPath[3] == '\\'));
939
940
0
  if (hasPrefix)
941
0
  {
942
0
    if (cchPath < 6)
943
0
      return S_FALSE;
944
945
0
    const size_t rc = (_wcslen(&pszPath[4]) + 1);
946
0
    if (cchPath < rc)
947
0
      return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
948
949
0
    if (IsCharAlphaW(pszPath[4]) && (pszPath[5] == L':')) /* like C: */
950
0
    {
951
0
      if (wmemmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4) < 0)
952
0
        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
0
      pszPath[cchPath - 4] = 0;
958
0
      return S_OK;
959
0
    }
960
0
  }
961
962
0
  return S_FALSE;
963
0
}
964
965
/**
966
 * PathCchRemoveFileSpec
967
 */
968
969
HRESULT PathCchRemoveFileSpecA(WINPR_ATTR_UNUSED PSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
970
0
{
971
0
  WLog_ERR(TAG, "not implemented");
972
0
  return E_NOTIMPL;
973
0
}
974
975
HRESULT PathCchRemoveFileSpecW(WINPR_ATTR_UNUSED PWSTR pszPath, WINPR_ATTR_UNUSED size_t cchPath)
976
0
{
977
0
  WLog_ERR(TAG, "not implemented");
978
0
  return E_NOTIMPL;
979
0
}
980
#endif
981
982
/*
983
 * Path Portability Functions
984
 */
985
986
/**
987
 * PathCchConvertStyle
988
 */
989
990
HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags)
991
68.1k
{
992
68.1k
  if (dwFlags == PATH_STYLE_WINDOWS)
993
0
  {
994
0
    for (size_t index = 0; index < cchPath; index++)
995
0
    {
996
0
      if (pszPath[index] == PATH_SLASH_CHR)
997
0
        pszPath[index] = PATH_BACKSLASH_CHR;
998
0
    }
999
0
  }
1000
68.1k
  else if (dwFlags == PATH_STYLE_UNIX)
1001
0
  {
1002
0
    for (size_t index = 0; index < cchPath; index++)
1003
0
    {
1004
0
      if (pszPath[index] == PATH_BACKSLASH_CHR)
1005
0
        pszPath[index] = PATH_SLASH_CHR;
1006
0
    }
1007
0
  }
1008
68.1k
  else if (dwFlags == PATH_STYLE_NATIVE)
1009
68.1k
  {
1010
68.1k
    if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR)
1011
0
    {
1012
      /* Unix-style to Windows-style */
1013
1014
0
      for (size_t index = 0; index < cchPath; index++)
1015
0
      {
1016
0
        if (pszPath[index] == PATH_SLASH_CHR)
1017
0
          pszPath[index] = PATH_BACKSLASH_CHR;
1018
0
      }
1019
0
    }
1020
68.1k
    else if (PATH_SEPARATOR_CHR == PATH_SLASH_CHR)
1021
68.1k
    {
1022
      /* Windows-style to Unix-style */
1023
1024
1.06M
      for (size_t index = 0; index < cchPath; index++)
1025
999k
      {
1026
999k
        if (pszPath[index] == PATH_BACKSLASH_CHR)
1027
0
          pszPath[index] = PATH_SLASH_CHR;
1028
999k
      }
1029
68.1k
    }
1030
0
    else
1031
0
    {
1032
      /* Unexpected error */
1033
0
      return E_FAIL;
1034
0
    }
1035
68.1k
  }
1036
0
  else
1037
0
  {
1038
    /* Gangnam style? */
1039
0
    return E_FAIL;
1040
0
  }
1041
1042
68.1k
  return S_OK;
1043
68.1k
}
1044
1045
HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags)
1046
0
{
1047
0
  if (dwFlags == PATH_STYLE_WINDOWS)
1048
0
  {
1049
0
    for (size_t index = 0; index < cchPath; index++)
1050
0
    {
1051
0
      if (pszPath[index] == PATH_SLASH_CHR_W)
1052
0
        pszPath[index] = PATH_BACKSLASH_CHR_W;
1053
0
    }
1054
0
  }
1055
0
  else if (dwFlags == PATH_STYLE_UNIX)
1056
0
  {
1057
0
    for (size_t index = 0; index < cchPath; index++)
1058
0
    {
1059
0
      if (pszPath[index] == PATH_BACKSLASH_CHR_W)
1060
0
        pszPath[index] = PATH_SLASH_CHR_W;
1061
0
    }
1062
0
  }
1063
0
  else if (dwFlags == PATH_STYLE_NATIVE)
1064
0
  {
1065
0
    if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR_W)
1066
0
    {
1067
      /* Unix-style to Windows-style */
1068
1069
0
      for (size_t index = 0; index < cchPath; index++)
1070
0
      {
1071
0
        if (pszPath[index] == PATH_SLASH_CHR_W)
1072
0
          pszPath[index] = PATH_BACKSLASH_CHR_W;
1073
0
      }
1074
0
    }
1075
0
    else if (PATH_SEPARATOR_CHR == PATH_SLASH_CHR_W)
1076
0
    {
1077
      /* Windows-style to Unix-style */
1078
1079
0
      for (size_t index = 0; index < cchPath; index++)
1080
0
      {
1081
0
        if (pszPath[index] == PATH_BACKSLASH_CHR_W)
1082
0
          pszPath[index] = PATH_SLASH_CHR_W;
1083
0
      }
1084
0
    }
1085
0
    else
1086
0
    {
1087
      /* Unexpected error */
1088
0
      return E_FAIL;
1089
0
    }
1090
0
  }
1091
0
  else
1092
0
  {
1093
    /* Gangnam style? */
1094
0
    return E_FAIL;
1095
0
  }
1096
1097
0
  return S_OK;
1098
0
}
1099
1100
/**
1101
 * PathGetSeparator
1102
 */
1103
1104
char PathGetSeparatorA(unsigned long dwFlags)
1105
0
{
1106
0
  if (dwFlags == PATH_STYLE_WINDOWS)
1107
0
    return PATH_BACKSLASH_CHR;
1108
0
  if (dwFlags == PATH_STYLE_UNIX)
1109
0
    return PATH_SLASH_CHR;
1110
1111
0
  return PATH_SEPARATOR_CHR;
1112
0
}
1113
1114
WCHAR PathGetSeparatorW(unsigned long dwFlags)
1115
0
{
1116
0
  if (dwFlags == PATH_STYLE_WINDOWS)
1117
0
    return PATH_BACKSLASH_CHR_W;
1118
0
  if (dwFlags == PATH_STYLE_UNIX)
1119
0
    return PATH_SLASH_CHR_W;
1120
1121
0
  return PATH_SEPARATOR_CHR;
1122
0
}
1123
1124
/**
1125
 * PathGetSharedLibraryExtension
1126
 */
1127
static const CHAR SharedLibraryExtensionDllA[] = "dll";
1128
static const CHAR SharedLibraryExtensionSoA[] = "so";
1129
static const CHAR SharedLibraryExtensionDylibA[] = "dylib";
1130
1131
static const CHAR SharedLibraryExtensionDotDllA[] = ".dll";
1132
static const CHAR SharedLibraryExtensionDotSoA[] = ".so";
1133
static const CHAR SharedLibraryExtensionDotDylibA[] = ".dylib";
1134
PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags)
1135
0
{
1136
0
  if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
1137
0
  {
1138
0
    if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1139
0
    {
1140
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1141
0
        return SharedLibraryExtensionDotDllA;
1142
1143
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1144
0
        return SharedLibraryExtensionDotSoA;
1145
1146
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1147
0
        return SharedLibraryExtensionDotDylibA;
1148
0
    }
1149
0
    else
1150
0
    {
1151
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1152
0
        return SharedLibraryExtensionDllA;
1153
1154
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1155
0
        return SharedLibraryExtensionSoA;
1156
1157
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1158
0
        return SharedLibraryExtensionDylibA;
1159
0
    }
1160
0
  }
1161
1162
0
  if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1163
0
  {
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
0
    return SharedLibraryExtensionDotSoA;
1173
0
#endif
1174
0
  }
1175
0
  else
1176
0
  {
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
0
    return SharedLibraryExtensionSoA;
1186
0
#endif
1187
0
  }
1188
0
}
1189
1190
PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags)
1191
0
{
1192
0
  static WCHAR buffer[6][16] = WINPR_C_ARRAY_INIT;
1193
0
  const WCHAR* SharedLibraryExtensionDotDllW = InitializeConstWCharFromUtf8(
1194
0
      SharedLibraryExtensionDotDllA, buffer[0], ARRAYSIZE(buffer[0]));
1195
0
  const WCHAR* SharedLibraryExtensionDotSoW =
1196
0
      InitializeConstWCharFromUtf8(SharedLibraryExtensionDotSoA, buffer[1], ARRAYSIZE(buffer[1]));
1197
0
  const WCHAR* SharedLibraryExtensionDotDylibW = InitializeConstWCharFromUtf8(
1198
0
      SharedLibraryExtensionDotDylibA, buffer[2], ARRAYSIZE(buffer[2]));
1199
0
  const WCHAR* SharedLibraryExtensionDllW =
1200
0
      InitializeConstWCharFromUtf8(SharedLibraryExtensionDllA, buffer[3], ARRAYSIZE(buffer[3]));
1201
0
  const WCHAR* SharedLibraryExtensionSoW =
1202
0
      InitializeConstWCharFromUtf8(SharedLibraryExtensionSoA, buffer[4], ARRAYSIZE(buffer[4]));
1203
0
  const WCHAR* SharedLibraryExtensionDylibW =
1204
0
      InitializeConstWCharFromUtf8(SharedLibraryExtensionDylibA, buffer[5], ARRAYSIZE(buffer[5]));
1205
1206
0
  if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
1207
0
  {
1208
0
    if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1209
0
    {
1210
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1211
0
        return SharedLibraryExtensionDotDllW;
1212
1213
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1214
0
        return SharedLibraryExtensionDotSoW;
1215
1216
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1217
0
        return SharedLibraryExtensionDotDylibW;
1218
0
    }
1219
0
    else
1220
0
    {
1221
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
1222
0
        return SharedLibraryExtensionDllW;
1223
1224
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
1225
0
        return SharedLibraryExtensionSoW;
1226
1227
0
      if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
1228
0
        return SharedLibraryExtensionDylibW;
1229
0
    }
1230
0
  }
1231
1232
0
  if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
1233
0
  {
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
0
    return SharedLibraryExtensionDotSoW;
1243
0
#endif
1244
0
  }
1245
0
  else
1246
0
  {
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
0
    return SharedLibraryExtensionSoW;
1256
0
#endif
1257
0
  }
1258
0
}
1259
1260
const char* GetKnownPathIdString(int id)
1261
0
{
1262
0
  switch (id)
1263
0
  {
1264
0
    case KNOWN_PATH_HOME:
1265
0
      return "KNOWN_PATH_HOME";
1266
0
    case KNOWN_PATH_TEMP:
1267
0
      return "KNOWN_PATH_TEMP";
1268
0
    case KNOWN_PATH_XDG_DATA_HOME:
1269
0
      return "KNOWN_PATH_XDG_DATA_HOME";
1270
0
    case KNOWN_PATH_XDG_CONFIG_HOME:
1271
0
      return "KNOWN_PATH_XDG_CONFIG_HOME";
1272
0
    case KNOWN_PATH_XDG_CACHE_HOME:
1273
0
      return "KNOWN_PATH_XDG_CACHE_HOME";
1274
0
    case KNOWN_PATH_XDG_RUNTIME_DIR:
1275
0
      return "KNOWN_PATH_XDG_RUNTIME_DIR";
1276
0
    case KNOWN_PATH_SYSTEM_CONFIG_HOME:
1277
0
      return "KNOWN_PATH_SYSTEM_CONFIG_HOME";
1278
0
    default:
1279
0
      return "KNOWN_PATH_UNKNOWN_ID";
1280
0
  }
1281
0
}
1282
1283
static char* concat(const char* path, size_t pathlen, const char* name, size_t namelen)
1284
0
{
1285
0
  const size_t strsize = pathlen + namelen + 2;
1286
0
  char* str = calloc(strsize, sizeof(char));
1287
0
  if (!str)
1288
0
    return nullptr;
1289
1290
0
  winpr_str_append(path, str, strsize, "");
1291
0
  winpr_str_append(name, str, strsize, "");
1292
0
  return str;
1293
0
}
1294
1295
BOOL winpr_RemoveDirectory_RecursiveA(LPCSTR lpPathName)
1296
0
{
1297
0
  BOOL ret = FALSE;
1298
1299
0
  if (!lpPathName)
1300
0
    return FALSE;
1301
1302
0
  const size_t pathnamelen = strlen(lpPathName);
1303
0
  const size_t path_slash_len = pathnamelen + 3;
1304
0
  char* path_slash = calloc(pathnamelen + 4, sizeof(char));
1305
0
  if (!path_slash)
1306
0
    return FALSE;
1307
0
  strncat(path_slash, lpPathName, pathnamelen);
1308
1309
0
  const char star[] = "*";
1310
0
  const HRESULT hr = NativePathCchAppendA(path_slash, path_slash_len, star);
1311
0
  HANDLE dir = INVALID_HANDLE_VALUE;
1312
0
  if (FAILED(hr))
1313
0
    goto fail;
1314
1315
0
  {
1316
0
    WIN32_FIND_DATAA findFileData = WINPR_C_ARRAY_INIT;
1317
0
    dir = FindFirstFileA(path_slash, &findFileData);
1318
1319
0
    if (dir == INVALID_HANDLE_VALUE)
1320
0
      goto fail;
1321
1322
0
    ret = TRUE;
1323
0
    path_slash[path_slash_len - 1] = '\0'; /* remove trailing '*' */
1324
0
    do
1325
0
    {
1326
0
      const size_t len = strnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
1327
1328
0
      if ((len == 1 && findFileData.cFileName[0] == '.') ||
1329
0
          (len == 2 && findFileData.cFileName[0] == '.' && findFileData.cFileName[1] == '.'))
1330
0
      {
1331
0
        continue;
1332
0
      }
1333
1334
0
      char* fullpath = concat(path_slash, path_slash_len, findFileData.cFileName, len);
1335
0
      if (!fullpath)
1336
0
        goto fail;
1337
1338
0
      if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1339
0
        ret = winpr_RemoveDirectory_RecursiveA(fullpath);
1340
0
      else
1341
0
      {
1342
0
        WINPR_PRAGMA_DIAG_PUSH
1343
0
        WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
1344
0
        ret = winpr_DeleteFile(fullpath);
1345
0
        WINPR_PRAGMA_DIAG_POP
1346
0
      }
1347
1348
0
      free(fullpath);
1349
1350
0
      if (!ret)
1351
0
        break;
1352
0
    } while (ret && FindNextFileA(dir, &findFileData) != 0);
1353
0
  }
1354
1355
0
  if (ret)
1356
0
  {
1357
0
    if (!winpr_RemoveDirectory(lpPathName))
1358
0
      ret = FALSE;
1359
0
  }
1360
1361
0
fail:
1362
0
  FindClose(dir);
1363
0
  free(path_slash);
1364
0
  return ret;
1365
0
}
1366
1367
BOOL winpr_RemoveDirectory_RecursiveW(LPCWSTR lpPathName)
1368
0
{
1369
0
  char* name = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1370
0
  if (!name)
1371
0
    return FALSE;
1372
0
  const BOOL rc = winpr_RemoveDirectory_RecursiveA(name);
1373
0
  free(name);
1374
0
  return rc;
1375
0
}
1376
1377
char* winpr_GetConfigFilePathVA(BOOL system, WINPR_FORMAT_ARG const char* filename, va_list ap)
1378
11.3k
{
1379
11.3k
  eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME;
1380
11.3k
  const char* vendor = winpr_getApplicationDetailsVendor();
1381
11.3k
  const char* product = winpr_getApplicationDetailsProduct();
1382
11.3k
  const SSIZE_T version = winpr_getApplicationDetailsVersion();
1383
1384
11.3k
  if (!vendor || !product)
1385
0
    return nullptr;
1386
1387
11.3k
  char* config = GetKnownSubPathV(id, "%s", vendor);
1388
11.3k
  if (!config)
1389
0
    return nullptr;
1390
1391
11.3k
  char* base = nullptr;
1392
11.3k
  if (version < 0)
1393
11.3k
    base = GetCombinedPathV(config, "%s", product);
1394
0
  else
1395
0
    base = GetCombinedPathV(config, "%s%" PRIdz, product, version);
1396
11.3k
  free(config);
1397
1398
11.3k
  if (!base)
1399
0
    return nullptr;
1400
11.3k
  char* path = GetCombinedPathVA(base, filename, ap);
1401
11.3k
  free(base);
1402
1403
11.3k
  return path;
1404
11.3k
}
1405
1406
char* winpr_GetConfigFilePath(BOOL system, const char* filename)
1407
11.3k
{
1408
11.3k
  if (!filename)
1409
0
    return winpr_GetConfigFilePathV(system, "%s", "");
1410
11.3k
  return winpr_GetConfigFilePathV(system, "%s", filename);
1411
11.3k
}
1412
1413
char* winpr_GetConfigFilePathV(BOOL system, const char* filename, ...)
1414
11.3k
{
1415
11.3k
  va_list ap = WINPR_C_ARRAY_INIT;
1416
11.3k
  va_start(ap, filename);
1417
11.3k
  char* str = winpr_GetConfigFilePathVA(system, filename, ap);
1418
  va_end(ap);
1419
11.3k
  return str;
1420
11.3k
}