Coverage Report

Created: 2026-08-31 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/path/shell.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
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/config.h>
22
#include <winpr/build-config.h>
23
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <sys/stat.h>
28
29
#include <winpr/crt.h>
30
#include <winpr/platform.h>
31
#include <winpr/file.h>
32
#include <winpr/tchar.h>
33
#include <winpr/environment.h>
34
35
#include <winpr/path.h>
36
#include <winpr/wlog.h>
37
38
#include "../file/file.h"
39
40
#include "../log.h"
41
#define TAG WINPR_TAG("path.shell")
42
43
#if defined(__IOS__)
44
#include "shell_ios.h"
45
#endif
46
47
#if defined(WIN32)
48
#include <windows.h>
49
#include <shlobj.h>
50
#include <shlwapi.h>
51
#else
52
#include <errno.h>
53
#include <dirent.h>
54
#endif
55
56
static char* GetPath_XDG_CONFIG_HOME(void);
57
static char* GetPath_XDG_RUNTIME_DIR(void);
58
59
/**
60
 * SHGetKnownFolderPath function:
61
 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
62
 */
63
64
#if defined(WIN32) && !defined(_UWP)
65
66
static char* win_get_known_folder(REFKNOWNFOLDERID id, BOOL currentUser)
67
{
68
  WCHAR* wpath = nullptr;
69
  HANDLE handle = currentUser ? nullptr : (HANDLE)-1;
70
  if (FAILED(SHGetKnownFolderPath(id, 0, handle, &wpath)))
71
    return nullptr;
72
73
  if (!wpath)
74
    return nullptr;
75
76
  char* path = ConvertWCharToUtf8Alloc(wpath, nullptr);
77
  CoTaskMemFree(wpath);
78
  return path;
79
}
80
#endif
81
82
/**
83
 * XDG Base Directory Specification:
84
 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
85
 */
86
87
char* GetEnvAlloc(LPCSTR lpName)
88
115
{
89
115
  DWORD nSize = 0;
90
115
  DWORD nStatus = 0;
91
115
  char* env = nullptr;
92
93
115
  nSize = GetEnvironmentVariableX(lpName, nullptr, 0);
94
95
115
  if (nSize > 0)
96
46
  {
97
46
    env = malloc(nSize);
98
99
46
    if (!env)
100
0
      return nullptr;
101
102
46
    nStatus = GetEnvironmentVariableX(lpName, env, nSize);
103
104
46
    if (nStatus != (nSize - 1))
105
0
    {
106
0
      free(env);
107
0
      return nullptr;
108
0
    }
109
46
  }
110
111
115
  return env;
112
115
}
113
114
static char* GetPath_HOME(void)
115
46
{
116
46
  char* path = nullptr;
117
#ifdef _WIN32
118
  path = GetEnvAlloc("UserProfile");
119
#elif defined(__IOS__)
120
  path = ios_get_home();
121
#else
122
46
  path = GetEnvAlloc("HOME");
123
46
#endif
124
46
  return path;
125
46
}
126
127
static char* GetPath_TEMP(void)
128
46
{
129
46
  char* path = nullptr;
130
#ifdef _WIN32
131
  path = GetEnvAlloc("TEMP");
132
#elif defined(__IOS__)
133
  path = ios_get_temp();
134
#else
135
46
  path = GetEnvAlloc("TMPDIR");
136
137
46
  if (!path)
138
46
    path = _strdup("/tmp");
139
140
46
#endif
141
46
  return path;
142
46
}
143
144
static char* GetPath_XDG_DATA_HOME(void)
145
0
{
146
0
  char* path = nullptr;
147
#if defined(WIN32) || defined(__IOS__)
148
  path = GetPath_XDG_CONFIG_HOME();
149
#else
150
0
  size_t size = 0;
151
0
  char* home = nullptr;
152
  /**
153
   * There is a single base directory relative to which user-specific data files should be
154
   * written. This directory is defined by the environment variable $XDG_DATA_HOME.
155
   *
156
   * $XDG_DATA_HOME defines the base directory relative to which user specific data files should
157
   * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
158
   * $HOME/.local/share should be used.
159
   */
160
0
  path = GetEnvAlloc("XDG_DATA_HOME");
161
162
0
  if (path)
163
0
    return path;
164
165
0
  home = GetPath_HOME();
166
167
0
  if (!home)
168
0
    return nullptr;
169
170
0
  size = strlen(home) + strlen("/.local/share") + 1;
171
0
  path = (char*)malloc(size);
172
173
0
  if (!path)
174
0
  {
175
0
    free(home);
176
0
    return nullptr;
177
0
  }
178
179
0
  (void)sprintf_s(path, size, "%s%s", home, "/.local/share");
180
0
  free(home);
181
0
#endif
182
0
  return path;
183
0
}
184
185
static char* GetPath_XDG_CONFIG_HOME(void)
186
23
{
187
23
  char* path = nullptr;
188
#if defined(WIN32) && !defined(_UWP)
189
190
  path = win_get_known_folder(&FOLDERID_RoamingAppData, TRUE);
191
192
#elif defined(__IOS__)
193
  path = ios_get_data();
194
#else
195
23
  size_t size = 0;
196
23
  char* home = nullptr;
197
  /**
198
   * There is a single base directory relative to which user-specific configuration files should
199
   * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
200
   *
201
   * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
202
   * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
203
   * $HOME/.config should be used.
204
   */
205
23
  path = GetEnvAlloc("XDG_CONFIG_HOME");
206
207
23
  if (path)
208
0
    return path;
209
210
23
  home = GetPath_HOME();
211
212
23
  if (!home)
213
0
    home = GetPath_TEMP();
214
215
23
  if (!home)
216
0
    return nullptr;
217
218
23
  size = strlen(home) + strlen("/.config") + 1;
219
23
  path = (char*)malloc(size);
220
221
23
  if (!path)
222
0
  {
223
0
    free(home);
224
0
    return nullptr;
225
0
  }
226
227
23
  (void)sprintf_s(path, size, "%s%s", home, "/.config");
228
23
  free(home);
229
23
#endif
230
23
  return path;
231
23
}
232
233
static char* GetPath_SYSTEM_CONFIG_HOME(void)
234
115
{
235
115
  char* path = nullptr;
236
#if defined(WIN32) && !defined(_UWP)
237
238
  path = win_get_known_folder(&FOLDERID_ProgramData, FALSE);
239
240
#elif defined(__IOS__)
241
  path = ios_get_data();
242
#else
243
115
  path = _strdup(WINPR_INSTALL_SYSCONFDIR);
244
115
#endif
245
115
  return path;
246
115
}
247
248
static char* GetPath_XDG_CACHE_HOME(void)
249
0
{
250
0
  char* path = nullptr;
251
#if defined(WIN32)
252
  {
253
    char* home = GetPath_XDG_RUNTIME_DIR();
254
255
    if (home)
256
    {
257
      path = GetCombinedPath(home, "cache");
258
259
      if (!winpr_PathFileExists(path))
260
        if (!winpr_PathMakePath(path, nullptr))
261
          path = nullptr;
262
    }
263
264
    free(home);
265
  }
266
#elif defined(__IOS__)
267
  path = ios_get_cache();
268
#else
269
0
  size_t size = 0;
270
  /**
271
   * There is a single base directory relative to which user-specific non-essential (cached) data
272
   * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
273
   *
274
   * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data
275
   * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to
276
   * $HOME/.cache should be used.
277
   */
278
0
  path = GetEnvAlloc("XDG_CACHE_HOME");
279
280
0
  if (path)
281
0
    return path;
282
283
0
  char* home = GetPath_HOME();
284
285
0
  if (!home)
286
0
    return nullptr;
287
288
0
  size = strlen(home) + strlen("/.cache") + 1;
289
0
  path = (char*)malloc(size);
290
291
0
  if (!path)
292
0
  {
293
0
    free(home);
294
0
    return nullptr;
295
0
  }
296
297
0
  (void)sprintf_s(path, size, "%s%s", home, "/.cache");
298
0
  free(home);
299
0
#endif
300
0
  return path;
301
0
}
302
303
char* GetPath_XDG_RUNTIME_DIR(void)
304
0
{
305
0
  char* path = nullptr;
306
#if defined(WIN32) && !defined(_UWP)
307
308
  path = win_get_known_folder(&FOLDERID_LocalAppData, TRUE);
309
310
#else
311
  /**
312
   * There is a single base directory relative to which user-specific runtime files and other file
313
   * objects should be placed. This directory is defined by the environment variable
314
   * $XDG_RUNTIME_DIR.
315
   *
316
   * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
317
   * runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
318
   * The directory MUST be owned by the user, and he MUST be the only one having read and write
319
   * access to it. Its Unix access mode MUST be 0700.
320
   *
321
   * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created
322
   * when the user first logs in and if the user fully logs out the directory MUST be removed. If
323
   * the user logs in more than once he should get pointed to the same directory, and it is
324
   * mandatory that the directory continues to exist from his first login to his last logout on
325
   * the system, and not removed in between. Files in the directory MUST not survive reboot or a
326
   * full logout/login cycle.
327
   *
328
   * The directory MUST be on a local file system and not shared with any other system. The
329
   * directory MUST by fully-featured by the standards of the operating system. More specifically,
330
   * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper
331
   * permissions, file locking, sparse files, memory mapping, file change notifications, a
332
   * reliable hard link count must be supported, and no restrictions on the file name character
333
   * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To
334
   * ensure that your files are not removed, they should have their access time timestamp modified
335
   * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
336
   *
337
   * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with
338
   * similar capabilities and print a warning message. Applications should use this directory for
339
   * communication and synchronization purposes and should not place larger files in it, since it
340
   * might reside in runtime memory and cannot necessarily be swapped out to disk.
341
   */
342
0
  path = GetEnvAlloc("XDG_RUNTIME_DIR");
343
0
#endif
344
345
0
  if (path)
346
0
    return path;
347
348
0
  path = GetPath_TEMP();
349
0
  return path;
350
0
}
351
352
char* GetKnownPath(eKnownPathTypes id)
353
207
{
354
207
  char* path = nullptr;
355
356
207
  switch (id)
357
207
  {
358
23
    case KNOWN_PATH_HOME:
359
23
      path = GetPath_HOME();
360
23
      break;
361
362
46
    case KNOWN_PATH_TEMP:
363
46
      path = GetPath_TEMP();
364
46
      break;
365
366
0
    case KNOWN_PATH_XDG_DATA_HOME:
367
0
      path = GetPath_XDG_DATA_HOME();
368
0
      break;
369
370
23
    case KNOWN_PATH_XDG_CONFIG_HOME:
371
23
      path = GetPath_XDG_CONFIG_HOME();
372
23
      break;
373
374
0
    case KNOWN_PATH_XDG_CACHE_HOME:
375
0
      path = GetPath_XDG_CACHE_HOME();
376
0
      break;
377
378
0
    case KNOWN_PATH_XDG_RUNTIME_DIR:
379
0
      path = GetPath_XDG_RUNTIME_DIR();
380
0
      break;
381
382
115
    case KNOWN_PATH_SYSTEM_CONFIG_HOME:
383
115
      path = GetPath_SYSTEM_CONFIG_HOME();
384
115
      break;
385
386
0
    default:
387
0
      path = nullptr;
388
0
      break;
389
207
  }
390
391
207
  if (!path)
392
0
    WLog_WARN(TAG, "Path %s is nullptr",
393
207
              GetKnownPathIdString(WINPR_ASSERTING_INT_CAST(int, id)));
394
207
  return path;
395
207
}
396
397
char* GetKnownSubPath(eKnownPathTypes id, const char* path)
398
69
{
399
69
  if (!path)
400
0
    return GetKnownSubPathV(id, "%s", "");
401
69
  return GetKnownSubPathV(id, "%s", path);
402
69
}
403
404
char* GetKnownSubPathV(eKnownPathTypes id, const char* path, ...)
405
184
{
406
184
  va_list ap = WINPR_C_ARRAY_INIT;
407
408
184
  va_start(ap, path);
409
184
  char* str = GetKnownSubPathVA(id, path, ap);
410
184
  va_end(ap);
411
184
  return str;
412
184
}
413
414
char* GetKnownSubPathVA(eKnownPathTypes id, const char* path, va_list ap)
415
184
{
416
184
  char* knownPath = GetKnownPath(id);
417
184
  if (!knownPath)
418
0
    return nullptr;
419
420
184
  char* subPath = GetCombinedPathVA(knownPath, path, ap);
421
184
  free(knownPath);
422
184
  return subPath;
423
184
}
424
425
char* GetEnvironmentPath(char* name)
426
0
{
427
0
  char* env = nullptr;
428
0
  DWORD nSize = 0;
429
0
  DWORD nStatus = 0;
430
0
  nSize = GetEnvironmentVariableX(name, nullptr, 0);
431
432
0
  if (nSize)
433
0
  {
434
0
    env = (LPSTR)malloc(nSize);
435
436
0
    if (!env)
437
0
      return nullptr;
438
439
0
    nStatus = GetEnvironmentVariableX(name, env, nSize);
440
441
0
    if (nStatus != (nSize - 1))
442
0
    {
443
0
      free(env);
444
0
      return nullptr;
445
0
    }
446
0
  }
447
448
0
  return env;
449
0
}
450
451
char* GetEnvironmentSubPath(char* name, const char* path)
452
0
{
453
0
  if (!path)
454
0
    return GetEnvironmentSubPathV(name, "%s", "");
455
0
  return GetEnvironmentSubPathV(name, "%s", path);
456
0
}
457
458
char* GetEnvironmentSubPathV(char* name, const char* path, ...)
459
0
{
460
0
  va_list ap = WINPR_C_ARRAY_INIT;
461
0
  va_start(ap, path);
462
0
  char* str = GetEnvironmentSubPathVA(name, path, ap);
463
0
  va_end(ap);
464
0
  return str;
465
0
}
466
467
char* GetEnvironmentSubPathVA(char* name, WINPR_FORMAT_ARG const char* path, va_list ap)
468
0
{
469
0
  char* env = GetEnvironmentPath(name);
470
471
0
  if (!env)
472
0
    return nullptr;
473
474
0
  char* subpath = GetCombinedPathVA(env, path, ap);
475
0
  free(env);
476
0
  return subpath;
477
0
}
478
479
char* GetCombinedPath(const char* basePath, const char* subPathFmt)
480
48
{
481
48
  if (!subPathFmt)
482
0
    return GetCombinedPathV(basePath, "%s", "");
483
48
  return GetCombinedPathV(basePath, "%s", subPathFmt);
484
48
}
485
486
char* GetCombinedPathV(const char* basePath, const char* subPathFmt, ...)
487
301
{
488
301
  va_list ap = WINPR_C_ARRAY_INIT;
489
490
301
  va_start(ap, subPathFmt);
491
301
  char* str = GetCombinedPathVA(basePath, subPathFmt, ap);
492
301
  va_end(ap);
493
301
  return str;
494
301
}
495
496
char* GetCombinedPathVA(const char* basePath, WINPR_FORMAT_ARG const char* subPathFmt, va_list ap)
497
600
{
498
600
  HRESULT status = 0;
499
600
  char* subPathCpy = nullptr;
500
600
  size_t basePathLength = 0;
501
600
  size_t subPathLength = 0;
502
503
600
  if (basePath)
504
600
    basePathLength = strlen(basePath);
505
506
600
  bool haveSubPath = subPathFmt && (*subPathFmt != '\0');
507
600
  if (haveSubPath)
508
600
  {
509
600
    const int rc = winpr_vasprintf(&subPathCpy, &subPathLength, subPathFmt, ap);
510
600
    if (rc < 0)
511
0
      return nullptr;
512
600
    if (rc == 0)
513
23
    {
514
23
      free(subPathCpy);
515
23
      subPathCpy = nullptr;
516
23
      subPathLength = 0;
517
23
      haveSubPath = false;
518
23
    }
519
600
  }
520
521
600
  const size_t length = basePathLength + subPathLength + 1;
522
600
  char* path = (char*)calloc(1, length + 1);
523
524
600
  if (!path)
525
0
    goto fail;
526
527
600
  if (basePath)
528
600
    CopyMemory(path, basePath, basePathLength);
529
530
600
  if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
531
0
    goto fail;
532
533
600
  if (!haveSubPath)
534
23
    return path;
535
536
577
  if (!subPathCpy)
537
0
    goto fail;
538
539
577
  if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
540
0
    goto fail;
541
542
577
  status = NativePathCchAppendA(path, length + 1, subPathCpy);
543
577
  if (FAILED(status))
544
0
    goto fail;
545
546
577
  free(subPathCpy);
547
577
  return path;
548
549
0
fail:
550
0
  free(path);
551
0
  free(subPathCpy);
552
0
  return nullptr;
553
577
}
554
555
BOOL PathMakePathA(LPCSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
556
1
{
557
#if defined(_UWP)
558
  return FALSE;
559
#elif defined(_WIN32)
560
  return (SHCreateDirectoryExA(nullptr, path, lpAttributes) == ERROR_SUCCESS);
561
#else
562
1
  const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
563
1
  char* dup = nullptr;
564
1
  BOOL result = TRUE;
565
  /* we only operate on a non-null, absolute path */
566
#if defined(__OS2__)
567
568
  if (!path)
569
    return FALSE;
570
571
#else
572
573
1
  if (!path || *path != delim)
574
0
    return FALSE;
575
576
1
#endif
577
578
1
  if (!(dup = _strdup(path)))
579
0
    return FALSE;
580
581
#ifdef __OS2__
582
  p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
583
584
  while (p)
585
#else
586
3
  for (char* p = dup; p;)
587
2
#endif
588
2
  {
589
2
    if ((p = strchr(p + 1, delim)))
590
1
      *p = '\0';
591
592
2
    if (mkdir(dup, 0777) != 0)
593
1
      if (errno != EEXIST)
594
0
      {
595
0
        result = FALSE;
596
0
        break;
597
0
      }
598
599
2
    if (p)
600
1
      *p = delim;
601
2
  }
602
603
1
  free(dup);
604
1
  return (result);
605
1
#endif
606
1
}
607
608
BOOL PathMakePathW(LPCWSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
609
0
{
610
#if defined(_UWP)
611
  return FALSE;
612
#elif defined(_WIN32)
613
  return (SHCreateDirectoryExW(nullptr, path, lpAttributes) == ERROR_SUCCESS);
614
#else
615
0
  const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
616
0
  const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
617
0
  char* dup = nullptr;
618
0
  BOOL result = TRUE;
619
  /* we only operate on a non-null, absolute path */
620
#if defined(__OS2__)
621
622
  if (!path)
623
    return FALSE;
624
625
#else
626
627
0
  if (!path || *path != wdelim)
628
0
    return FALSE;
629
630
0
#endif
631
632
0
  dup = ConvertWCharToUtf8Alloc(path, nullptr);
633
0
  if (!dup)
634
0
    return FALSE;
635
636
#ifdef __OS2__
637
  p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
638
639
  while (p)
640
#else
641
0
  for (char* p = dup; p;)
642
0
#endif
643
0
  {
644
0
    if ((p = strchr(p + 1, delim)))
645
0
      *p = '\0';
646
647
0
    if (mkdir(dup, 0777) != 0)
648
0
      if (errno != EEXIST)
649
0
      {
650
0
        result = FALSE;
651
0
        break;
652
0
      }
653
654
0
    if (p)
655
0
      *p = delim;
656
0
  }
657
658
0
  free(dup);
659
0
  return (result);
660
0
#endif
661
0
}
662
663
#if !defined(_WIN32) || defined(_UWP)
664
665
BOOL PathIsRelativeA(LPCSTR pszPath)
666
0
{
667
0
  if (!pszPath)
668
0
    return FALSE;
669
670
0
  return pszPath[0] != '/';
671
0
}
672
673
BOOL PathIsRelativeW(LPCWSTR pszPath)
674
0
{
675
0
  LPSTR lpFileNameA = nullptr;
676
0
  BOOL ret = FALSE;
677
678
0
  if (!pszPath)
679
0
    goto fail;
680
681
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
682
0
  if (!lpFileNameA)
683
0
    goto fail;
684
0
  ret = PathIsRelativeA(lpFileNameA);
685
0
fail:
686
0
  free(lpFileNameA);
687
0
  return ret;
688
0
}
689
690
BOOL PathFileExistsA(LPCSTR pszPath)
691
137
{
692
137
  struct stat stat_info = WINPR_C_ARRAY_INIT;
693
694
137
  return (stat(pszPath, &stat_info) == 0);
695
137
}
696
697
BOOL PathFileExistsW(LPCWSTR pszPath)
698
0
{
699
0
  LPSTR lpFileNameA = nullptr;
700
0
  BOOL ret = FALSE;
701
702
0
  if (!pszPath)
703
0
    goto fail;
704
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
705
0
  if (!lpFileNameA)
706
0
    goto fail;
707
708
0
  ret = winpr_PathFileExists(lpFileNameA);
709
0
fail:
710
0
  free(lpFileNameA);
711
0
  return ret;
712
0
}
713
714
BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
715
0
{
716
0
  struct dirent* dp = nullptr;
717
0
  int empty = 1;
718
0
  DIR* dir = opendir(pszPath);
719
720
0
  if (dir == nullptr) /* Not a directory or doesn't exist */
721
0
    return 1;
722
723
  // NOLINTNEXTLINE(concurrency-mt-unsafe)
724
0
  while ((dp = readdir(dir)) != nullptr)
725
0
  {
726
0
    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
727
0
      continue; /* Skip . and .. */
728
729
0
    empty = 0;
730
0
    break;
731
0
  }
732
733
0
  closedir(dir);
734
0
  return empty;
735
0
}
736
737
BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
738
0
{
739
0
  LPSTR lpFileNameA = nullptr;
740
0
  BOOL ret = FALSE;
741
0
  if (!pszPath)
742
0
    goto fail;
743
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
744
0
  if (!lpFileNameA)
745
0
    goto fail;
746
0
  ret = PathIsDirectoryEmptyA(lpFileNameA);
747
0
fail:
748
0
  free(lpFileNameA);
749
0
  return ret;
750
0
}
751
752
#endif
753
754
BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
755
0
{
756
0
  return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
757
0
}
758
759
BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
760
0
{
761
0
#ifndef _WIN32
762
0
  struct stat st;
763
0
  int ret = 0;
764
0
  ret = stat(lpNewFileName, &st);
765
766
0
  if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
767
0
  {
768
0
    if (ret == 0)
769
0
    {
770
0
      SetLastError(ERROR_ALREADY_EXISTS);
771
0
      return FALSE;
772
0
    }
773
0
  }
774
0
  else
775
0
  {
776
0
    if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
777
0
    {
778
0
      SetLastError(ERROR_ACCESS_DENIED);
779
0
      return FALSE;
780
0
    }
781
0
  }
782
783
0
  ret = rename(lpExistingFileName, lpNewFileName);
784
785
0
  if (ret != 0)
786
0
    SetLastError(map_posix_err(errno));
787
788
0
  return ret == 0;
789
#else
790
  BOOL result = FALSE;
791
  LPWSTR lpExistingFileNameW = nullptr;
792
  LPWSTR lpNewFileNameW = nullptr;
793
794
  if (!lpExistingFileName || !lpNewFileName)
795
    return FALSE;
796
797
  lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, nullptr);
798
  if (!lpExistingFileNameW)
799
    goto cleanup;
800
  lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, nullptr);
801
  if (!lpNewFileNameW)
802
    goto cleanup;
803
804
  result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
805
806
cleanup:
807
  free(lpExistingFileNameW);
808
  free(lpNewFileNameW);
809
  return result;
810
#endif
811
0
}
812
813
BOOL winpr_DeleteFile(const char* lpFileName)
814
0
{
815
0
#ifndef _WIN32
816
0
  if (!lpFileName)
817
0
    return FALSE;
818
819
0
  const int status = unlink(lpFileName);
820
0
  return (status != -1);
821
#else
822
  LPWSTR lpFileNameW = nullptr;
823
  BOOL result = FALSE;
824
825
  if (lpFileName)
826
    lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, nullptr);
827
828
  if (!lpFileNameW)
829
    goto cleanup;
830
831
  result = DeleteFileW(lpFileNameW);
832
833
cleanup:
834
  free(lpFileNameW);
835
  return result;
836
#endif
837
0
}
838
839
BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
840
0
{
841
0
#ifndef _WIN32
842
0
  int ret = rmdir(lpPathName);
843
844
0
  if (ret != 0)
845
0
    SetLastError(map_posix_err(errno));
846
0
  else
847
0
    SetLastError(STATUS_SUCCESS);
848
849
0
  return ret == 0;
850
#else
851
  LPWSTR lpPathNameW = nullptr;
852
  BOOL result = FALSE;
853
854
  if (lpPathName)
855
    lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, nullptr);
856
857
  if (!lpPathNameW)
858
    goto cleanup;
859
860
  result = RemoveDirectoryW(lpPathNameW);
861
862
cleanup:
863
  free(lpPathNameW);
864
  return result;
865
#endif
866
0
}
867
868
BOOL winpr_PathFileExists(const char* pszPath)
869
137
{
870
137
  if (!pszPath)
871
0
    return FALSE;
872
137
#ifndef _WIN32
873
137
  return PathFileExistsA(pszPath);
874
#else
875
  WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, nullptr);
876
  BOOL result = FALSE;
877
878
  if (!pathW)
879
    return FALSE;
880
881
  result = PathFileExistsW(pathW);
882
  free(pathW);
883
884
  return result;
885
#endif
886
137
}
887
888
BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
889
1
{
890
1
  if (!path)
891
0
    return FALSE;
892
1
#ifndef _WIN32
893
1
  return PathMakePathA(path, lpAttributes);
894
#else
895
  WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, nullptr);
896
  BOOL result = FALSE;
897
898
  if (!pathW)
899
    return FALSE;
900
901
  result = SHCreateDirectoryExW(nullptr, pathW, lpAttributes) == ERROR_SUCCESS;
902
  free(pathW);
903
904
  return result;
905
#endif
906
1
}