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"
33 static const char version[] = "$Revision$";
35 static char *prog_name;
36 static char *file_arg, *output_arg;
37 static int path_flag, unix_flag, windows_flag, absolute_flag;
38 static int shortname_flag, longname_flag;
39 static int ignore_flag, allusers_flag, output_flag;
40 static int mixed_flag, options_from_file_flag, mode_flag;
43 static const char *format_type_arg;
45 static struct option long_options[] = {
46 {(char *) "absolute", no_argument, NULL, 'a'},
47 {(char *) "close", required_argument, NULL, 'c'},
48 {(char *) "dos", no_argument, NULL, 'd'},
49 {(char *) "file", required_argument, NULL, 'f'},
50 {(char *) "help", no_argument, NULL, 'h'},
51 {(char *) "ignore", no_argument, NULL, 'i'},
52 {(char *) "long-name", no_argument, NULL, 'l'},
53 {(char *) "mixed", no_argument, NULL, 'm'},
54 {(char *) "mode", no_argument, NULL, 'M'},
55 {(char *) "option", no_argument, NULL, 'o'},
56 {(char *) "path", no_argument, NULL, 'p'},
57 {(char *) "short-name", no_argument, NULL, 's'},
58 {(char *) "type", required_argument, NULL, 't'},
59 {(char *) "unix", no_argument, NULL, 'u'},
60 {(char *) "version", no_argument, NULL, 'v'},
61 {(char *) "windows", no_argument, NULL, 'w'},
62 {(char *) "allusers", no_argument, NULL, 'A'},
63 {(char *) "desktop", no_argument, NULL, 'D'},
64 {(char *) "homeroot", no_argument, NULL, 'H'},
65 {(char *) "mydocs", no_argument, NULL, 'O'},
66 {(char *) "smprograms", no_argument, NULL, 'P'},
67 {(char *) "sysdir", no_argument, NULL, 'S'},
68 {(char *) "windir", no_argument, NULL, 'W'},
69 {(char *) "folder", required_argument, NULL, 'F'},
70 {(char *) "codepage", required_argument, NULL, 'C'},
71 {0, no_argument, 0, 0}
74 static char options[] = "ac:df:hilmMopst:uvwAC:DHOPSWF:";
77 usage (FILE * stream, int status)
79 if (!ignore_flag || !status)
81 Usage: %s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\
85 Convert Unix and Windows format paths, or output system path information\n\
87 Output type options:\n\
88 -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\
89 -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\
90 -M, --mode report on mode of file (binmode or textmode)\n\
91 -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\
92 -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\
93 -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\
94 Path conversion options:\n\
95 -a, --absolute output absolute path\n\
96 -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\
97 -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\
98 -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\
99 -C, --codepage CP print DOS, Windows, or mixed pathname in Windows\n\
100 codepage CP. CP can be a numeric codepage identifier,\n\
101 or one of the reserved words ANSI, OEM, or UTF8.\n\
102 If this option is missing, %s defaults to the\n\
103 character set defined by the current locale.\n\
104 System information:\n\
105 -A, --allusers use `All Users' instead of current user for -D, -O, -P\n\
106 -D, --desktop output `Desktop' directory and exit\n\
107 -H, --homeroot output `Profiles' directory (home root) and exit\n\
108 -O, --mydocs output `My Documents' directory and exit\n\
109 -P, --smprograms output Start Menu `Programs' directory and exit\n\
110 -S, --sysdir output system directory and exit\n\
111 -W, --windir output `Windows' directory and exit\n\
112 -F, --folder ID output special folder with numeric ID and exit\n\
113 ", prog_name, prog_name, prog_name, prog_name, prog_name);
116 else if (stream != stdout)
117 fprintf(stream, "Try `%s --help' for more information.\n", prog_name);
122 -f, --file FILE read FILE for input; use - to read from STDIN\n\
123 -o, --option read options from FILE as well (for use with --file)\n\
124 -c, --close HANDLE close HANDLE (for use in captured process)\n\
125 -i, --ignore ignore missing argument\n\
126 -h, --help output usage information and exit\n\
127 -v, --version output version information and exit\n\
130 exit (ignore_flag ? 0 : status);
133 static inline BOOLEAN
134 RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
137 uni->MaximumLength = 512;
138 uni->Buffer = (WCHAR *) malloc (size);
139 return uni->Buffer != NULL;
143 my_wcstombs (char *dest, const wchar_t *src, size_t n)
146 return WideCharToMultiByte (codepage, 0, src, -1, dest, n, NULL, NULL);
148 return wcstombs (dest, src, n);
152 get_device_name (char *path)
154 UNICODE_STRING ntdev, tgtdev, ntdevdir;
156 OBJECT_ATTRIBUTES ntobj;
159 char *ret = strdup (path);
160 PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
165 if (strncasecmp (path, "\\Device\\", 8))
168 if (!RtlAllocateUnicodeString (&ntdev, 65536))
170 if (!RtlAllocateUnicodeString (&tgtdev, 65536))
172 RtlInitAnsiString (&ans, path);
173 RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
175 /* First check if the given device name is a symbolic link itself. If so,
176 query it and use the new name as actual device name to search for in the
177 DOS device name directory. If not, just use the incoming device name. */
178 InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL);
179 status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
180 if (NT_SUCCESS (status))
182 status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
184 if (!NT_SUCCESS (status))
186 RtlCopyUnicodeString (&ntdev, &tgtdev);
188 else if (status != STATUS_OBJECT_TYPE_MISMATCH)
191 for (int i = 0; i < 2; ++i)
193 /* There are two DOS device directories, the local and the global dir.
194 Try both, local first. */
195 RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??");
197 /* Open the directory... */
198 InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE,
200 status = ZwOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj);
201 if (!NT_SUCCESS (status))
204 /* ...and scan it. */
205 for (restart = TRUE, cont = 0;
206 NT_SUCCESS (ZwQueryDirectoryObject (dir, odi, 4096, TRUE,
207 restart, &cont, NULL));
210 /* For each entry check if it's a symbolic link. */
211 InitializeObjectAttributes (&ntobj, &odi->ObjectName,
212 OBJ_CASE_INSENSITIVE, dir, NULL);
213 status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj);
214 if (!NT_SUCCESS (status))
217 tgtdev.MaximumLength = 512;
218 /* If so, query it and compare the target of the symlink with the
219 incoming device name. */
220 status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
222 if (!NT_SUCCESS (status))
224 if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE))
226 /* If the comparison succeeds, the name of the directory entry is
227 a valid DOS device name, if prepended with "\\.\". Return that
229 ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
231 ret = (char *) malloc (len + 4);
232 strcpy (ret, "\\\\.\\");
234 ans.MaximumLength = len;
235 ans.Buffer = ret + 4;
236 RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE);
237 /* Special case for local disks: It's most feasible if the
238 DOS device name reflects the DOS drive, so we check for this
239 explicitly and only return prematurely if so. */
240 #define HARDDISK_PREFIX L"\\Device\\Harddisk"
241 if (ntdev.Length < wcslen (HARDDISK_PREFIX)
242 || wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0
243 || (odi->ObjectName.Length == 2 * sizeof (WCHAR)
244 && odi->ObjectName.Buffer[1] == L':'))
255 free (tgtdev.Buffer);
261 get_device_paths (char *path)
268 while ((ptr = strchr (ptr, ';')))
280 for (i = 0, ptr = path; ptr; i++)
283 ptr = strchr (ptr, ';');
286 paths[i] = get_device_name (next);
287 acc += strlen (paths[i]) + 1;
290 sbuf = (char *) malloc (acc + 1);
293 fprintf (stderr, "%s: out of memory\n", prog_name);
298 for (i = 0; i < n; i++)
300 strcat (strcat (sbuf, paths[i]), ";");
304 strchr (sbuf, '\0')[-1] = '\0';
309 get_short_paths (char *path)
315 char *end = strrchr (path, 0);
322 ptr = strchr (ptr, ';');
325 wide_path wpath (next);
326 len = GetShortPathNameW (wpath, NULL, 0);
329 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
335 sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
338 fprintf (stderr, "%s: out of memory\n", prog_name);
344 wide_path wpath (ptr);
345 len = GetShortPathNameW (wpath, sptr, acc);
348 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
353 ptr = strrchr (ptr, 0);
354 sptr = wcsrchr (sptr, 0);
361 len = my_wcstombs (NULL, sbuf, 0) + 1;
362 ptr = (char *) malloc (len);
365 fprintf (stderr, "%s: out of memory\n", prog_name);
368 my_wcstombs (ptr, sbuf, len);
373 get_short_name (const char *filename)
377 wide_path wpath (filename);
378 DWORD len = GetShortPathNameW (wpath, buf, 32768);
381 fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
385 len = my_wcstombs (NULL, buf, 0) + 1;
386 sbuf = (char *) malloc (len);
389 fprintf (stderr, "%s: out of memory\n", prog_name);
392 my_wcstombs (sbuf, buf, len);
397 get_long_path_name_w32impl (LPCWSTR src, LPWSTR sbuf, DWORD)
399 wchar_t *buf1 = (wchar_t *) malloc (32768);
400 wchar_t *buf2 = (wchar_t *) malloc (32768);
402 const wchar_t *pelem, *next;
403 WIN32_FIND_DATAW w32_fd;
415 wcscat (ptr++, L"\\");
421 pelem = wcschr (next, L'\\');
422 len = pelem ? (pelem++ - next) : wcslen (next);
423 wcsncpy (ptr, next, len);
425 if (next[1] != L':' && wcscmp(next, L".") && wcscmp(next, L".."))
428 h = FindFirstFileW (buf2, &w32_fd);
429 if (h != INVALID_HANDLE_VALUE)
431 wcscpy (ptr, w32_fd.cFileName);
445 len = wcslen (buf2) + (sbuf ? 0 : 1);
452 get_long_name (const char *filename, DWORD& len)
456 static HINSTANCE k32 = GetModuleHandleW (L"kernel32.dll");
457 static DWORD (WINAPI *GetLongPathName) (LPCWSTR, LPWSTR, DWORD) =
458 (DWORD (WINAPI *) (LPCWSTR, LPWSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameW");
459 if (!GetLongPathName)
460 GetLongPathName = get_long_path_name_w32impl;
462 wide_path wpath (filename);
463 len = GetLongPathName (wpath, buf, 32768);
466 DWORD err = GetLastError ();
468 if (err == ERROR_INVALID_PARAMETER)
470 fprintf (stderr, "%s: cannot create long name of %s\n", prog_name,
474 else if (err == ERROR_FILE_NOT_FOUND)
475 get_long_path_name_w32impl (wpath, buf, 32768);
479 wcsncat (buf, wpath, 32767);
482 len = my_wcstombs (NULL, buf, 0);
483 sbuf = (char *) malloc (len + 1);
486 fprintf (stderr, "%s: out of memory\n", prog_name);
489 my_wcstombs (sbuf, buf, len + 1);
494 get_long_paths (char *path)
501 while ((ptr = strchr (ptr, ';')))
513 for (i = 0, ptr = path; ptr; i++)
517 ptr = strchr (ptr, ';');
520 paths[i] = get_long_name (next, len);
524 sbuf = (char *) malloc (acc + 1);
527 fprintf (stderr, "%s: out of memory\n", prog_name);
532 for (i = 0; i < n; i++)
534 strcat (strcat (sbuf, paths[i]), ";");
538 strchr (sbuf, '\0')[-1] = '\0';
543 convert_slashes (char* name)
545 while ((name = strchr (name, '\\')) != NULL)
554 get_special_folder (char* path, int id)
556 WCHAR wpath[MAX_PATH];
560 LPITEMIDLIST pidl = 0;
561 if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
563 if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
565 my_wcstombs (path, wpath, PATH_MAX);
570 do_sysfolders (char option)
572 char *buf, buf1[PATH_MAX], buf2[PATH_MAX];
574 WCHAR wbuf[MAX_PATH];
575 DWORD len = MAX_PATH;
576 WIN32_FIND_DATAW w32_fd;
578 BOOL (*GetProfilesDirectoryAPtrW) (LPWSTR, LPDWORD) = 0;
585 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
586 : CSIDL_DESKTOPDIRECTORY);
590 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_PROGRAMS
595 get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
601 int val = -1, len = -1;
602 if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
603 && len == (int) strlen (output_arg) && val >= 0))
605 fprintf (stderr, "%s: syntax error in special folder ID %s\n",
606 prog_name, output_arg);
609 get_special_folder (buf, val);
614 k32 = LoadLibrary ("userenv.dll");
616 GetProfilesDirectoryAPtrW = (BOOL (*) (LPWSTR, LPDWORD))
617 GetProcAddress (k32, "GetProfilesDirectoryW");
618 if (GetProfilesDirectoryAPtrW)
619 (*GetProfilesDirectoryAPtrW) (wbuf, &len);
622 GetWindowsDirectoryW (wbuf, MAX_PATH);
623 wcscat (wbuf, L"\\Profiles");
625 my_wcstombs (buf, wbuf, PATH_MAX);
632 GetSystemDirectoryW (wbuf, MAX_PATH);
633 if ((fh = FindFirstFileW (wbuf, &w32_fd)) != INVALID_HANDLE_VALUE)
636 wcscpy (wcsrchr (wbuf, L'\\') + 1, w32_fd.cFileName);
638 my_wcstombs (buf, wbuf, PATH_MAX);
643 GetWindowsDirectoryW (wbuf, MAX_PATH);
644 my_wcstombs (buf, wbuf, PATH_MAX);
653 fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name);
655 else if (!windows_flag)
657 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, buf, buf2,
659 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
660 prog_name, buf, strerror (errno));
667 tmp = buf = get_short_name (buf);
669 convert_slashes (buf);
671 printf ("%s\n", buf);
677 report_mode (char *filename)
679 switch (cygwin_internal (CW_GET_BINMODE, filename))
682 printf ("%s: binary\n", filename);
685 printf ("%s: text\n", filename);
688 fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename,
695 do_pathconv (char *filename)
697 char *buf = NULL, *tmp;
698 wchar_t *buf2 = NULL;
701 bool print_tmp = false;
702 cygwin_conv_path_t conv_func =
703 (unix_flag ? CCP_WIN_A_TO_POSIX
704 : (path_flag ? CCP_POSIX_TO_WIN_A
705 : CCP_POSIX_TO_WIN_W))
706 | (absolute_flag ? CCP_ABSOLUTE : CCP_RELATIVE);
710 len = strlen (filename);
713 else if (ignore_flag)
717 fprintf (stderr, "%s: can't convert empty path\n", prog_name);
722 len = cygwin_conv_path_list (conv_func, filename, NULL, 0);
724 buf = (char *) malloc (len);
725 if (!unix_flag && !path_flag)
726 buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
729 fprintf (stderr, "%s: out of memory\n", prog_name);
735 err = cygwin_conv_path_list (conv_func, filename, buf, len);
740 buf = get_device_paths (tmp = buf);
744 buf = get_short_paths (tmp = buf);
749 buf = get_long_paths (tmp = buf);
753 convert_slashes (buf);
757 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
758 prog_name, filename, strerror (errno));
764 err = cygwin_conv_path (conv_func, filename,
765 unix_flag ? (void *) buf : (void *) buf2, len);
768 fprintf (stderr, "%s: error converting \"%s\" - %s\n",
769 prog_name, filename, strerror (errno));
774 my_wcstombs (buf, buf2, 32768);
775 buf = get_device_name (tmp = buf);
779 buf = get_short_name (tmp = buf);
784 buf = get_long_name (tmp = buf, len);
788 if (strncmp (buf, "\\\\?\\", 4) == 0)
791 if (strncmp (buf + 4, "UNC\\", 4) == 0)
793 if (strlen (buf) < MAX_PATH + len)
802 convert_slashes (tmp);
806 puts (print_tmp ? tmp : buf);
816 const char *v = strchr (version, ':');
826 len = strchr (v, ' ') - v;
829 cygpath (cygwin) %.*s\n\
830 Path Conversion Utility\n\
831 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\
832 2007, 2008 Red Hat, Inc.\n\
834 ", len, v, __DATE__);
838 do_options (int argc, char **argv, int from_file)
853 options_from_file_flag = 0;
855 while ((c = getopt_long (argc, argv, options,
856 long_options, (int *) NULL)) != EOF)
867 CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
876 if (from_file || !optarg)
888 options_from_file_flag = 1;
920 format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
921 if (strcasecmp (format_type_arg, "dos") == 0)
926 else if (!strcasecmp (format_type_arg, "mixed"))
931 else if (!strcasecmp (format_type_arg, "unix"))
933 else if (!strcasecmp (format_type_arg, "windows"))
946 if (!strcasecmp (optarg, "ANSI"))
947 codepage = GetACP ();
948 else if (!strcasecmp (optarg, "OEM"))
949 codepage = GetOEMCP ();
950 else if (!strcasecmp (optarg, "UTF8")
951 || !strcasecmp (optarg, "UTF-8"))
956 codepage = (UINT) strtoul (optarg, &c, 10);
998 /* If none of the "important" flags are set, -u is default. */
999 if (!unix_flag && !windows_flag && !mode_flag
1000 && (!from_file ? !options_from_file_flag : 1))
1003 /* Only one of ... */
1004 if (unix_flag + windows_flag + mode_flag > 1
1005 + (!from_file ? options_from_file_flag : 0))
1008 /* options_from_file_flag requires a file. */
1009 if (!from_file && options_from_file_flag && !file_arg)
1012 /* longname and shortname don't play well together. */
1013 if (longname_flag && shortname_flag)
1016 /* longname and shortname only make sense with Windows paths. */
1017 if ((longname_flag || shortname_flag) && !windows_flag)
1024 action (int argc, char **argv, int opt)
1031 do_sysfolders (opt);
1035 if (optind > argc - 1)
1038 for (int i = optind; argv[i]; i++)
1040 report_mode (argv[i]);
1042 do_pathconv (argv[i]);
1047 main (int argc, char **argv)
1051 setlocale (LC_CTYPE, "");
1052 prog_name = strrchr (argv[0], '/');
1054 prog_name = strrchr (argv[0], '\\');
1056 prog_name = argv[0];
1060 o = do_options (argc, argv, 0);
1063 action (argc, argv, o);
1067 char buf[PATH_MAX * 2 + 1];
1072 if (strcmp (file_arg, "-"))
1074 if (!(fp = fopen (file_arg, "rt")))
1083 setmode (0, O_TEXT);
1085 setbuf (stdout, NULL);
1087 while (fgets (buf, sizeof (buf), fp))
1090 char *av[4] = { NULL, NULL, NULL, NULL };
1091 char *p = strchr (buf, '\n');
1095 av[ac++] = prog_name;
1097 if (options_from_file_flag && *p == '-')
1099 while (*p && !isspace (*p))
1104 while (*p && isspace (*p))
1108 o = do_options (ac, av, 1);