]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/cygpath.cc
Cygwin: add 3.2.1 release file and add fixes up to this point
[newlib-cygwin.git] / winsup / utils / cygpath.cc
CommitLineData
e05f3227 1/* cygpath.cc -- convert pathnames between Windows and Unix format
1fd5e000
CF
2
3This file is part of Cygwin.
4
5This software is a copyrighted work licensed under the terms of the
6Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7details. */
8
9#include <stdio.h>
10#include <string.h>
26fb7ef5
CV
11#include <wchar.h>
12#include <locale.h>
1fd5e000
CF
13#include <stdlib.h>
14#include <limits.h>
15#include <getopt.h>
138d4f51
CF
16#include <io.h>
17#include <sys/fcntl.h>
1fd5e000 18#include <sys/cygwin.h>
92b499ac 19#include <cygwin/version.h>
418068d4 20#include <ctype.h>
e70dbe77 21#include <wctype.h>
7c03f799 22#include <errno.h>
9cfc9511 23
d21b6359
CV
24#define _WIN32_WINNT 0x0a00
25#define WINVER 0x0a00
9cfc9511
CV
26#define NOCOMATTRIBUTE
27#include <windows.h>
28#include <userenv.h>
29#include <shlobj.h>
30#include <ntdef.h>
31#include <ntdll.h>
32
26fb7ef5 33#include "wide_path.h"
71d8f118 34#include "loadlib.h"
1fd5e000
CF
35
36static char *prog_name;
ca902991 37static char *file_arg, *output_arg;
5aa8817e 38static int path_flag, unix_flag, windows_flag, absolute_flag, cygdrive_flag;
5bb52de4
CV
39static int shortname_flag, longname_flag;
40static int ignore_flag, allusers_flag, output_flag;
fc200025 41static int mixed_flag, options_from_file_flag, mode_flag;
0a23799a 42static UINT codepage;
fc200025 43
1050e57c 44static const char *format_type_arg;
1fd5e000 45
6a344609 46static struct option long_options[] = {
6a344609 47 {(char *) "absolute", no_argument, NULL, 'a'},
1050e57c
CF
48 {(char *) "close", required_argument, NULL, 'c'},
49 {(char *) "dos", no_argument, NULL, 'd'},
50 {(char *) "file", required_argument, NULL, 'f'},
51 {(char *) "help", no_argument, NULL, 'h'},
52 {(char *) "ignore", no_argument, NULL, 'i'},
53 {(char *) "long-name", no_argument, NULL, 'l'},
54 {(char *) "mixed", no_argument, NULL, 'm'},
f135dd3e 55 {(char *) "mode", no_argument, NULL, 'M'},
6a344609
CF
56 {(char *) "option", no_argument, NULL, 'o'},
57 {(char *) "path", no_argument, NULL, 'p'},
5aa8817e 58 {(char *) "proc-cygdrive", no_argument, NULL, 'U'},
1050e57c
CF
59 {(char *) "short-name", no_argument, NULL, 's'},
60 {(char *) "type", required_argument, NULL, 't'},
6a344609 61 {(char *) "unix", no_argument, NULL, 'u'},
92b499ac 62 {(char *) "version", no_argument, NULL, 'V'},
6a344609 63 {(char *) "windows", no_argument, NULL, 'w'},
6a344609
CF
64 {(char *) "allusers", no_argument, NULL, 'A'},
65 {(char *) "desktop", no_argument, NULL, 'D'},
e355de81 66 {(char *) "homeroot", no_argument, NULL, 'H'},
ca902991 67 {(char *) "mydocs", no_argument, NULL, 'O'},
1050e57c
CF
68 {(char *) "smprograms", no_argument, NULL, 'P'},
69 {(char *) "sysdir", no_argument, NULL, 'S'},
70 {(char *) "windir", no_argument, NULL, 'W'},
ca902991 71 {(char *) "folder", required_argument, NULL, 'F'},
0a23799a 72 {(char *) "codepage", required_argument, NULL, 'C'},
6a344609 73 {0, no_argument, 0, 0}
1fd5e000
CF
74};
75
5aa8817e 76static char options[] = "ac:df:hilmMopst:uUVwAC:DHOPSWF:";
9b566b96 77
e7fca6f8 78static void __attribute__ ((__noreturn__))
6a344609 79usage (FILE * stream, int status)
1fd5e000 80{
45b80bb4
CF
81 if (!ignore_flag || !status)
82 fprintf (stream, "\
92b499ac
CV
83Usage: %1$s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\
84 %1$s [-c HANDLE] \n\
85 %1$s [-ADHOPSW] \n\
86 %1$s [-F ID] \n\
87\n\
aa275fe0 88Convert Unix and Windows format paths, or output system path information\n\
bd79b736 89\n\
1050e57c 90Output type options:\n\
92b499ac 91\n\
ca902991 92 -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\
1050e57c 93 -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\
ca902991
CV
94 -M, --mode report on mode of file (binmode or textmode)\n\
95 -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\
4c634492 96 -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\
1050e57c 97 -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\
92b499ac 98\n\
33bd2d12 99Path conversion options:\n\
92b499ac 100\n\
1050e57c 101 -a, --absolute output absolute path\n\
ca902991
CV
102 -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\
103 -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\
5aa8817e
CV
104 -U, --proc-cygdrive Emit /proc/cygdrive path instead of cygdrive prefix\n\
105 when converting Windows path to UNIX path.\n\
ca902991 106 -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\
0a23799a
CV
107 -C, --codepage CP print DOS, Windows, or mixed pathname in Windows\n\
108 codepage CP. CP can be a numeric codepage identifier,\n\
109 or one of the reserved words ANSI, OEM, or UTF8.\n\
92b499ac 110 If this option is missing, %1$s defaults to the\n\
0a23799a 111 character set defined by the current locale.\n\
92b499ac 112\n\
1050e57c 113System information:\n\
92b499ac 114\n\
ca902991
CV
115 -A, --allusers use `All Users' instead of current user for -D, -O, -P\n\
116 -D, --desktop output `Desktop' directory and exit\n\
1050e57c 117 -H, --homeroot output `Profiles' directory (home root) and exit\n\
ca902991
CV
118 -O, --mydocs output `My Documents' directory and exit\n\
119 -P, --smprograms output Start Menu `Programs' directory and exit\n\
120 -S, --sysdir output system directory and exit\n\
121 -W, --windir output `Windows' directory and exit\n\
122 -F, --folder ID output special folder with numeric ID and exit\n\
92b499ac 123", prog_name);
befdf18b
CF
124 if (ignore_flag)
125 /* nothing to do */;
126 else if (stream != stdout)
127 fprintf(stream, "Try `%s --help' for more information.\n", prog_name);
128 else
1050e57c 129 {
befdf18b 130 fprintf (stream, "\
92b499ac 131\n\
33bd2d12 132Other options:\n\
92b499ac 133\n\
1050e57c
CF
134 -f, --file FILE read FILE for input; use - to read from STDIN\n\
135 -o, --option read options from FILE as well (for use with --file)\n\
136 -c, --close HANDLE close HANDLE (for use in captured process)\n\
ca902991 137 -i, --ignore ignore missing argument\n\
1050e57c 138 -h, --help output usage information and exit\n\
92b499ac
CV
139 -V, --version output version information and exit\n\
140\n");
1050e57c 141 }
45b80bb4
CF
142 exit (ignore_flag ? 0 : status);
143}
144
2dba45f4
CV
145static inline BOOLEAN
146RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
147{
148 uni->Length = 0;
d2cc418e 149 uni->MaximumLength = size / sizeof (WCHAR);
2dba45f4
CV
150 uni->Buffer = (WCHAR *) malloc (size);
151 return uni->Buffer != NULL;
152}
153
0a23799a
CV
154static size_t
155my_wcstombs (char *dest, const wchar_t *src, size_t n)
156{
157 if (codepage)
158 return WideCharToMultiByte (codepage, 0, src, -1, dest, n, NULL, NULL);
159 else
160 return wcstombs (dest, src, n);
161}
162
d2cc418e
CV
163#define HARDDISK_PREFIX L"\\Device\\Harddisk"
164#define GLOBALROOT_PREFIX "\\\\.\\GLOBALROOT"
165
2dba45f4
CV
166static char *
167get_device_name (char *path)
168{
169 UNICODE_STRING ntdev, tgtdev, ntdevdir;
170 ANSI_STRING ans;
171 OBJECT_ATTRIBUTES ntobj;
172 NTSTATUS status;
173 HANDLE lnk, dir;
d2cc418e 174 bool got_one = false;
2dba45f4
CV
175 char *ret = strdup (path);
176 PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
177 alloca (4096);
178 BOOLEAN restart;
179 ULONG cont;
180
d2cc418e
CV
181 if (!strncasecmp (path, GLOBALROOT_PREFIX "\\", sizeof (GLOBALROOT_PREFIX)))
182 path += sizeof (GLOBALROOT_PREFIX) - 1;
2dba45f4
CV
183 if (strncasecmp (path, "\\Device\\", 8))
184 return ret;
185
d2cc418e 186 if (!RtlAllocateUnicodeString (&ntdev, 65534))
2dba45f4 187 return ret;
d2cc418e 188 if (!RtlAllocateUnicodeString (&tgtdev, 65534))
2dba45f4
CV
189 return ret;
190 RtlInitAnsiString (&ans, path);
191 RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
192
193 /* First check if the given device name is a symbolic link itself. If so,
194 query it and use the new name as actual device name to search for in the
195 DOS device name directory. If not, just use the incoming device name. */
196 InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL);
9cfc9511 197 status = NtOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
2dba45f4
CV
198 if (NT_SUCCESS (status))
199 {
9cfc9511
CV
200 status = NtQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
201 NtClose (lnk);
2dba45f4
CV
202 if (!NT_SUCCESS (status))
203 goto out;
204 RtlCopyUnicodeString (&ntdev, &tgtdev);
205 }
d2cc418e
CV
206 else if (status != STATUS_OBJECT_TYPE_MISMATCH
207 && status != STATUS_OBJECT_PATH_SYNTAX_BAD)
2dba45f4
CV
208 goto out;
209
210 for (int i = 0; i < 2; ++i)
211 {
212 /* There are two DOS device directories, the local and the global dir.
213 Try both, local first. */
214 RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??");
215
216 /* Open the directory... */
217 InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE,
218 NULL, NULL);
9cfc9511 219 status = NtOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj);
2dba45f4
CV
220 if (!NT_SUCCESS (status))
221 break;
222
223 /* ...and scan it. */
224 for (restart = TRUE, cont = 0;
9cfc9511 225 NT_SUCCESS (NtQueryDirectoryObject (dir, odi, 4096, TRUE,
2dba45f4
CV
226 restart, &cont, NULL));
227 restart = FALSE)
228 {
229 /* For each entry check if it's a symbolic link. */
230 InitializeObjectAttributes (&ntobj, &odi->ObjectName,
231 OBJ_CASE_INSENSITIVE, dir, NULL);
9cfc9511 232 status = NtOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
2dba45f4
CV
233 if (!NT_SUCCESS (status))
234 continue;
235 tgtdev.Length = 0;
236 tgtdev.MaximumLength = 512;
237 /* If so, query it and compare the target of the symlink with the
238 incoming device name. */
9cfc9511
CV
239 status = NtQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
240 NtClose (lnk);
2dba45f4
CV
241 if (!NT_SUCCESS (status))
242 continue;
d2cc418e
CV
243 if (tgtdev.Length /* There's actually a symlink pointing to an
244 empty string: \??\GLOBALROOT -> "" */
245 && RtlEqualUnicodePathPrefix (&ntdev, &tgtdev, TRUE))
2dba45f4
CV
246 {
247 /* If the comparison succeeds, the name of the directory entry is
248 a valid DOS device name, if prepended with "\\.\". Return that
249 valid DOS path. */
d2cc418e
CV
250 wchar_t *trailing = NULL;
251 if (ntdev.Length > tgtdev.Length)
252 trailing = ntdev.Buffer + tgtdev.Length / sizeof (WCHAR);
2dba45f4 253 ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
d2cc418e
CV
254 if (trailing)
255 len += my_wcstombs (NULL, trailing, 0);
bf4b8020 256 free (ret);
2dba45f4
CV
257 ret = (char *) malloc (len + 4);
258 strcpy (ret, "\\\\.\\");
259 ans.Length = 0;
260 ans.MaximumLength = len;
261 ans.Buffer = ret + 4;
262 RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE);
d2cc418e
CV
263 if (trailing)
264 my_wcstombs (ans.Buffer + ans.Length, trailing,
265 ans.MaximumLength - ans.Length);
266 ans.Buffer[ans.MaximumLength - 1] = '\0';
267 got_one = true;
708bbfd0
CV
268 /* Special case for local disks: It's most feasible if the
269 DOS device name reflects the DOS drive, so we check for this
270 explicitly and only return prematurely if so. */
708bbfd0
CV
271 if (ntdev.Length < wcslen (HARDDISK_PREFIX)
272 || wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0
273 || (odi->ObjectName.Length == 2 * sizeof (WCHAR)
274 && odi->ObjectName.Buffer[1] == L':'))
275 {
d2cc418e
CV
276 if (trailing)
277 {
278 /* If there's a trailing path, it's a perfectly valid
279 DOS pathname without the \\.\ prefix. Unless it's
1b23b30b 280 longer than MAX_PATH - 1 in which case it needs
d2cc418e 281 the \\?\ prefix. */
ac5d7dcd 282 if ((len = strlen (ret + 4)) >= MAX_PATH)
d2cc418e
CV
283 ret[2] = '?';
284 else
285 memmove (ret, ret + 4, strlen (ret + 4) + 1);
286 }
9cfc9511 287 NtClose (dir);
708bbfd0
CV
288 goto out;
289 }
2dba45f4
CV
290 }
291 }
9cfc9511 292 NtClose (dir);
2dba45f4
CV
293 }
294
295out:
296 free (tgtdev.Buffer);
297 free (ntdev.Buffer);
d2cc418e
CV
298 if (!got_one)
299 {
300 free (ret);
301 ret = (char *) malloc (sizeof (GLOBALROOT_PREFIX) + strlen (path));
302 if (ret)
303 stpcpy (stpcpy (ret, GLOBALROOT_PREFIX), path);
304 }
2dba45f4
CV
305 return ret;
306}
307
308static char *
309get_device_paths (char *path)
310{
311 char *sbuf;
312 char *ptr;
313 int n = 1;
314
315 ptr = path;
316 while ((ptr = strchr (ptr, ';')))
317 {
318 ptr++;
319 n++;
320 }
321
322 char *paths[n];
323 DWORD acc = 0;
324 int i;
325 if (!n)
326 return strdup ("");
327
328 for (i = 0, ptr = path; ptr; i++)
329 {
330 char *next = ptr;
331 ptr = strchr (ptr, ';');
332 if (ptr)
333 *ptr++ = 0;
334 paths[i] = get_device_name (next);
335 acc += strlen (paths[i]) + 1;
336 }
337
338 sbuf = (char *) malloc (acc + 1);
339 if (sbuf == NULL)
340 {
341 fprintf (stderr, "%s: out of memory\n", prog_name);
342 exit (1);
343 }
344
345 sbuf[0] = '\0';
346 for (i = 0; i < n; i++)
347 {
348 strcat (strcat (sbuf, paths[i]), ";");
349 free (paths[i]);
350 }
351
352 strchr (sbuf, '\0')[-1] = '\0';
353 return sbuf;
354}
355
45b80bb4
CF
356static char *
357get_short_paths (char *path)
358{
26fb7ef5
CV
359 wchar_t *sbuf;
360 wchar_t *sptr;
45b80bb4
CF
361 char *next;
362 char *ptr = path;
363 char *end = strrchr (path, 0);
364 DWORD acc = 0;
365 DWORD len;
366
367 while (ptr != NULL)
45b80bb4 368 {
6a344609
CF
369 next = ptr;
370 ptr = strchr (ptr, ';');
371 if (ptr)
372 *ptr++ = 0;
26fb7ef5
CV
373 wide_path wpath (next);
374 len = GetShortPathNameW (wpath, NULL, 0);
cc3ce0bb 375 if (!len)
6a344609 376 {
92b499ac
CV
377 fprintf (stderr, "%s: cannot create short name of %s\n",
378 prog_name, next);
6a344609
CF
379 exit (2);
380 }
381 acc += len + 1;
45b80bb4 382 }
26fb7ef5 383 sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
45b80bb4 384 if (sbuf == NULL)
45b80bb4 385 {
6a344609
CF
386 fprintf (stderr, "%s: out of memory\n", prog_name);
387 exit (1);
45b80bb4 388 }
6a344609
CF
389 ptr = path;
390 for (;;)
391 {
26fb7ef5
CV
392 wide_path wpath (ptr);
393 len = GetShortPathNameW (wpath, sptr, acc);
cc3ce0bb 394 if (!len)
6a344609 395 {
92b499ac
CV
396 fprintf (stderr, "%s: cannot create short name of %s\n",
397 prog_name, ptr);
6a344609
CF
398 exit (2);
399 }
45b80bb4 400
6a344609 401 ptr = strrchr (ptr, 0);
26fb7ef5 402 sptr = wcsrchr (sptr, 0);
6a344609
CF
403 if (ptr == end)
404 break;
26fb7ef5 405 *sptr = L';';
6a344609 406 ++ptr, ++sptr;
5bb52de4 407 acc -= len + 1;
6a344609 408 }
0a23799a 409 len = my_wcstombs (NULL, sbuf, 0) + 1;
26fb7ef5
CV
410 ptr = (char *) malloc (len);
411 if (ptr == NULL)
412 {
413 fprintf (stderr, "%s: out of memory\n", prog_name);
414 exit (1);
415 }
0a23799a 416 my_wcstombs (ptr, sbuf, len);
584c6643 417 free (sbuf);
26fb7ef5 418 return ptr;
45b80bb4
CF
419}
420
421static char *
422get_short_name (const char *filename)
423{
26fb7ef5
CV
424 wchar_t buf[32768];
425 char *sbuf;
426 wide_path wpath (filename);
427 DWORD len = GetShortPathNameW (wpath, buf, 32768);
cc3ce0bb 428 if (!len)
6a344609 429 {
92b499ac
CV
430 fprintf (stderr, "%s: cannot create short name of %s\n",
431 prog_name, filename);
6a344609
CF
432 exit (2);
433 }
0a23799a 434 len = my_wcstombs (NULL, buf, 0) + 1;
26fb7ef5 435 sbuf = (char *) malloc (len);
45b80bb4 436 if (sbuf == NULL)
6a344609
CF
437 {
438 fprintf (stderr, "%s: out of memory\n", prog_name);
439 exit (1);
440 }
0a23799a 441 my_wcstombs (sbuf, buf, len);
26fb7ef5 442 return sbuf;
5bb52de4
CV
443}
444
5bb52de4 445static char *
cf157504 446get_long_name (const char *filename, DWORD& len)
5bb52de4 447{
26fb7ef5
CV
448 char *sbuf;
449 wchar_t buf[32768];
26fb7ef5 450 wide_path wpath (filename);
acc31d1a 451
71148bfc
CV
452 if (!GetLongPathNameW (wpath, buf, 32768))
453 wcscpy (buf, wpath);
0a23799a 454 len = my_wcstombs (NULL, buf, 0);
cf157504
CF
455 sbuf = (char *) malloc (len + 1);
456 if (!sbuf)
5bb52de4
CV
457 {
458 fprintf (stderr, "%s: out of memory\n", prog_name);
459 exit (1);
460 }
0a23799a 461 my_wcstombs (sbuf, buf, len + 1);
26fb7ef5 462 return sbuf;
5bb52de4
CV
463}
464
465static char *
cf157504 466get_long_paths (char *path)
5bb52de4 467{
cf157504
CF
468 char *sbuf;
469 char *ptr;
470 int n = 1;
471
472 ptr = path;
473 while ((ptr = strchr (ptr, ';')))
6a344609 474 {
cf157504
CF
475 ptr++;
476 n++;
6a344609 477 }
cf157504
CF
478
479 char *paths[n];
480 DWORD acc = 0;
481 int i;
482 if (!n)
483 return strdup ("");
484
485 for (i = 0, ptr = path; ptr; i++)
486 {
487 DWORD len;
488 char *next = ptr;
489 ptr = strchr (ptr, ';');
490 if (ptr)
491 *ptr++ = 0;
492 paths[i] = get_long_name (next, len);
493 acc += len + 1;
494 }
495
496 sbuf = (char *) malloc (acc + 1);
5bb52de4
CV
497 if (sbuf == NULL)
498 {
499 fprintf (stderr, "%s: out of memory\n", prog_name);
500 exit (1);
501 }
cf157504
CF
502
503 sbuf[0] = '\0';
504 for (i = 0; i < n; i++)
505 {
506 strcat (strcat (sbuf, paths[i]), ";");
507 free (paths[i]);
508 }
509
510 strchr (sbuf, '\0')[-1] = '\0';
511 return sbuf;
5bb52de4
CV
512}
513
33bd2d12
CF
514static void
515convert_slashes (char* name)
516{
517 while ((name = strchr (name, '\\')) != NULL)
518 {
519 if (*name == '\\')
520 *name = '/';
521 name++;
522 }
523}
524
ca902991 525static bool
71148bfc 526get_special_folder (PWCHAR wpath, int id)
ca902991 527{
ca902991
CV
528 LPITEMIDLIST pidl = 0;
529 if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
530 return false;
175e39bb 531 if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
ca902991
CV
532 return false;
533 return true;
534}
535
5bb52de4 536static void
fc200025 537do_sysfolders (char option)
5bb52de4 538{
175e39bb 539 WCHAR wbuf[MAX_PATH];
71148bfc 540 char buf[PATH_MAX];
79b1b77b 541 BOOL iswow64 = FALSE;
cf157504 542
71148bfc 543 wbuf[0] = L'\0';
5bb52de4
CV
544 switch (option)
545 {
546 case 'D':
71148bfc 547 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
175e39bb 548 : CSIDL_DESKTOPDIRECTORY);
5bb52de4
CV
549 break;
550
551 case 'P':
71148bfc 552 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_PROGRAMS
175e39bb 553 : CSIDL_PROGRAMS);
ca902991
CV
554 break;
555
556 case 'O':
71148bfc 557 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
175e39bb 558 : CSIDL_PERSONAL);
ca902991
CV
559 break;
560
561 case 'F':
562 {
563 int val = -1, len = -1;
564 if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
565 && len == (int) strlen (output_arg) && val >= 0))
566 {
567 fprintf (stderr, "%s: syntax error in special folder ID %s\n",
568 prog_name, output_arg);
569 exit (1);
570 }
71148bfc 571 get_special_folder (wbuf, val);
ca902991 572 }
5bb52de4
CV
573 break;
574
575 case 'H':
71148bfc
CV
576 {
577 DWORD len = MAX_PATH;
578 GetProfilesDirectoryW (wbuf, &len);
579 }
5bb52de4
CV
580 break;
581
582 case 'S':
e70dbe77 583 GetSystemDirectoryW (wbuf, MAX_PATH);
79b1b77b
CV
584 if (!windows_flag
585 && IsWow64Process (GetCurrentProcess (), &iswow64) && iswow64)
586 {
587 /* When calling NtQueryInformationFile(FileNameInformation) on WOW64,
588 the returned path will point to SysWOW64. This breaks path
589 redirection to the network related files under device/etc. This
590 here is a bad hack to make sure that the conversion will convert
591 the case *and* stick to System32. */
592 PWCHAR last_bs = wcsrchr (wbuf, L'\\');
593 if (last_bs)
594 wcpcpy (last_bs + 1, L"Sysnative");
595 }
5bb52de4
CV
596 break;
597
598 case 'W':
71148bfc 599 GetSystemWindowsDirectoryW (wbuf, MAX_PATH);
5bb52de4
CV
600 break;
601
602 default:
603 usage (stderr, 1);
604 }
605
71148bfc 606 if (!wbuf[0])
ca902991 607 {
92b499ac
CV
608 fprintf (stderr, "%s: failed to retrieve special folder path\n",
609 prog_name);
e70dbe77 610 return;
ca902991
CV
611 }
612 else if (!windows_flag)
5bb52de4 613 {
e70dbe77
CV
614 /* The system folders are not necessarily case-correct. To allow
615 case-sensitivity, try to correct the case. Note that this only
616 works for local filesystems. */
617 if (iswalpha (wbuf[0]) && wbuf[1] == L':' && wbuf[2] == L'\\')
618 {
619 OBJECT_ATTRIBUTES attr;
620 NTSTATUS status;
621 HANDLE h;
622 IO_STATUS_BLOCK io;
623 UNICODE_STRING upath;
624 const ULONG size = sizeof (FILE_NAME_INFORMATION)
625 + PATH_MAX * sizeof (WCHAR);
626 PFILE_NAME_INFORMATION pfni = (PFILE_NAME_INFORMATION) alloca (size);
627
628 /* Avoid another buffer, reuse pfni. */
629 wcpcpy (wcpcpy (pfni->FileName, L"\\??\\"), wbuf);
630 RtlInitUnicodeString (&upath, pfni->FileName);
631 InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE,
632 NULL, NULL);
633 status = NtOpenFile (&h, READ_CONTROL, &attr, &io,
634 FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT);
635 if (NT_SUCCESS (status))
636 {
637 status = NtQueryInformationFile (h, &io, pfni, size,
638 FileNameInformation);
639 if (NT_SUCCESS (status))
640 {
641 pfni->FileName[pfni->FileNameLength / sizeof (WCHAR)] = L'\0';
642 wcscpy (wbuf + 2, pfni->FileName);
643 }
644 NtClose (h);
645 }
646 }
5aa8817e
CV
647 if (cygwin_conv_path (CCP_WIN_W_TO_POSIX | cygdrive_flag,
648 wbuf, buf, PATH_MAX))
71148bfc
CV
649 fprintf (stderr, "%s: error converting \"%ls\" - %s\n",
650 prog_name, wbuf, strerror (errno));
5bb52de4
CV
651 }
652 else
653 {
654 if (shortname_flag)
71148bfc
CV
655 /* System paths are never longer than MAX_PATH. The buffer pointers
656 in a call to GetShortPathNameW may point to the same buffer. */
657 GetShortPathNameW (wbuf, wbuf, MAX_PATH);
658 my_wcstombs (buf, wbuf, MAX_PATH);
33bd2d12 659 if (mixed_flag)
1b526b48 660 convert_slashes (buf);
5bb52de4
CV
661 }
662 printf ("%s\n", buf);
1fd5e000
CF
663}
664
f135dd3e
CF
665static void
666report_mode (char *filename)
667{
668 switch (cygwin_internal (CW_GET_BINMODE, filename))
669 {
670 case O_BINARY:
671 printf ("%s: binary\n", filename);
672 break;
673 case O_TEXT:
674 printf ("%s: text\n", filename);
675 break;
676 default:
92b499ac
CV
677 fprintf (stderr, "%s: file '%s' - %s\n", prog_name,
678 filename, strerror (errno));
f135dd3e
CF
679 break;
680 }
681}
682
138d4f51 683static void
fc200025 684do_pathconv (char *filename)
138d4f51 685{
bf4b8020
CV
686 char *buf = NULL, *tmp;
687 wchar_t *buf2 = NULL;
71148bfc 688 DWORD len = 32768;
2b2b42cf 689 ssize_t err;
80206d7f 690 bool print_tmp = false;
2b2b42cf 691 cygwin_conv_path_t conv_func =
71148bfc 692 (unix_flag ? CCP_WIN_W_TO_POSIX : CCP_POSIX_TO_WIN_W)
5aa8817e 693 | absolute_flag | cygdrive_flag;
138d4f51 694
71148bfc 695 if (!filename || !filename[0])
c02e32c9 696 {
71148bfc
CV
697 if (ignore_flag)
698 return;
699 fprintf (stderr, "%s: can't convert empty path\n", prog_name);
700 exit (1);
c02e32c9 701 }
138d4f51
CF
702
703 buf = (char *) malloc (len);
71148bfc 704 if (!unix_flag)
26fb7ef5 705 buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
138d4f51
CF
706 if (buf == NULL)
707 {
708 fprintf (stderr, "%s: out of memory\n", prog_name);
709 exit (1);
710 }
711
712 if (path_flag)
713 {
71148bfc
CV
714 if (unix_flag)
715 {
733b554b 716 wide_path wpath (filename, false);
71148bfc
CV
717 err = cygwin_conv_path_list (conv_func, wpath, buf, len);
718 }
719 else
720 err = cygwin_conv_path_list (conv_func, filename, buf2, len);
721 if (err)
722 {
723 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
724 prog_name, filename, strerror (errno));
725 exit (1);
726 }
2b2b42cf 727 if (!unix_flag)
6a344609 728 {
71148bfc 729 my_wcstombs (buf, buf2, 32768);
bf4b8020
CV
730 buf = get_device_paths (tmp = buf);
731 free (tmp);
6a344609 732 if (shortname_flag)
bf4b8020
CV
733 {
734 buf = get_short_paths (tmp = buf);
735 free (tmp);
736 }
5bb52de4 737 if (longname_flag)
bf4b8020
CV
738 {
739 buf = get_long_paths (tmp = buf);
740 free (tmp);
741 }
2bd6505b 742 if (mixed_flag)
1b526b48 743 convert_slashes (buf);
6a344609 744 }
138d4f51
CF
745 }
746 else
747 {
71148bfc
CV
748 if (unix_flag)
749 {
750 wide_path wpath (filename);
751 err = cygwin_conv_path (conv_func, wpath, (void *) buf, len);
752 }
753 else
754 err = cygwin_conv_path (conv_func, filename, (void *) buf2, len);
7c03f799 755 if (err)
6a344609 756 {
7c03f799
CF
757 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
758 prog_name, filename, strerror (errno));
6a344609
CF
759 exit (1);
760 }
5bb52de4
CV
761 if (!unix_flag)
762 {
0a23799a 763 my_wcstombs (buf, buf2, 32768);
bf4b8020
CV
764 buf = get_device_name (tmp = buf);
765 free (tmp);
7ca68b7e 766 if (shortname_flag)
bf4b8020
CV
767 {
768 buf = get_short_name (tmp = buf);
769 free (tmp);
770 }
7ca68b7e 771 if (longname_flag)
bf4b8020
CV
772 {
773 buf = get_long_name (tmp = buf, len);
774 free (tmp);
775 }
bf4b8020 776 tmp = buf;
8c3a79bb 777 if (strncmp (buf, "\\\\?\\", 4) == 0)
26fb7ef5 778 {
d2cc418e
CV
779 len = 0;
780 if (buf[5] == ':')
781 len = 4;
782 else if (!strncmp (buf + 4, "UNC\\", 4))
8c3a79bb 783 len = 6;
d2cc418e 784 if (len && strlen (buf) < MAX_PATH + len)
8c3a79bb 785 {
03ac74c1 786 tmp += len;
8c3a79bb 787 if (len == 6)
03ac74c1 788 *tmp = '\\';
80206d7f 789 print_tmp = true;
8c3a79bb 790 }
26fb7ef5 791 }
8c3a79bb 792 if (mixed_flag)
03ac74c1 793 convert_slashes (tmp);
5bb52de4 794 }
138d4f51
CF
795 }
796
80206d7f 797 puts (print_tmp ? tmp : buf);
bf4b8020
CV
798 if (buf2)
799 free (buf2);
800 if (buf)
03ac74c1 801 free (buf);
138d4f51
CF
802}
803
6a344609
CF
804static void
805print_version ()
806{
92b499ac
CV
807 printf ("cygpath (cygwin) %d.%d.%d\n"
808 "Path Conversion Utility\n"
6e623e93 809 "Copyright (C) 1998 - %s Cygwin Authors\n"
92b499ac
CV
810 "This is free software; see the source for copying conditions. There is NO\n"
811 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
812 CYGWIN_VERSION_DLL_MAJOR / 1000,
813 CYGWIN_VERSION_DLL_MAJOR % 1000,
814 CYGWIN_VERSION_DLL_MINOR,
815 strrchr (__DATE__, ' ') + 1);
6a344609
CF
816}
817
fc200025
CV
818static int
819do_options (int argc, char **argv, int from_file)
1fd5e000 820{
6a344609 821 int c, o = 0;
1fd5e000 822 path_flag = 0;
fc200025 823 unix_flag = 0;
1fd5e000 824 windows_flag = 0;
45b80bb4 825 shortname_flag = 0;
5bb52de4 826 longname_flag = 0;
2bd6505b 827 mixed_flag = 0;
45b80bb4 828 ignore_flag = 0;
3cdacffc
CV
829 allusers_flag = 0;
830 output_flag = 0;
f135dd3e 831 mode_flag = 0;
0a23799a 832 codepage = 0;
5aa8817e
CV
833 cygdrive_flag = 0;
834 absolute_flag = CCP_RELATIVE;
fc200025
CV
835 if (!from_file)
836 options_from_file_flag = 0;
837 optind = 0;
9b566b96 838 while ((c = getopt_long (argc, argv, options,
2bd6505b 839 long_options, (int *) NULL)) != EOF)
1fd5e000
CF
840 {
841 switch (c)
842 {
418068d4 843 case 'a':
5aa8817e 844 absolute_flag = CCP_ABSOLUTE;
418068d4
CF
845 break;
846
847 case 'c':
fc200025
CV
848 if (!optarg)
849 usage (stderr, 1);
418068d4
CF
850 CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
851 break;
852
1050e57c 853 case 'd':
1050e57c
CF
854 windows_flag = 1;
855 shortname_flag = 1;
856 break;
857
138d4f51 858 case 'f':
fc200025
CV
859 if (from_file || !optarg)
860 usage (stderr, 1);
138d4f51
CF
861 file_arg = optarg;
862 break;
863
f135dd3e
CF
864 case 'M':
865 mode_flag = 1;
866 break;
867
418068d4 868 case 'o':
fc200025
CV
869 if (from_file)
870 usage (stderr, 1);
418068d4
CF
871 options_from_file_flag = 1;
872 break;
873
1fd5e000
CF
874 case 'p':
875 path_flag = 1;
876 break;
877
878 case 'u':
1fd5e000
CF
879 unix_flag = 1;
880 break;
881
882 case 'w':
1fd5e000
CF
883 windows_flag = 1;
884 break;
885
1050e57c 886 case 'm':
1050e57c
CF
887 windows_flag = 1;
888 mixed_flag = 1;
889 break;
890
5bb52de4 891 case 'l':
5bb52de4
CV
892 longname_flag = 1;
893 break;
894
45b80bb4 895 case 's':
45b80bb4
CF
896 shortname_flag = 1;
897 break;
898
fc200025
CV
899 case 't':
900 if (!optarg)
2bd6505b
CF
901 usage (stderr, 1);
902
1050e57c
CF
903 format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
904 if (strcasecmp (format_type_arg, "dos") == 0)
cf157504 905 {
fc200025
CV
906 windows_flag = 1;
907 shortname_flag = 1;
cf157504 908 }
fc200025 909 else if (!strcasecmp (format_type_arg, "mixed"))
cf157504 910 {
fc200025
CV
911 windows_flag = 1;
912 mixed_flag = 1;
cf157504 913 }
fc200025 914 else if (!strcasecmp (format_type_arg, "unix"))
1050e57c 915 unix_flag = 1;
fc200025 916 else if (!strcasecmp (format_type_arg, "windows"))
cf157504 917 windows_flag = 1;
2bd6505b
CF
918 else
919 usage (stderr, 1);
920 break;
921
3cdacffc
CV
922 case 'A':
923 allusers_flag = 1;
924 break;
925
5aa8817e
CV
926 case 'U':
927 cygdrive_flag = CCP_PROC_CYGDRIVE;
928 break;
929
0a23799a
CV
930 case 'C':
931 if (!optarg)
932 usage (stderr, 1);
933 if (!strcasecmp (optarg, "ANSI"))
934 codepage = GetACP ();
935 else if (!strcasecmp (optarg, "OEM"))
936 codepage = GetOEMCP ();
937 else if (!strcasecmp (optarg, "UTF8")
938 || !strcasecmp (optarg, "UTF-8"))
939 codepage = CP_UTF8;
940 else
941 {
942 char *c;
943 codepage = (UINT) strtoul (optarg, &c, 10);
944 if (*c)
945 usage (stderr, 1);
946 }
947 break;
948
3cdacffc 949 case 'D':
e355de81 950 case 'H':
ca902991 951 case 'O':
3cdacffc 952 case 'P':
f00c1d2c 953 case 'S':
3cdacffc 954 case 'W':
fc200025 955 ++output_flag;
e355de81 956 o = c;
3cdacffc 957 break;
f00c1d2c 958
ca902991 959 case 'F':
fc200025 960 if (!optarg)
ca902991 961 usage (stderr, 1);
fc200025 962 ++output_flag;
ca902991
CV
963 output_arg = optarg;
964 o = c;
965 break;
966
45b80bb4
CF
967 case 'i':
968 ignore_flag = 1;
969 break;
970
1fd5e000
CF
971 case 'h':
972 usage (stdout, 0);
1fd5e000 973
92b499ac 974 case 'V':
6a344609 975 print_version ();
1fd5e000
CF
976 exit (0);
977
978 default:
92b499ac
CV
979 fprintf (stderr, "Try `%s --help' for more information.\n",
980 prog_name);
981 exit (1);
1fd5e000
CF
982 }
983 }
984
fc200025 985 /* If none of the "important" flags are set, -u is default. */
df1841c3
CV
986 if (!unix_flag && !windows_flag && !mode_flag
987 && (!from_file ? !options_from_file_flag : 1))
fc200025
CV
988 unix_flag = 1;
989
990 /* Only one of ... */
df1841c3 991 if (unix_flag + windows_flag + mode_flag > 1
fc200025 992 + (!from_file ? options_from_file_flag : 0))
418068d4
CF
993 usage (stderr, 1);
994
fc200025
CV
995 /* options_from_file_flag requires a file. */
996 if (!from_file && options_from_file_flag && !file_arg)
1050e57c
CF
997 usage (stderr, 1);
998
fc200025
CV
999 /* longname and shortname don't play well together. */
1000 if (longname_flag && shortname_flag)
1050e57c
CF
1001 usage (stderr, 1);
1002
fc200025
CV
1003 /* longname and shortname only make sense with Windows paths. */
1004 if ((longname_flag || shortname_flag) && !windows_flag)
1fd5e000
CF
1005 usage (stderr, 1);
1006
fc200025
CV
1007 return o;
1008}
1009
1010static void
1011action (int argc, char **argv, int opt)
1012{
1013 if (output_flag)
1fd5e000 1014 {
fc200025
CV
1015 if (argv[optind])
1016 usage (stderr, 1);
5bb52de4 1017
fc200025
CV
1018 do_sysfolders (opt);
1019 }
1020 else
1021 {
0a5ea947 1022 if (optind > argc - 1)
138d4f51 1023 usage (stderr, 1);
1fd5e000 1024
f135dd3e
CF
1025 for (int i = optind; argv[i]; i++)
1026 if (mode_flag)
1027 report_mode (argv[i]);
1028 else
fc200025 1029 do_pathconv (argv[i]);
138d4f51 1030 }
fc200025
CV
1031}
1032
1033int
1034main (int argc, char **argv)
1035{
1036 int o;
1037
73535010 1038 setlocale (LC_CTYPE, "");
92b499ac 1039 prog_name = program_invocation_short_name;
fc200025
CV
1040
1041 o = do_options (argc, argv, 0);
1042
1043 if (!file_arg)
1044 action (argc, argv, o);
1fd5e000
CF
1045 else
1046 {
138d4f51
CF
1047 FILE *fp;
1048 char buf[PATH_MAX * 2 + 1];
1fd5e000 1049
138d4f51
CF
1050 if (argv[optind])
1051 usage (stderr, 1);
1fd5e000 1052
fc200025
CV
1053 if (strcmp (file_arg, "-"))
1054 {
1055 if (!(fp = fopen (file_arg, "rt")))
1056 {
1057 perror ("cygpath");
1058 exit (1);
1059 }
1060 }
1fd5e000 1061 else
138d4f51
CF
1062 {
1063 fp = stdin;
1064 setmode (0, O_TEXT);
1065 }
418068d4 1066 setbuf (stdout, NULL);
fc200025
CV
1067
1068 while (fgets (buf, sizeof (buf), fp))
138d4f51 1069 {
6004370b
CV
1070 int ac = 0;
1071 char *av[4] = { NULL, NULL, NULL, NULL };
fc200025 1072 char *p = strchr (buf, '\n');
138d4f51
CF
1073 if (p)
1074 *p = '\0';
6004370b
CV
1075 p = buf;
1076 av[ac++] = prog_name;
1077 av[ac++] = p;
1078 if (options_from_file_flag && *p == '-')
fc200025 1079 {
6004370b
CV
1080 while (*p && !isspace (*p))
1081 ++p;
1082 if (*p)
1083 {
1084 *p++ = '\0';
1085 while (*p && isspace (*p))
1086 ++p;
1087 av[ac++] = p;
1088 }
1089 o = do_options (ac, av, 1);
fc200025 1090 }
6004370b 1091 else
418068d4 1092 {
6004370b
CV
1093 output_flag = 0;
1094 optind = 1;
418068d4 1095 }
fc200025 1096 action (ac, av, o);
138d4f51
CF
1097 }
1098 }
1fd5e000
CF
1099 exit (0);
1100}
This page took 0.461306 seconds and 5 git commands to generate.