]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/cygpath.cc
Remove pre-Vista considerations from utilities
[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
CV
23
24#define _WIN32_WINNT 0x0602
25#define WINVER 0x0602
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
1fd5e000 78static void
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);
26fb7ef5 417 return ptr;
45b80bb4
CF
418}
419
420static char *
421get_short_name (const char *filename)
422{
26fb7ef5
CV
423 wchar_t buf[32768];
424 char *sbuf;
425 wide_path wpath (filename);
426 DWORD len = GetShortPathNameW (wpath, buf, 32768);
cc3ce0bb 427 if (!len)
6a344609 428 {
92b499ac
CV
429 fprintf (stderr, "%s: cannot create short name of %s\n",
430 prog_name, filename);
6a344609
CF
431 exit (2);
432 }
0a23799a 433 len = my_wcstombs (NULL, buf, 0) + 1;
26fb7ef5 434 sbuf = (char *) malloc (len);
45b80bb4 435 if (sbuf == NULL)
6a344609
CF
436 {
437 fprintf (stderr, "%s: out of memory\n", prog_name);
438 exit (1);
439 }
0a23799a 440 my_wcstombs (sbuf, buf, len);
26fb7ef5 441 return sbuf;
5bb52de4
CV
442}
443
5bb52de4 444static char *
cf157504 445get_long_name (const char *filename, DWORD& len)
5bb52de4 446{
26fb7ef5
CV
447 char *sbuf;
448 wchar_t buf[32768];
26fb7ef5 449 wide_path wpath (filename);
acc31d1a 450
71148bfc
CV
451 if (!GetLongPathNameW (wpath, buf, 32768))
452 wcscpy (buf, wpath);
0a23799a 453 len = my_wcstombs (NULL, buf, 0);
cf157504
CF
454 sbuf = (char *) malloc (len + 1);
455 if (!sbuf)
5bb52de4
CV
456 {
457 fprintf (stderr, "%s: out of memory\n", prog_name);
458 exit (1);
459 }
0a23799a 460 my_wcstombs (sbuf, buf, len + 1);
26fb7ef5 461 return sbuf;
5bb52de4
CV
462}
463
464static char *
cf157504 465get_long_paths (char *path)
5bb52de4 466{
cf157504
CF
467 char *sbuf;
468 char *ptr;
469 int n = 1;
470
471 ptr = path;
472 while ((ptr = strchr (ptr, ';')))
6a344609 473 {
cf157504
CF
474 ptr++;
475 n++;
6a344609 476 }
cf157504
CF
477
478 char *paths[n];
479 DWORD acc = 0;
480 int i;
481 if (!n)
482 return strdup ("");
483
484 for (i = 0, ptr = path; ptr; i++)
485 {
486 DWORD len;
487 char *next = ptr;
488 ptr = strchr (ptr, ';');
489 if (ptr)
490 *ptr++ = 0;
491 paths[i] = get_long_name (next, len);
492 acc += len + 1;
493 }
494
495 sbuf = (char *) malloc (acc + 1);
5bb52de4
CV
496 if (sbuf == NULL)
497 {
498 fprintf (stderr, "%s: out of memory\n", prog_name);
499 exit (1);
500 }
cf157504
CF
501
502 sbuf[0] = '\0';
503 for (i = 0; i < n; i++)
504 {
505 strcat (strcat (sbuf, paths[i]), ";");
506 free (paths[i]);
507 }
508
509 strchr (sbuf, '\0')[-1] = '\0';
510 return sbuf;
5bb52de4
CV
511}
512
33bd2d12
CF
513static void
514convert_slashes (char* name)
515{
516 while ((name = strchr (name, '\\')) != NULL)
517 {
518 if (*name == '\\')
519 *name = '/';
520 name++;
521 }
522}
523
ca902991 524static bool
71148bfc 525get_special_folder (PWCHAR wpath, int id)
ca902991 526{
ca902991
CV
527 LPITEMIDLIST pidl = 0;
528 if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
529 return false;
175e39bb 530 if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
ca902991
CV
531 return false;
532 return true;
533}
534
5bb52de4 535static void
fc200025 536do_sysfolders (char option)
5bb52de4 537{
175e39bb 538 WCHAR wbuf[MAX_PATH];
71148bfc 539 char buf[PATH_MAX];
79b1b77b 540 BOOL iswow64 = FALSE;
cf157504 541
71148bfc 542 wbuf[0] = L'\0';
5bb52de4
CV
543 switch (option)
544 {
545 case 'D':
71148bfc 546 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
175e39bb 547 : CSIDL_DESKTOPDIRECTORY);
5bb52de4
CV
548 break;
549
550 case 'P':
71148bfc 551 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_PROGRAMS
175e39bb 552 : CSIDL_PROGRAMS);
ca902991
CV
553 break;
554
555 case 'O':
71148bfc 556 get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
175e39bb 557 : CSIDL_PERSONAL);
ca902991
CV
558 break;
559
560 case 'F':
561 {
562 int val = -1, len = -1;
563 if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
564 && len == (int) strlen (output_arg) && val >= 0))
565 {
566 fprintf (stderr, "%s: syntax error in special folder ID %s\n",
567 prog_name, output_arg);
568 exit (1);
569 }
71148bfc 570 get_special_folder (wbuf, val);
ca902991 571 }
5bb52de4
CV
572 break;
573
574 case 'H':
71148bfc
CV
575 {
576 DWORD len = MAX_PATH;
577 GetProfilesDirectoryW (wbuf, &len);
578 }
5bb52de4
CV
579 break;
580
581 case 'S':
e70dbe77 582 GetSystemDirectoryW (wbuf, MAX_PATH);
79b1b77b
CV
583 if (!windows_flag
584 && IsWow64Process (GetCurrentProcess (), &iswow64) && iswow64)
585 {
586 /* When calling NtQueryInformationFile(FileNameInformation) on WOW64,
587 the returned path will point to SysWOW64. This breaks path
588 redirection to the network related files under device/etc. This
589 here is a bad hack to make sure that the conversion will convert
590 the case *and* stick to System32. */
591 PWCHAR last_bs = wcsrchr (wbuf, L'\\');
592 if (last_bs)
593 wcpcpy (last_bs + 1, L"Sysnative");
594 }
5bb52de4
CV
595 break;
596
597 case 'W':
71148bfc 598 GetSystemWindowsDirectoryW (wbuf, MAX_PATH);
5bb52de4
CV
599 break;
600
601 default:
602 usage (stderr, 1);
603 }
604
71148bfc 605 if (!wbuf[0])
ca902991 606 {
92b499ac
CV
607 fprintf (stderr, "%s: failed to retrieve special folder path\n",
608 prog_name);
e70dbe77 609 return;
ca902991
CV
610 }
611 else if (!windows_flag)
5bb52de4 612 {
e70dbe77
CV
613 /* The system folders are not necessarily case-correct. To allow
614 case-sensitivity, try to correct the case. Note that this only
615 works for local filesystems. */
616 if (iswalpha (wbuf[0]) && wbuf[1] == L':' && wbuf[2] == L'\\')
617 {
618 OBJECT_ATTRIBUTES attr;
619 NTSTATUS status;
620 HANDLE h;
621 IO_STATUS_BLOCK io;
622 UNICODE_STRING upath;
623 const ULONG size = sizeof (FILE_NAME_INFORMATION)
624 + PATH_MAX * sizeof (WCHAR);
625 PFILE_NAME_INFORMATION pfni = (PFILE_NAME_INFORMATION) alloca (size);
626
627 /* Avoid another buffer, reuse pfni. */
628 wcpcpy (wcpcpy (pfni->FileName, L"\\??\\"), wbuf);
629 RtlInitUnicodeString (&upath, pfni->FileName);
630 InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE,
631 NULL, NULL);
632 status = NtOpenFile (&h, READ_CONTROL, &attr, &io,
633 FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT);
634 if (NT_SUCCESS (status))
635 {
636 status = NtQueryInformationFile (h, &io, pfni, size,
637 FileNameInformation);
638 if (NT_SUCCESS (status))
639 {
640 pfni->FileName[pfni->FileNameLength / sizeof (WCHAR)] = L'\0';
641 wcscpy (wbuf + 2, pfni->FileName);
642 }
643 NtClose (h);
644 }
645 }
5aa8817e
CV
646 if (cygwin_conv_path (CCP_WIN_W_TO_POSIX | cygdrive_flag,
647 wbuf, buf, PATH_MAX))
71148bfc
CV
648 fprintf (stderr, "%s: error converting \"%ls\" - %s\n",
649 prog_name, wbuf, strerror (errno));
5bb52de4
CV
650 }
651 else
652 {
653 if (shortname_flag)
71148bfc
CV
654 /* System paths are never longer than MAX_PATH. The buffer pointers
655 in a call to GetShortPathNameW may point to the same buffer. */
656 GetShortPathNameW (wbuf, wbuf, MAX_PATH);
657 my_wcstombs (buf, wbuf, MAX_PATH);
33bd2d12 658 if (mixed_flag)
1b526b48 659 convert_slashes (buf);
5bb52de4
CV
660 }
661 printf ("%s\n", buf);
1fd5e000
CF
662}
663
f135dd3e
CF
664static void
665report_mode (char *filename)
666{
667 switch (cygwin_internal (CW_GET_BINMODE, filename))
668 {
669 case O_BINARY:
670 printf ("%s: binary\n", filename);
671 break;
672 case O_TEXT:
673 printf ("%s: text\n", filename);
674 break;
675 default:
92b499ac
CV
676 fprintf (stderr, "%s: file '%s' - %s\n", prog_name,
677 filename, strerror (errno));
f135dd3e
CF
678 break;
679 }
680}
681
138d4f51 682static void
fc200025 683do_pathconv (char *filename)
138d4f51 684{
bf4b8020
CV
685 char *buf = NULL, *tmp;
686 wchar_t *buf2 = NULL;
71148bfc 687 DWORD len = 32768;
2b2b42cf 688 ssize_t err;
80206d7f 689 bool print_tmp = false;
2b2b42cf 690 cygwin_conv_path_t conv_func =
71148bfc 691 (unix_flag ? CCP_WIN_W_TO_POSIX : CCP_POSIX_TO_WIN_W)
5aa8817e 692 | absolute_flag | cygdrive_flag;
138d4f51 693
71148bfc 694 if (!filename || !filename[0])
c02e32c9 695 {
71148bfc
CV
696 if (ignore_flag)
697 return;
698 fprintf (stderr, "%s: can't convert empty path\n", prog_name);
699 exit (1);
c02e32c9 700 }
138d4f51
CF
701
702 buf = (char *) malloc (len);
71148bfc 703 if (!unix_flag)
26fb7ef5 704 buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
138d4f51
CF
705 if (buf == NULL)
706 {
707 fprintf (stderr, "%s: out of memory\n", prog_name);
708 exit (1);
709 }
710
711 if (path_flag)
712 {
71148bfc
CV
713 if (unix_flag)
714 {
733b554b 715 wide_path wpath (filename, false);
71148bfc
CV
716 err = cygwin_conv_path_list (conv_func, wpath, buf, len);
717 }
718 else
719 err = cygwin_conv_path_list (conv_func, filename, buf2, len);
720 if (err)
721 {
722 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
723 prog_name, filename, strerror (errno));
724 exit (1);
725 }
2b2b42cf 726 if (!unix_flag)
6a344609 727 {
71148bfc 728 my_wcstombs (buf, buf2, 32768);
bf4b8020
CV
729 buf = get_device_paths (tmp = buf);
730 free (tmp);
6a344609 731 if (shortname_flag)
bf4b8020
CV
732 {
733 buf = get_short_paths (tmp = buf);
734 free (tmp);
735 }
5bb52de4 736 if (longname_flag)
bf4b8020
CV
737 {
738 buf = get_long_paths (tmp = buf);
739 free (tmp);
740 }
2bd6505b 741 if (mixed_flag)
1b526b48 742 convert_slashes (buf);
6a344609 743 }
138d4f51
CF
744 }
745 else
746 {
71148bfc
CV
747 if (unix_flag)
748 {
749 wide_path wpath (filename);
750 err = cygwin_conv_path (conv_func, wpath, (void *) buf, len);
751 }
752 else
753 err = cygwin_conv_path (conv_func, filename, (void *) buf2, len);
7c03f799 754 if (err)
6a344609 755 {
7c03f799
CF
756 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
757 prog_name, filename, strerror (errno));
6a344609
CF
758 exit (1);
759 }
5bb52de4
CV
760 if (!unix_flag)
761 {
0a23799a 762 my_wcstombs (buf, buf2, 32768);
bf4b8020
CV
763 buf = get_device_name (tmp = buf);
764 free (tmp);
7ca68b7e 765 if (shortname_flag)
bf4b8020
CV
766 {
767 buf = get_short_name (tmp = buf);
768 free (tmp);
769 }
7ca68b7e 770 if (longname_flag)
bf4b8020
CV
771 {
772 buf = get_long_name (tmp = buf, len);
773 free (tmp);
774 }
bf4b8020 775 tmp = buf;
8c3a79bb 776 if (strncmp (buf, "\\\\?\\", 4) == 0)
26fb7ef5 777 {
d2cc418e
CV
778 len = 0;
779 if (buf[5] == ':')
780 len = 4;
781 else if (!strncmp (buf + 4, "UNC\\", 4))
8c3a79bb 782 len = 6;
d2cc418e 783 if (len && strlen (buf) < MAX_PATH + len)
8c3a79bb 784 {
03ac74c1 785 tmp += len;
8c3a79bb 786 if (len == 6)
03ac74c1 787 *tmp = '\\';
80206d7f 788 print_tmp = true;
8c3a79bb 789 }
26fb7ef5 790 }
8c3a79bb 791 if (mixed_flag)
03ac74c1 792 convert_slashes (tmp);
5bb52de4 793 }
138d4f51
CF
794 }
795
80206d7f 796 puts (print_tmp ? tmp : buf);
bf4b8020
CV
797 if (buf2)
798 free (buf2);
799 if (buf)
03ac74c1 800 free (buf);
138d4f51
CF
801}
802
6a344609
CF
803static void
804print_version ()
805{
92b499ac
CV
806 printf ("cygpath (cygwin) %d.%d.%d\n"
807 "Path Conversion Utility\n"
6e623e93 808 "Copyright (C) 1998 - %s Cygwin Authors\n"
92b499ac
CV
809 "This is free software; see the source for copying conditions. There is NO\n"
810 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
811 CYGWIN_VERSION_DLL_MAJOR / 1000,
812 CYGWIN_VERSION_DLL_MAJOR % 1000,
813 CYGWIN_VERSION_DLL_MINOR,
814 strrchr (__DATE__, ' ') + 1);
6a344609
CF
815}
816
fc200025
CV
817static int
818do_options (int argc, char **argv, int from_file)
1fd5e000 819{
6a344609 820 int c, o = 0;
1fd5e000 821 path_flag = 0;
fc200025 822 unix_flag = 0;
1fd5e000 823 windows_flag = 0;
45b80bb4 824 shortname_flag = 0;
5bb52de4 825 longname_flag = 0;
2bd6505b 826 mixed_flag = 0;
45b80bb4 827 ignore_flag = 0;
3cdacffc
CV
828 allusers_flag = 0;
829 output_flag = 0;
f135dd3e 830 mode_flag = 0;
0a23799a 831 codepage = 0;
5aa8817e
CV
832 cygdrive_flag = 0;
833 absolute_flag = CCP_RELATIVE;
fc200025
CV
834 if (!from_file)
835 options_from_file_flag = 0;
836 optind = 0;
9b566b96 837 while ((c = getopt_long (argc, argv, options,
2bd6505b 838 long_options, (int *) NULL)) != EOF)
1fd5e000
CF
839 {
840 switch (c)
841 {
418068d4 842 case 'a':
5aa8817e 843 absolute_flag = CCP_ABSOLUTE;
418068d4
CF
844 break;
845
846 case 'c':
fc200025
CV
847 if (!optarg)
848 usage (stderr, 1);
418068d4
CF
849 CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
850 break;
851
1050e57c 852 case 'd':
1050e57c
CF
853 windows_flag = 1;
854 shortname_flag = 1;
855 break;
856
138d4f51 857 case 'f':
fc200025
CV
858 if (from_file || !optarg)
859 usage (stderr, 1);
138d4f51
CF
860 file_arg = optarg;
861 break;
862
f135dd3e
CF
863 case 'M':
864 mode_flag = 1;
865 break;
866
418068d4 867 case 'o':
fc200025
CV
868 if (from_file)
869 usage (stderr, 1);
418068d4
CF
870 options_from_file_flag = 1;
871 break;
872
1fd5e000
CF
873 case 'p':
874 path_flag = 1;
875 break;
876
877 case 'u':
1fd5e000
CF
878 unix_flag = 1;
879 break;
880
881 case 'w':
1fd5e000
CF
882 windows_flag = 1;
883 break;
884
1050e57c 885 case 'm':
1050e57c
CF
886 windows_flag = 1;
887 mixed_flag = 1;
888 break;
889
5bb52de4 890 case 'l':
5bb52de4
CV
891 longname_flag = 1;
892 break;
893
45b80bb4 894 case 's':
45b80bb4
CF
895 shortname_flag = 1;
896 break;
897
fc200025
CV
898 case 't':
899 if (!optarg)
2bd6505b
CF
900 usage (stderr, 1);
901
1050e57c
CF
902 format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
903 if (strcasecmp (format_type_arg, "dos") == 0)
cf157504 904 {
fc200025
CV
905 windows_flag = 1;
906 shortname_flag = 1;
cf157504 907 }
fc200025 908 else if (!strcasecmp (format_type_arg, "mixed"))
cf157504 909 {
fc200025
CV
910 windows_flag = 1;
911 mixed_flag = 1;
cf157504 912 }
fc200025 913 else if (!strcasecmp (format_type_arg, "unix"))
1050e57c 914 unix_flag = 1;
fc200025 915 else if (!strcasecmp (format_type_arg, "windows"))
cf157504 916 windows_flag = 1;
2bd6505b
CF
917 else
918 usage (stderr, 1);
919 break;
920
3cdacffc
CV
921 case 'A':
922 allusers_flag = 1;
923 break;
924
5aa8817e
CV
925 case 'U':
926 cygdrive_flag = CCP_PROC_CYGDRIVE;
927 break;
928
0a23799a
CV
929 case 'C':
930 if (!optarg)
931 usage (stderr, 1);
932 if (!strcasecmp (optarg, "ANSI"))
933 codepage = GetACP ();
934 else if (!strcasecmp (optarg, "OEM"))
935 codepage = GetOEMCP ();
936 else if (!strcasecmp (optarg, "UTF8")
937 || !strcasecmp (optarg, "UTF-8"))
938 codepage = CP_UTF8;
939 else
940 {
941 char *c;
942 codepage = (UINT) strtoul (optarg, &c, 10);
943 if (*c)
944 usage (stderr, 1);
945 }
946 break;
947
3cdacffc 948 case 'D':
e355de81 949 case 'H':
ca902991 950 case 'O':
3cdacffc 951 case 'P':
f00c1d2c 952 case 'S':
3cdacffc 953 case 'W':
fc200025 954 ++output_flag;
e355de81 955 o = c;
3cdacffc 956 break;
f00c1d2c 957
ca902991 958 case 'F':
fc200025 959 if (!optarg)
ca902991 960 usage (stderr, 1);
fc200025 961 ++output_flag;
ca902991
CV
962 output_arg = optarg;
963 o = c;
964 break;
965
45b80bb4
CF
966 case 'i':
967 ignore_flag = 1;
968 break;
969
1fd5e000
CF
970 case 'h':
971 usage (stdout, 0);
972 break;
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.437995 seconds and 5 git commands to generate.