1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22#include "tool_setup.h"
23
24#if defined(MSDOS) || defined(WIN32)
25
26#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
27# include <libgen.h>
28#endif
29
30#ifdef WIN32
31# include <tlhelp32.h>
32# include "tool_cfgable.h"
33# include "tool_libinfo.h"
34#endif
35
36#include "tool_bname.h"
37#include "tool_doswin.h"
38
39#include "memdebug.h" /* keep this as LAST include */
40
41/*
42 * Macros ALWAYS_TRUE and ALWAYS_FALSE are used to avoid compiler warnings.
43 */
44
45#define ALWAYS_TRUE (1)
46#define ALWAYS_FALSE (0)
47
48#if defined(_MSC_VER) && !defined(__POCC__)
49# undef ALWAYS_TRUE
50# undef ALWAYS_FALSE
51# if (_MSC_VER < 1500)
52# define ALWAYS_TRUE (0, 1)
53# define ALWAYS_FALSE (1, 0)
54# else
55# define ALWAYS_TRUE \
56__pragma(warning(push)) \
57__pragma(warning(disable:4127)) \
58(1) \
59__pragma(warning(pop))
60# define ALWAYS_FALSE \
61__pragma(warning(push)) \
62__pragma(warning(disable:4127)) \
63(0) \
64__pragma(warning(pop))
65# endif
66#endif
67
68#ifdef WIN32
69# undef PATH_MAX
70# define PATH_MAX MAX_PATH
71#endif
72
73#ifndef S_ISCHR
74# ifdef S_IFCHR
75# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
76# else
77# define S_ISCHR(m) (0) /* cannot tell if file is a device */
78# endif
79#endif
80
81#ifdef WIN32
82# define _use_lfn(f) ALWAYS_TRUE /* long file names always available */
83#elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */
84# define _use_lfn(f) ALWAYS_FALSE /* long file names never available */
85#elif defined(__DJGPP__)
86# include <fcntl.h> /* _use_lfn(f) prototype */
87#endif
88
89#ifndef UNITTESTS
90static SANITIZEcode truncate_dryrun(const char *path,
91 const size_t truncate_pos);
92#ifdef MSDOS
93static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
94 int flags);
95#endif
96static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
97 const char *file_name,
98 int flags);
99#endif /* !UNITTESTS (static declarations used if no unit tests) */
100
101
102/*
103Sanitize a file or path name.
104
105All banned characters are replaced by underscores, for example:
106f?*foo => f__foo
107f:foo::$DATA => f_foo__$DATA
108f:\foo:bar => f__foo_bar
109f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH)
110
111This function was implemented according to the guidelines in 'Naming Files,
112Paths, and Namespaces' section 'Naming Conventions'.
113https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
114
115Flags
116-----
117SANITIZE_ALLOW_COLONS: Allow colons.
118Without this flag colons are sanitized.
119
120SANITIZE_ALLOW_PATH: Allow path separators and colons.
121Without this flag path separators and colons are sanitized.
122
123SANITIZE_ALLOW_RESERVED: Allow reserved device names.
124Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's
125in a UNC prefixed path.
126
127SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename.
128Without this flag if the sanitized filename or path will be too long an error
129occurs. With this flag the filename --and not any other parts of the path-- may
130be truncated to at least a single character. A filename followed by an
131alternate data stream (ADS) cannot be truncated in any case.
132
133Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
134Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
135*/
136SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
137 int flags)
138{
139 char *p, *target;
140 size_t len;
141 SANITIZEcode sc;
142 size_t max_sanitized_len;
143
144 if(!sanitized)
145 return SANITIZE_ERR_BAD_ARGUMENT;
146
147 *sanitized = NULL;
148
149 if(!file_name)
150 return SANITIZE_ERR_BAD_ARGUMENT;
151
152 if((flags & SANITIZE_ALLOW_PATH)) {
153#ifndef MSDOS
154 if(file_name[0] == '\\' && file_name[1] == '\\')
155 /* UNC prefixed path \\ (eg \\?\C:\foo) */
156 max_sanitized_len = 32767-1;
157 else
158#endif
159 max_sanitized_len = PATH_MAX-1;
160 }
161 else
162 /* The maximum length of a filename.
163 FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and
164 does not discount the path information therefore we shouldn't use it. */
165 max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
166
167 len = strlen(file_name);
168 if(len > max_sanitized_len) {
169 if(!(flags & SANITIZE_ALLOW_TRUNCATE) ||
170 truncate_dryrun(file_name, max_sanitized_len))
171 return SANITIZE_ERR_INVALID_PATH;
172
173 len = max_sanitized_len;
174 }
175
176 target = malloc(len + 1);
177 if(!target)
178 return SANITIZE_ERR_OUT_OF_MEMORY;
179
180 strncpy(target, file_name, len);
181 target[len] = '\0';
182
183#ifndef MSDOS
184 if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
185 /* Skip the literal path prefix \\?\ */
186 p = target + 4;
187 else
188#endif
189 p = target;
190
191 /* replace control characters and other banned characters */
192 for(; *p; ++p) {
193 const char *banned;
194
195 if((1 <= *p && *p <= 31) ||
196 (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
197 (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
198 *p = '_';
199 continue;
200 }
201
202 for(banned = "|<>\"?*"; *banned; ++banned) {
203 if(*p == *banned) {
204 *p = '_';
205 break;
206 }
207 }
208 }
209
210 /* remove trailing spaces and periods if not allowing paths */
211 if(!(flags & SANITIZE_ALLOW_PATH) && len) {
212 char *clip = NULL;
213
214 p = &target[len];
215 do {
216 --p;
217 if(*p != ' ' && *p != '.')
218 break;
219 clip = p;
220 } while(p != target);
221
222 if(clip) {
223 *clip = '\0';
224 len = clip - target;
225 }
226 }
227
228#ifdef MSDOS
229 sc = msdosify(&p, target, flags);
230 free(target);
231 if(sc)
232 return sc;
233 target = p;
234 len = strlen(target);
235
236 if(len > max_sanitized_len) {
237 free(target);
238 return SANITIZE_ERR_INVALID_PATH;
239 }
240#endif
241
242 if(!(flags & SANITIZE_ALLOW_RESERVED)) {
243 sc = rename_if_reserved_dos_device_name(&p, target, flags);
244 free(target);
245 if(sc)
246 return sc;
247 target = p;
248 len = strlen(target);
249
250 if(len > max_sanitized_len) {
251 free(target);
252 return SANITIZE_ERR_INVALID_PATH;
253 }
254 }
255
256 *sanitized = target;
257 return SANITIZE_ERR_OK;
258}
259
260
261/*
262Test if truncating a path to a file will leave at least a single character in
263the filename. Filenames suffixed by an alternate data stream can't be
264truncated. This performs a dry run, nothing is modified.
265
266Good truncate_pos 9: C:\foo\bar => C:\foo\ba
267Good truncate_pos 6: C:\foo => C:\foo
268Good truncate_pos 5: C:\foo => C:\fo
269Bad* truncate_pos 5: C:foo => C:foo
270Bad truncate_pos 5: C:\foo:ads => C:\fo
271Bad truncate_pos 9: C:\foo:ads => C:\foo:ad
272Bad truncate_pos 5: C:\foo\bar => C:\fo
273Bad truncate_pos 5: C:\foo\ => C:\fo
274Bad truncate_pos 7: C:\foo\ => C:\foo\
275Error truncate_pos 7: C:\foo => (pos out of range)
276Bad truncate_pos 1: C:\foo\ => C
277
278* C:foo is ambiguous, C could end up being a drive or file therefore something
279 like C:superlongfilename can't be truncated.
280
281Returns
282SANITIZE_ERR_OK: Good -- 'path' can be truncated
283SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
284!= SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
285*/
286SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
287{
288 size_t len;
289
290 if(!path)
291 return SANITIZE_ERR_BAD_ARGUMENT;
292
293 len = strlen(path);
294
295 if(truncate_pos > len)
296 return SANITIZE_ERR_BAD_ARGUMENT;
297
298 if(!len || !truncate_pos)
299 return SANITIZE_ERR_INVALID_PATH;
300
301 if(strpbrk(&path[truncate_pos - 1], "\\/:"))
302 return SANITIZE_ERR_INVALID_PATH;
303
304 /* C:\foo can be truncated but C:\foo:ads can't */
305 if(truncate_pos > 1) {
306 const char *p = &path[truncate_pos - 1];
307 do {
308 --p;
309 if(*p == ':')
310 return SANITIZE_ERR_INVALID_PATH;
311 } while(p != path && *p != '\\' && *p != '/');
312 }
313
314 return SANITIZE_ERR_OK;
315}
316
317/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function
318 * were taken with modification from the DJGPP port of tar 1.12. They use
319 * algorithms originally from DJTAR.
320 */
321
322/*
323Extra sanitization MSDOS for file_name.
324
325This is a supporting function for sanitize_file_name.
326
327Warning: This is an MSDOS legacy function and was purposely written in a way
328that some path information may pass through. For example drive letter names
329(C:, D:, etc) are allowed to pass through. For sanitizing a filename use
330sanitize_file_name.
331
332Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
333Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
334*/
335#if defined(MSDOS) || defined(UNITTESTS)
336SANITIZEcode msdosify(char **const sanitized, const char *file_name,
337 int flags)
338{
339 char dos_name[PATH_MAX];
340 static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
341 "|<>/\\\":?*"; /* illegal in DOS & W95 */
342 static const char *illegal_chars_w95 = &illegal_chars_dos[8];
343 int idx, dot_idx;
344 const char *s = file_name;
345 char *d = dos_name;
346 const char *const dlimit = dos_name + sizeof(dos_name) - 1;
347 const char *illegal_aliens = illegal_chars_dos;
348 size_t len = sizeof(illegal_chars_dos) - 1;
349
350 if(!sanitized)
351 return SANITIZE_ERR_BAD_ARGUMENT;
352
353 *sanitized = NULL;
354
355 if(!file_name)
356 return SANITIZE_ERR_BAD_ARGUMENT;
357
358 if(strlen(file_name) > PATH_MAX-1 &&
359 (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
360 truncate_dryrun(file_name, PATH_MAX-1)))
361 return SANITIZE_ERR_INVALID_PATH;
362
363 /* Support for Windows 9X VFAT systems, when available. */
364 if(_use_lfn(file_name)) {
365 illegal_aliens = illegal_chars_w95;
366 len -= (illegal_chars_w95 - illegal_chars_dos);
367 }
368
369 /* Get past the drive letter, if any. */
370 if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
371 *d++ = *s++;
372 *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
373 ++d, ++s;
374 }
375
376 for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
377 if(memchr(illegal_aliens, *s, len)) {
378
379 if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
380 *d = ':';
381 else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
382 *d = *s;
383 /* Dots are special: DOS doesn't allow them as the leading character,
384 and a file name cannot have more than a single dot. We leave the
385 first non-leading dot alone, unless it comes too close to the
386 beginning of the name: we want sh.lex.c to become sh_lex.c, not
387 sh.lex-c. */
388 else if(*s == '.') {
389 if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
390 (s[1] == '/' || s[1] == '\\' ||
391 (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
392 /* Copy "./" and "../" verbatim. */
393 *d++ = *s++;
394 if(d == dlimit)
395 break;
396 if(*s == '.') {
397 *d++ = *s++;
398 if(d == dlimit)
399 break;
400 }
401 *d = *s;
402 }
403 else if(idx == 0)
404 *d = '_';
405 else if(dot_idx >= 0) {
406 if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
407 d[dot_idx - idx] = '_'; /* replace previous dot */
408 *d = '.';
409 }
410 else
411 *d = '-';
412 }
413 else
414 *d = '.';
415
416 if(*s == '.')
417 dot_idx = idx;
418 }
419 else if(*s == '+' && s[1] == '+') {
420 if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
421 *d++ = 'x';
422 if(d == dlimit)
423 break;
424 *d = 'x';
425 }
426 else {
427 /* libg++ etc. */
428 if(dlimit - d < 4) {
429 *d++ = 'x';
430 if(d == dlimit)
431 break;
432 *d = 'x';
433 }
434 else {
435 memcpy(d, "plus", 4);
436 d += 3;
437 }
438 }
439 s++;
440 idx++;
441 }
442 else
443 *d = '_';
444 }
445 else
446 *d = *s;
447 if(*s == '/' || *s == '\\') {
448 idx = 0;
449 dot_idx = -1;
450 }
451 else
452 idx++;
453 }
454 *d = '\0';
455
456 if(*s) {
457 /* dos_name is truncated, check that truncation requirements are met,
458 specifically truncating a filename suffixed by an alternate data stream
459 or truncating the entire filename is not allowed. */
460 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") ||
461 truncate_dryrun(dos_name, d - dos_name))
462 return SANITIZE_ERR_INVALID_PATH;
463 }
464
465 *sanitized = strdup(dos_name);
466 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
467}
468#endif /* MSDOS || UNITTESTS */
469
470/*
471Rename file_name if it's a reserved dos device name.
472
473This is a supporting function for sanitize_file_name.
474
475Warning: This is an MSDOS legacy function and was purposely written in a way
476that some path information may pass through. For example drive letter names
477(C:, D:, etc) are allowed to pass through. For sanitizing a filename use
478sanitize_file_name.
479
480Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
481Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
482*/
483SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
484 const char *file_name,
485 int flags)
486{
487 /* We could have a file whose name is a device on MS-DOS. Trying to
488 * retrieve such a file would fail at best and wedge us at worst. We need
489 * to rename such files. */
490 char *p, *base;
491 char fname[PATH_MAX];
492#ifdef MSDOS
493 struct_stat st_buf;
494#endif
495
496 if(!sanitized)
497 return SANITIZE_ERR_BAD_ARGUMENT;
498
499 *sanitized = NULL;
500
501 if(!file_name)
502 return SANITIZE_ERR_BAD_ARGUMENT;
503
504 /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
505#ifndef MSDOS
506 if((flags & SANITIZE_ALLOW_PATH) &&
507 file_name[0] == '\\' && file_name[1] == '\\') {
508 size_t len = strlen(file_name);
509 *sanitized = malloc(len + 1);
510 if(!*sanitized)
511 return SANITIZE_ERR_OUT_OF_MEMORY;
512 strncpy(*sanitized, file_name, len + 1);
513 return SANITIZE_ERR_OK;
514 }
515#endif
516
517 if(strlen(file_name) > PATH_MAX-1 &&
518 (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
519 truncate_dryrun(file_name, PATH_MAX-1)))
520 return SANITIZE_ERR_INVALID_PATH;
521
522 strncpy(fname, file_name, PATH_MAX-1);
523 fname[PATH_MAX-1] = '\0';
524 base = basename(fname);
525
526 /* Rename reserved device names that are known to be accessible without \\.\
527 Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
528 https://support.microsoft.com/en-us/kb/74496
529 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
530 */
531 for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
532 size_t p_len;
533 int x = (curl_strnequal(p, "CON", 3) ||
534 curl_strnequal(p, "PRN", 3) ||
535 curl_strnequal(p, "AUX", 3) ||
536 curl_strnequal(p, "NUL", 3)) ? 3 :
537 (curl_strnequal(p, "CLOCK$", 6)) ? 6 :
538 (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
539 (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
540
541 if(!x)
542 continue;
543
544 /* the devices may be accessible with an extension or ADS, for
545 example CON.AIR and 'CON . AIR' and CON:AIR access console */
546
547 for(; p[x] == ' '; ++x)
548 ;
549
550 if(p[x] == '.') {
551 p[x] = '_';
552 continue;
553 }
554 else if(p[x] == ':') {
555 if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
556 p[x] = '_';
557 continue;
558 }
559 ++x;
560 }
561 else if(p[x]) /* no match */
562 continue;
563
564 /* p points to 'CON' or 'CON ' or 'CON:', etc */
565 p_len = strlen(p);
566
567 /* Prepend a '_' */
568 if(strlen(fname) == PATH_MAX-1) {
569 --p_len;
570 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
571 return SANITIZE_ERR_INVALID_PATH;
572 p[p_len] = '\0';
573 }
574 memmove(p + 1, p, p_len + 1);
575 p[0] = '_';
576 ++p_len;
577
578 /* if fname was just modified then the basename pointer must be updated */
579 if(p == fname)
580 base = basename(fname);
581 }
582
583 /* This is the legacy portion from rename_if_dos_device_name that checks for
584 reserved device names. It only works on MSDOS. On Windows XP the stat
585 check errors with EINVAL if the device name is reserved. On Windows
586 Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN
587 stat doc the latter behavior is correct, but that doesn't help us identify
588 whether it's a reserved device name and not a regular file name. */
589#ifdef MSDOS
590 if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
591 /* Prepend a '_' */
592 size_t blen = strlen(base);
593 if(blen) {
594 if(strlen(fname) == PATH_MAX-1) {
595 --blen;
596 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
597 return SANITIZE_ERR_INVALID_PATH;
598 base[blen] = '\0';
599 }
600 memmove(base + 1, base, blen + 1);
601 base[0] = '_';
602 }
603 }
604#endif
605
606 *sanitized = strdup(fname);
607 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
608}
609
610#if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
611
612/*
613 * Disable program default argument globbing. We do it on our own.
614 */
615char **__crt0_glob_function(char *arg)
616{
617 (void)arg;
618 return (char **)0;
619}
620
621#endif /* MSDOS && (__DJGPP__ || __GO32__) */
622
623#ifdef WIN32
624
625/*
626 * Function to find CACert bundle on a Win32 platform using SearchPath.
627 * (SearchPath is already declared via inclusions done in setup header file)
628 * (Use the ASCII version instead of the unicode one!)
629 * The order of the directories it searches is:
630 * 1. application's directory
631 * 2. current working directory
632 * 3. Windows System directory (e.g. C:\windows\system32)
633 * 4. Windows Directory (e.g. C:\windows)
634 * 5. all directories along %PATH%
635 *
636 * For WinXP and later search order actually depends on registry value:
637 * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
638 */
639
640CURLcode FindWin32CACert(struct OperationConfig *config,
641 curl_sslbackend backend,
642 const char *bundle_file)
643{
644 CURLcode result = CURLE_OK;
645
646 /* Search and set cert file only if libcurl supports SSL.
647 *
648 * If Schannel is the selected SSL backend then these locations are
649 * ignored. We allow setting CA location for schannel only when explicitly
650 * specified by the user via CURLOPT_CAINFO / --cacert.
651 */
652 if((curlinfo->features & CURL_VERSION_SSL) &&
653 backend != CURLSSLBACKEND_SCHANNEL) {
654
655 DWORD res_len;
656 char buf[PATH_MAX];
657 char *ptr = NULL;
658
659 buf[0] = '\0';
660
661 res_len = SearchPathA(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
662 if(res_len > 0) {
663 Curl_safefree(config->cacert);
664 config->cacert = strdup(buf);
665 if(!config->cacert)
666 result = CURLE_OUT_OF_MEMORY;
667 }
668 }
669
670 return result;
671}
672
673
674/* Get a list of all loaded modules with full paths.
675 * Returns slist on success or NULL on error.
676 */
677struct curl_slist *GetLoadedModulePaths(void)
678{
679 HANDLE hnd = INVALID_HANDLE_VALUE;
680 MODULEENTRY32 mod = {0};
681 struct curl_slist *slist = NULL;
682
683 mod.dwSize = sizeof(MODULEENTRY32);
684
685 do {
686 hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
687 } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
688
689 if(hnd == INVALID_HANDLE_VALUE)
690 goto error;
691
692 if(!Module32First(hnd, &mod))
693 goto error;
694
695 do {
696 char *path; /* points to stack allocated buffer */
697 struct curl_slist *temp;
698
699#ifdef UNICODE
700 /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total
701 bytes of multibyte chars won't be more than twice that. */
702 char buffer[sizeof(mod.szExePath) * 2];
703 if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
704 buffer, sizeof(buffer), NULL, NULL))
705 goto error;
706 path = buffer;
707#else
708 path = mod.szExePath;
709#endif
710 temp = curl_slist_append(slist, path);
711 if(!temp)
712 goto error;
713 slist = temp;
714 } while(Module32Next(hnd, &mod));
715
716 goto cleanup;
717
718error:
719 curl_slist_free_all(slist);
720 slist = NULL;
721cleanup:
722 if(hnd != INVALID_HANDLE_VALUE)
723 CloseHandle(hnd);
724 return slist;
725}
726
727#endif /* WIN32 */
728
729#endif /* MSDOS || WIN32 */
730