1 /* cygpath.cc -- convert pathnames between Windows and Unix format
2 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 #define NOCOMATTRIBUTE
23 #include <sys/fcntl.h>
24 #include <sys/cygwin.h>
27 #include <ddk/ntddk.h>
28 #include <ddk/winddk.h>
29 #include <ddk/ntifs.h>
30 #include "wide_path.h"
32 static const char version[] = "$Revision$";
34 static char *prog_name;
35 static char *file_arg, *output_arg;
36 static int path_flag, unix_flag, windows_flag, absolute_flag;
37 static int shortname_flag, longname_flag;
38 static int ignore_flag, allusers_flag, output_flag;
39 static int mixed_flag, options_from_file_flag, mode_flag;
42 static const char *format_type_arg;
44 static struct option long_options[] = {
45 {(char *) "absolute", no_argument, NULL, 'a'},
46 {(char *) "close", required_argument, NULL, 'c'},
47 {(char *) "dos", no_argument, NULL, 'd'},
48 {(char *) "file", required_argument, NULL, 'f'},
49 {(char *) "help", no_argument, NULL, 'h'},
50 {(char *) "ignore", no_argument, NULL, 'i'},
51 {(char *) "long-name", no_argument, NULL, 'l'},
52 {(char *) "mixed", no_argument, NULL, 'm'},
53 {(char *) "mode", no_argument, NULL, 'M'},
54 {(char *) "option", no_argument, NULL, 'o'},
55 {(char *) "path", no_argument, NULL, 'p'},
56 {(char *) "short-name", no_argument, NULL, 's'},
57 {(char *) "type", required_argument, NULL, 't'},
58 {(char *) "unix", no_argument, NULL, 'u'},
59 {(char *) "version", no_argument, NULL, 'v'},
60 {(char *) "windows", no_argument, NULL, 'w'},
61 {(char *) "allusers", no_argument, NULL, 'A'},
62 {(char *) "desktop", no_argument, NULL, 'D'},
63 {(char *) "homeroot", no_argument, NULL, 'H'},
64 {(char *) "mydocs", no_argument, NULL, 'O'},
65 {(char *) "smprograms", no_argument, NULL, 'P'},
66 {(char *) "sysdir", no_argument, NULL, 'S'},
67 {(char *) "windir", no_argument, NULL, 'W'},
68 {(char *) "folder", required_argument, NULL, 'F'},
69 {(char *) "codepage", required_argument, NULL, 'C'},
70 {0, no_argument, 0, 0}
73 static char options[] = "ac:df:hilmMopst:uvwAC:DHOPSWF:";
76 usage (FILE * stream, int status)
78 if (!ignore_flag || !status)
80 Usage: %s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\
84 Convert Unix and Windows format paths, or output system path information\n\
86 Output type options:\n\
87 -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\
88 -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\
89 -M, --mode report on mode of file (binmode or textmode)\n\
90 -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\
91 -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\
92 -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\
93 Path conversion options:\n\
94 -a, --absolute output absolute path\n\
95 -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\
96 -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\
97 -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\
98 -C, --codepage CP print DOS, Windows, or mixed pathname in Windows\n\
99 codepage CP. CP can be a numeric codepage identifier,\n\
100 or one of the reserved words ANSI, OEM, or UTF8.\n\
101 If this option is missing, %s defaults to the\n\
102 character set defined by the current locale.\n\
103 System information:\n\
104 -A, --allusers use `All Users' instead of current user for -D, -O, -P\n\
105 -D, --desktop output `Desktop' directory and exit\n\
106 -H, --homeroot output `Profiles' directory (home root) and exit\n\
107 -O, --mydocs output `My Documents' directory and exit\n\
108 -P, --smprograms output Start Menu `Programs' directory and exit\n\
109 -S, --sysdir output system directory and exit\n\
110 -W, --windir output `Windows' directory and exit\n\
111 -F, --folder ID output special folder with numeric ID and exit\n\
112 ", prog_name, prog_name, prog_name, prog_name, prog_name);
115 else if (stream != stdout)
116 fprintf(stream, "Try `%s --help' for more information.\n", prog_name);
121 -f, --file FILE read FILE for input; use - to read from STDIN\n\
122 -o, --option read options from FILE as well (for use with --file)\n\
123 -c, --close HANDLE close HANDLE (for use in captured process)\n\
124 -i, --ignore ignore missing argument\n\
125 -h, --help output usage information and exit\n\
126 -v, --version output version information and exit\n\
129 exit (ignore_flag ? 0 : status);
132 static inline BOOLEAN
133 RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
136 uni->MaximumLength = 512;
137 uni->Buffer = (WCHAR *) malloc (size);
138 return uni->Buffer != NULL;
142 my_wcstombs (char *dest, const wchar_t *src, size_t n)
145 return WideCharToMultiByte (codepage, 0, src, -1, dest, n, NULL, NULL);
147 return wcstombs (dest, src, n);
151 get_device_name (char *path)
153 UNICODE_STRING ntdev, tgtdev, ntdevdir;
155 OBJECT_ATTRIBUTES ntobj;
158 char *ret = strdup (path);
159 PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
164 if (strncasecmp (path, "\\Device\\", 8))
167 if (!RtlAllocateUnicodeString (&ntdev, 65536))
169 if (!RtlAllocateUnicodeString (&tgtdev, 65536))
171 RtlInitAnsiString (&ans, path);
172 RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
174 /* First check if the given device name is a symbolic link itself. If so,
175 query it and use the new name as actual device name to search for in the
176 DOS device name directory. If not, just use the incoming device name. */
177 InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL);
178 status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
179 if (NT_SUCCESS (status))
181 status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
183 if (!NT_SUCCESS (status))
185 RtlCopyUnicodeString (&ntdev, &tgtdev);
187 else if (status != STATUS_OBJECT_TYPE_MISMATCH)
190 for (int i = 0; i < 2; ++i)
192 /* There are two DOS device directories, the local and the global dir.
193 Try both, local first. */
194 RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??");
196 /* Open the directory... */
197 InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE,
199 status = ZwOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj);
200 if (!NT_SUCCESS (status))
203 /* ...and scan it. */
204 for (restart = TRUE, cont = 0;
205 NT_SUCCESS (ZwQueryDirectoryObject (dir, odi, 4096, TRUE,
206 restart, &cont, NULL));
209 /* For each entry check if it's a symbolic link. */
210 InitializeObjectAttributes (&ntobj, &odi->ObjectName,
211 OBJ_CASE_INSENSITIVE, dir, NULL);
212 status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
213 if (!NT_SUCCESS (status))
216 tgtdev.MaximumLength = 512;
217 /* If so, query it and compare the target of the symlink with the
218 incoming device name. */
219 status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
221 if (!NT_SUCCESS (status))
223 if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE))
225 /* If the comparison succeeds, the name of the directory entry is
226 a valid DOS device name, if prepended with "\\.\". Return that
228 ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
230 ret = (char *) malloc (len + 4);
231 strcpy (ret, "\\\\.\\");
233 ans.MaximumLength = len;
234 ans.Buffer = ret + 4;
235 RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE);
244 free (tgtdev.Buffer);
250 get_device_paths (char *path)
257 while ((ptr = strchr (ptr, ';')))
269 for (i = 0, ptr = path; ptr; i++)
272 ptr = strchr (ptr, ';');
275 paths[i] = get_device_name (next);
276 acc += strlen (paths[i]) + 1;
279 sbuf = (char *) malloc (acc + 1);
282 fprintf (stderr, "%s: out of memory\n", prog_name);
287 for (i = 0; i < n; i++)
289 strcat (strcat (sbuf, paths[i]), ";");
293 strchr (sbuf, '\0')[-1] = '\0';
298 get_short_paths (char *path)
304 char *end = strrchr (path, 0);
311 ptr = strchr (ptr, ';');
314 wide_path wpath (next);
315 len = GetShortPathNameW (wpath, NULL, 0);
318 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
324 sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
327 fprintf (stderr, "%s: out of memory\n", prog_name);
333 wide_path wpath (ptr);
334 len = GetShortPathNameW (wpath, sptr, acc);
337 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
342 ptr = strrchr (ptr, 0);
343 sptr = wcsrchr (sptr, 0);
350 len = my_wcstombs (NULL, sbuf, 0) + 1;
351 ptr = (char *) malloc (len);
354 fprintf (stderr, "%s: out of memory\n", prog_name);
357 my_wcstombs (ptr, sbuf, len);
362 get_short_name (const char *filename)
366 wide_path wpath (filename);
367 DWORD len = GetShortPathNameW (wpath, buf, 32768);
370 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
374 len = my_wcstombs (NULL, buf, 0) + 1;
375 sbuf = (char *) malloc (len);
378 fprintf (stderr, "%s: out of memory\n", prog_name);
381 my_wcstombs (sbuf, buf, len);
386 get_long_path_name_w32impl (LPCWSTR src, LPWSTR sbuf, DWORD)
388 wchar_t *buf1 = (wchar_t *) malloc (32768);
389 wchar_t *buf2 = (wchar_t *) malloc (32768);
391 const wchar_t *pelem, *next;
392 WIN32_FIND_DATAW w32_fd;
404 wcscat (ptr++, L"\\");
410 pelem = wcschr (next, L'\\');
411 len = pelem ? (pelem++ - next) : wcslen (next);
412 wcsncpy (ptr, next, len);
414 if (next[1] != L':' && wcscmp(next, L".") && wcscmp(next, L".."))
417 h = FindFirstFileW (buf2, &w32_fd);
418 if (h != INVALID_HANDLE_VALUE)
420 wcscpy (ptr, w32_fd.cFileName);
434 len = wcslen (buf2) + (sbuf ? 0 : 1);
441 get_long_name (const char *filename, DWORD& len)
445 static HINSTANCE k32 = LoadLibrary ("kernel32.dll");
446 static DWORD (WINAPI *GetLongPathName) (LPCWSTR, LPWSTR, DWORD) =
447 (DWORD (WINAPI *) (LPCWSTR, LPWSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameW");
448 if (!GetLongPathName)
449 GetLongPathName = get_long_path_name_w32impl;
451 wide_path wpath (filename);
452 len = GetLongPathName (wpath, buf, 32768);
455 DWORD err = GetLastError ();
457 if (err == ERROR_INVALID_PARAMETER)
459 fprintf (stderr, "%s: cannot create long name of %s\n", prog_name,
463 else if (err == ERROR_FILE_NOT_FOUND)
464 get_long_path_name_w32impl (wpath, buf, 32768);
468 wcsncat (buf, wpath, 32767);
471 len = my_wcstombs (NULL, buf, 0);
472 sbuf = (char *) malloc (len + 1);
475 fprintf (stderr, "%s: out of memory\n", prog_name);
478 my_wcstombs (sbuf, buf, len + 1);
483 get_long_paths (char *path)
490 while ((ptr = strchr (ptr, ';')))
502 for (i = 0, ptr = path; ptr; i++)
506 ptr = strchr (ptr, ';');
509 paths[i] = get_long_name (next, len);
513 sbuf = (char *) malloc (acc + 1);
516 fprintf (stderr, "%s: out of memory\n", prog_name);
521 for (i = 0; i < n; i++)
523 strcat (strcat (sbuf, paths[i]), ";");
527 strchr (sbuf, '\0')[-1] = '\0';
532 convert_slashes (char* name)
534 while ((name = strchr (name, '\\')) != NULL)
543 get_special_folder (char* path, int id)
545 WCHAR wpath[MAX_PATH];
549 LPITEMIDLIST pidl = 0;
550 if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
552 if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
554 my_wcstombs (path, wpath, PATH_MAX);
559 do_sysfolders (char option)
561 char *buf, buf1[PATH_MAX], buf2[PATH_MAX];
563 WCHAR wbuf[MAX_PATH];
564 DWORD len = MAX_PATH;
565 WIN32_FIND_DATAW w32_fd;
567 BOOL (*GetProfilesDirectoryAPtrW) (LPWSTR, LPDWORD) = 0;
574 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
575 : CSIDL_DESKTOPDIRECTORY);
579 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_PROGRAMS
584 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
590 int val = -1, len = -1;
591 if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
592 && len == (int) strlen (output_arg) && val >= 0))
594 fprintf (stderr, "%s: syntax error in special folder ID %s\n",
595 prog_name, output_arg);
598 get_special_folder (buf, val);
603 k32 = LoadLibrary ("userenv");
605 GetProfilesDirectoryAPtrW = (BOOL (*) (LPWSTR, LPDWORD))
606 GetProcAddress (k32, "GetProfilesDirectoryW");
607 if (GetProfilesDirectoryAPtrW)
608 (*GetProfilesDirectoryAPtrW) (wbuf, &len);
611 GetWindowsDirectoryW (wbuf, MAX_PATH);
612 wcscat (wbuf, L"\\Profiles");
614 my_wcstombs (buf, wbuf, PATH_MAX);
621 GetSystemDirectoryW (wbuf, MAX_PATH);
622 if ((fh = FindFirstFileW (wbuf, &w32_fd)) != INVALID_HANDLE_VALUE)
625 wcscpy (wcsrchr (wbuf, L'\\') + 1, w32_fd.cFileName);
627 my_wcstombs (buf, wbuf, PATH_MAX);
632 GetWindowsDirectoryW (wbuf, MAX_PATH);
633 my_wcstombs (buf, wbuf, PATH_MAX);
642 fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name);
644 else if (!windows_flag)
646 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, buf, buf2,
648 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
649 prog_name, buf, strerror (errno));
656 tmp = buf = get_short_name (buf);
658 convert_slashes (buf);
660 printf ("%s\n", buf);
666 report_mode (char *filename)
668 switch (cygwin_internal (CW_GET_BINMODE, filename))
671 printf ("%s: binary\n", filename);
674 printf ("%s: text\n", filename);
677 fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename,
684 do_pathconv (char *filename)
686 char *buf = NULL, *tmp;
687 wchar_t *buf2 = NULL;
690 bool print_tmp = false;
691 cygwin_conv_path_t conv_func =
692 (unix_flag ? CCP_WIN_A_TO_POSIX
693 : (path_flag ? CCP_POSIX_TO_WIN_A
694 : CCP_POSIX_TO_WIN_W))
695 | (absolute_flag ? CCP_ABSOLUTE : CCP_RELATIVE);
699 len = strlen (filename);
702 else if (ignore_flag)
706 fprintf (stderr, "%s: can't convert empty path\n", prog_name);
711 len = cygwin_conv_path_list (conv_func, filename, NULL, 0);
713 buf = (char *) malloc (len);
714 if (!unix_flag && !path_flag)
715 buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
718 fprintf (stderr, "%s: out of memory\n", prog_name);
724 err = cygwin_conv_path_list (conv_func, filename, buf, len);
729 buf = get_device_paths (tmp = buf);
733 buf = get_short_paths (tmp = buf);
738 buf = get_long_paths (tmp = buf);
742 convert_slashes (buf);
746 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
747 prog_name, filename, strerror (errno));
753 err = cygwin_conv_path (conv_func, filename,
754 unix_flag ? (void *) buf : (void *) buf2, len);
757 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
758 prog_name, filename, strerror (errno));
763 my_wcstombs (buf, buf2, 32768);
764 buf = get_device_name (tmp = buf);
768 buf = get_short_name (tmp = buf);
773 buf = get_long_name (tmp = buf, len);
777 if (strncmp (buf, "\\\\?\\", 4) == 0)
780 if (strncmp (buf + 4, "UNC\\", 4) == 0)
782 if (strlen (buf) < MAX_PATH + len)
791 convert_slashes (tmp);
795 puts (print_tmp ? tmp : buf);
805 const char *v = strchr (version, ':');
815 len = strchr (v, ' ') - v;
818 cygpath (cygwin) %.*s\n\
819 Path Conversion Utility\n\
820 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\
821 2007, 2008 Red Hat, Inc.\n\
823 ", len, v, __DATE__);
827 do_options (int argc, char **argv, int from_file)
842 options_from_file_flag = 0;
844 while ((c = getopt_long (argc, argv, options,
845 long_options, (int *) NULL)) != EOF)
856 CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
865 if (from_file || !optarg)
877 options_from_file_flag = 1;
909 format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
910 if (strcasecmp (format_type_arg, "dos") == 0)
915 else if (!strcasecmp (format_type_arg, "mixed"))
920 else if (!strcasecmp (format_type_arg, "unix"))
922 else if (!strcasecmp (format_type_arg, "windows"))
935 if (!strcasecmp (optarg, "ANSI"))
936 codepage = GetACP ();
937 else if (!strcasecmp (optarg, "OEM"))
938 codepage = GetOEMCP ();
939 else if (!strcasecmp (optarg, "UTF8")
940 || !strcasecmp (optarg, "UTF-8"))
945 codepage = (UINT) strtoul (optarg, &c, 10);
987 /* If none of the "important" flags are set, -u is default. */
988 if (!unix_flag && !windows_flag && !mode_flag
989 && (!from_file ? !options_from_file_flag : 1))
992 /* Only one of ... */
993 if (unix_flag + windows_flag + mode_flag > 1
994 + (!from_file ? options_from_file_flag : 0))
997 /* options_from_file_flag requires a file. */
998 if (!from_file && options_from_file_flag && !file_arg)
1001 /* longname and shortname don't play well together. */
1002 if (longname_flag && shortname_flag)
1005 /* longname and shortname only make sense with Windows paths. */
1006 if ((longname_flag || shortname_flag) && !windows_flag)
1013 action (int argc, char **argv, int opt)
1020 do_sysfolders (opt);
1024 if (optind > argc - 1)
1027 for (int i = optind; argv[i]; i++)
1029 report_mode (argv[i]);
1031 do_pathconv (argv[i]);
1036 main (int argc, char **argv)
1040 setlocale (LC_CTYPE, "");
1041 prog_name = strrchr (argv[0], '/');
1043 prog_name = strrchr (argv[0], '\\');
1045 prog_name = argv[0];
1049 o = do_options (argc, argv, 0);
1052 action (argc, argv, o);
1056 char buf[PATH_MAX * 2 + 1];
1061 if (strcmp (file_arg, "-"))
1063 if (!(fp = fopen (file_arg, "rt")))
1072 setmode (0, O_TEXT);
1074 setbuf (stdout, NULL);
1076 while (fgets (buf, sizeof (buf), fp))
1079 char *av[4] = { NULL, NULL, NULL, NULL };
1080 char *p = strchr (buf, '\n');
1084 av[ac++] = prog_name;
1086 if (options_from_file_flag && *p == '-')
1088 while (*p && !isspace (*p))
1093 while (*p && isspace (*p))
1097 o = do_options (ac, av, 1);