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