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);
236 /* Special case for local disks: It's most feasible if the
237 DOS device name reflects the DOS drive, so we check for this
238 explicitly and only return prematurely if so. */
239 #define HARDDISK_PREFIX L"\\Device\\Harddisk"
240 if (ntdev.Length < wcslen (HARDDISK_PREFIX)
241 || wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0
242 || (odi->ObjectName.Length == 2 * sizeof (WCHAR)
243 && odi->ObjectName.Buffer[1] == L':'))
254 free (tgtdev.Buffer);
260 get_device_paths (char *path)
267 while ((ptr = strchr (ptr, ';')))
279 for (i = 0, ptr = path; ptr; i++)
282 ptr = strchr (ptr, ';');
285 paths[i] = get_device_name (next);
286 acc += strlen (paths[i]) + 1;
289 sbuf = (char *) malloc (acc + 1);
292 fprintf (stderr, "%s: out of memory\n", prog_name);
297 for (i = 0; i < n; i++)
299 strcat (strcat (sbuf, paths[i]), ";");
303 strchr (sbuf, '\0')[-1] = '\0';
308 get_short_paths (char *path)
314 char *end = strrchr (path, 0);
321 ptr = strchr (ptr, ';');
324 wide_path wpath (next);
325 len = GetShortPathNameW (wpath, NULL, 0);
328 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
334 sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
337 fprintf (stderr, "%s: out of memory\n", prog_name);
343 wide_path wpath (ptr);
344 len = GetShortPathNameW (wpath, sptr, acc);
347 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
352 ptr = strrchr (ptr, 0);
353 sptr = wcsrchr (sptr, 0);
360 len = my_wcstombs (NULL, sbuf, 0) + 1;
361 ptr = (char *) malloc (len);
364 fprintf (stderr, "%s: out of memory\n", prog_name);
367 my_wcstombs (ptr, sbuf, len);
372 get_short_name (const char *filename)
376 wide_path wpath (filename);
377 DWORD len = GetShortPathNameW (wpath, buf, 32768);
380 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
384 len = my_wcstombs (NULL, buf, 0) + 1;
385 sbuf = (char *) malloc (len);
388 fprintf (stderr, "%s: out of memory\n", prog_name);
391 my_wcstombs (sbuf, buf, len);
396 get_long_path_name_w32impl (LPCWSTR src, LPWSTR sbuf, DWORD)
398 wchar_t *buf1 = (wchar_t *) malloc (32768);
399 wchar_t *buf2 = (wchar_t *) malloc (32768);
401 const wchar_t *pelem, *next;
402 WIN32_FIND_DATAW w32_fd;
414 wcscat (ptr++, L"\\");
420 pelem = wcschr (next, L'\\');
421 len = pelem ? (pelem++ - next) : wcslen (next);
422 wcsncpy (ptr, next, len);
424 if (next[1] != L':' && wcscmp(next, L".") && wcscmp(next, L".."))
427 h = FindFirstFileW (buf2, &w32_fd);
428 if (h != INVALID_HANDLE_VALUE)
430 wcscpy (ptr, w32_fd.cFileName);
444 len = wcslen (buf2) + (sbuf ? 0 : 1);
451 get_long_name (const char *filename, DWORD& len)
455 static HINSTANCE k32 = LoadLibrary ("kernel32.dll");
456 static DWORD (WINAPI *GetLongPathName) (LPCWSTR, LPWSTR, DWORD) =
457 (DWORD (WINAPI *) (LPCWSTR, LPWSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameW");
458 if (!GetLongPathName)
459 GetLongPathName = get_long_path_name_w32impl;
461 wide_path wpath (filename);
462 len = GetLongPathName (wpath, buf, 32768);
465 DWORD err = GetLastError ();
467 if (err == ERROR_INVALID_PARAMETER)
469 fprintf (stderr, "%s: cannot create long name of %s\n", prog_name,
473 else if (err == ERROR_FILE_NOT_FOUND)
474 get_long_path_name_w32impl (wpath, buf, 32768);
478 wcsncat (buf, wpath, 32767);
481 len = my_wcstombs (NULL, buf, 0);
482 sbuf = (char *) malloc (len + 1);
485 fprintf (stderr, "%s: out of memory\n", prog_name);
488 my_wcstombs (sbuf, buf, len + 1);
493 get_long_paths (char *path)
500 while ((ptr = strchr (ptr, ';')))
512 for (i = 0, ptr = path; ptr; i++)
516 ptr = strchr (ptr, ';');
519 paths[i] = get_long_name (next, len);
523 sbuf = (char *) malloc (acc + 1);
526 fprintf (stderr, "%s: out of memory\n", prog_name);
531 for (i = 0; i < n; i++)
533 strcat (strcat (sbuf, paths[i]), ";");
537 strchr (sbuf, '\0')[-1] = '\0';
542 convert_slashes (char* name)
544 while ((name = strchr (name, '\\')) != NULL)
553 get_special_folder (char* path, int id)
555 WCHAR wpath[MAX_PATH];
559 LPITEMIDLIST pidl = 0;
560 if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
562 if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
564 my_wcstombs (path, wpath, PATH_MAX);
569 do_sysfolders (char option)
571 char *buf, buf1[PATH_MAX], buf2[PATH_MAX];
573 WCHAR wbuf[MAX_PATH];
574 DWORD len = MAX_PATH;
575 WIN32_FIND_DATAW w32_fd;
577 BOOL (*GetProfilesDirectoryAPtrW) (LPWSTR, LPDWORD) = 0;
584 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
585 : CSIDL_DESKTOPDIRECTORY);
589 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_PROGRAMS
594 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
600 int val = -1, len = -1;
601 if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
602 && len == (int) strlen (output_arg) && val >= 0))
604 fprintf (stderr, "%s: syntax error in special folder ID %s\n",
605 prog_name, output_arg);
608 get_special_folder (buf, val);
613 k32 = LoadLibrary ("userenv");
615 GetProfilesDirectoryAPtrW = (BOOL (*) (LPWSTR, LPDWORD))
616 GetProcAddress (k32, "GetProfilesDirectoryW");
617 if (GetProfilesDirectoryAPtrW)
618 (*GetProfilesDirectoryAPtrW) (wbuf, &len);
621 GetWindowsDirectoryW (wbuf, MAX_PATH);
622 wcscat (wbuf, L"\\Profiles");
624 my_wcstombs (buf, wbuf, PATH_MAX);
631 GetSystemDirectoryW (wbuf, MAX_PATH);
632 if ((fh = FindFirstFileW (wbuf, &w32_fd)) != INVALID_HANDLE_VALUE)
635 wcscpy (wcsrchr (wbuf, L'\\') + 1, w32_fd.cFileName);
637 my_wcstombs (buf, wbuf, PATH_MAX);
642 GetWindowsDirectoryW (wbuf, MAX_PATH);
643 my_wcstombs (buf, wbuf, PATH_MAX);
652 fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name);
654 else if (!windows_flag)
656 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, buf, buf2,
658 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
659 prog_name, buf, strerror (errno));
666 tmp = buf = get_short_name (buf);
668 convert_slashes (buf);
670 printf ("%s\n", buf);
676 report_mode (char *filename)
678 switch (cygwin_internal (CW_GET_BINMODE, filename))
681 printf ("%s: binary\n", filename);
684 printf ("%s: text\n", filename);
687 fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename,
694 do_pathconv (char *filename)
696 char *buf = NULL, *tmp;
697 wchar_t *buf2 = NULL;
700 bool print_tmp = false;
701 cygwin_conv_path_t conv_func =
702 (unix_flag ? CCP_WIN_A_TO_POSIX
703 : (path_flag ? CCP_POSIX_TO_WIN_A
704 : CCP_POSIX_TO_WIN_W))
705 | (absolute_flag ? CCP_ABSOLUTE : CCP_RELATIVE);
709 len = strlen (filename);
712 else if (ignore_flag)
716 fprintf (stderr, "%s: can't convert empty path\n", prog_name);
721 len = cygwin_conv_path_list (conv_func, filename, NULL, 0);
723 buf = (char *) malloc (len);
724 if (!unix_flag && !path_flag)
725 buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
728 fprintf (stderr, "%s: out of memory\n", prog_name);
734 err = cygwin_conv_path_list (conv_func, filename, buf, len);
739 buf = get_device_paths (tmp = buf);
743 buf = get_short_paths (tmp = buf);
748 buf = get_long_paths (tmp = buf);
752 convert_slashes (buf);
756 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
757 prog_name, filename, strerror (errno));
763 err = cygwin_conv_path (conv_func, filename,
764 unix_flag ? (void *) buf : (void *) buf2, len);
767 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
768 prog_name, filename, strerror (errno));
773 my_wcstombs (buf, buf2, 32768);
774 buf = get_device_name (tmp = buf);
778 buf = get_short_name (tmp = buf);
783 buf = get_long_name (tmp = buf, len);
787 if (strncmp (buf, "\\\\?\\", 4) == 0)
790 if (strncmp (buf + 4, "UNC\\", 4) == 0)
792 if (strlen (buf) < MAX_PATH + len)
801 convert_slashes (tmp);
805 puts (print_tmp ? tmp : buf);
815 const char *v = strchr (version, ':');
825 len = strchr (v, ' ') - v;
828 cygpath (cygwin) %.*s\n\
829 Path Conversion Utility\n\
830 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\
831 2007, 2008 Red Hat, Inc.\n\
833 ", len, v, __DATE__);
837 do_options (int argc, char **argv, int from_file)
852 options_from_file_flag = 0;
854 while ((c = getopt_long (argc, argv, options,
855 long_options, (int *) NULL)) != EOF)
866 CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
875 if (from_file || !optarg)
887 options_from_file_flag = 1;
919 format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
920 if (strcasecmp (format_type_arg, "dos") == 0)
925 else if (!strcasecmp (format_type_arg, "mixed"))
930 else if (!strcasecmp (format_type_arg, "unix"))
932 else if (!strcasecmp (format_type_arg, "windows"))
945 if (!strcasecmp (optarg, "ANSI"))
946 codepage = GetACP ();
947 else if (!strcasecmp (optarg, "OEM"))
948 codepage = GetOEMCP ();
949 else if (!strcasecmp (optarg, "UTF8")
950 || !strcasecmp (optarg, "UTF-8"))
955 codepage = (UINT) strtoul (optarg, &c, 10);
997 /* If none of the "important" flags are set, -u is default. */
998 if (!unix_flag && !windows_flag && !mode_flag
999 && (!from_file ? !options_from_file_flag : 1))
1002 /* Only one of ... */
1003 if (unix_flag + windows_flag + mode_flag > 1
1004 + (!from_file ? options_from_file_flag : 0))
1007 /* options_from_file_flag requires a file. */
1008 if (!from_file && options_from_file_flag && !file_arg)
1011 /* longname and shortname don't play well together. */
1012 if (longname_flag && shortname_flag)
1015 /* longname and shortname only make sense with Windows paths. */
1016 if ((longname_flag || shortname_flag) && !windows_flag)
1023 action (int argc, char **argv, int opt)
1030 do_sysfolders (opt);
1034 if (optind > argc - 1)
1037 for (int i = optind; argv[i]; i++)
1039 report_mode (argv[i]);
1041 do_pathconv (argv[i]);
1046 main (int argc, char **argv)
1050 setlocale (LC_CTYPE, "");
1051 prog_name = strrchr (argv[0], '/');
1053 prog_name = strrchr (argv[0], '\\');
1055 prog_name = argv[0];
1059 o = do_options (argc, argv, 0);
1062 action (argc, argv, o);
1066 char buf[PATH_MAX * 2 + 1];
1071 if (strcmp (file_arg, "-"))
1073 if (!(fp = fopen (file_arg, "rt")))
1082 setmode (0, O_TEXT);
1084 setbuf (stdout, NULL);
1086 while (fgets (buf, sizeof (buf), fp))
1089 char *av[4] = { NULL, NULL, NULL, NULL };
1090 char *p = strchr (buf, '\n');
1094 av[ac++] = prog_name;
1096 if (options_from_file_flag && *p == '-')
1098 while (*p && !isspace (*p))
1103 while (*p && isspace (*p))
1107 o = do_options (ac, av, 1);