OSDN Git Service

449c5436e4d94e9caf1f63f631f33ffe05e79643
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / cygpath.cc
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.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #define NOCOMATTRIBUTE
12
13 #include <shlobj.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <wchar.h>
17 #include <locale.h>
18 #include <stdlib.h>
19 #include <limits.h>
20 #include <getopt.h>
21 #include <windows.h>
22 #include <io.h>
23 #include <sys/fcntl.h>
24 #include <sys/cygwin.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <ddk/ntddk.h>
28 #include <ddk/winddk.h>
29 #include <ddk/ntifs.h>
30 #include "wide_path.h"
31
32 static const char version[] = "$Revision$";
33
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;
40 static UINT codepage;
41
42 static const char *format_type_arg;
43
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}
71 };
72
73 static char options[] = "ac:df:hilmMopst:uvwAC:DHOPSWF:";
74
75 static void
76 usage (FILE * stream, int status)
77 {
78   if (!ignore_flag || !status)
79     fprintf (stream, "\
80 Usage: %s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\
81        %s [-c HANDLE] \n\
82        %s [-ADHOPSW] \n\
83        %s [-F ID] \n\
84 Convert Unix and Windows format paths, or output system path information\n\
85 \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);
113   if (ignore_flag)
114     /* nothing to do */;
115   else if (stream != stdout)
116     fprintf(stream, "Try `%s --help' for more information.\n", prog_name);
117   else
118     {
119       fprintf (stream, "\
120 Other options:\n\
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\
127 ");
128     }
129   exit (ignore_flag ? 0 : status);
130 }
131
132 static inline BOOLEAN
133 RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size)
134 {
135   uni->Length = 0;
136   uni->MaximumLength = 512;
137   uni->Buffer = (WCHAR *) malloc (size);
138   return uni->Buffer != NULL;
139 }
140
141 static size_t
142 my_wcstombs (char *dest, const wchar_t *src, size_t n)
143 {
144   if (codepage)
145     return WideCharToMultiByte (codepage, 0, src, -1, dest, n, NULL, NULL);
146   else
147     return wcstombs (dest, src, n);
148 }
149
150 static char *
151 get_device_name (char *path)
152 {
153   UNICODE_STRING ntdev, tgtdev, ntdevdir;
154   ANSI_STRING ans;
155   OBJECT_ATTRIBUTES ntobj;
156   NTSTATUS status;
157   HANDLE lnk, dir;
158   char *ret = strdup (path);
159   PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION)
160                                      alloca (4096);
161   BOOLEAN restart;
162   ULONG cont;
163
164   if (strncasecmp (path, "\\Device\\", 8))
165     return ret;
166
167   if (!RtlAllocateUnicodeString (&ntdev, 65536))
168     return ret;
169   if (!RtlAllocateUnicodeString (&tgtdev, 65536))
170     return ret;
171   RtlInitAnsiString (&ans, path);
172   RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE);
173
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))
180     {
181       status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL);
182       ZwClose (lnk);
183       if (!NT_SUCCESS (status))
184         goto out;
185       RtlCopyUnicodeString (&ntdev, &tgtdev);
186     }
187   else if (status != STATUS_OBJECT_TYPE_MISMATCH)
188     goto out;
189
190   for (int i = 0; i < 2; ++i)
191     {
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"\\??");
195
196       /* Open the directory... */
197       InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE,
198                                   NULL, NULL);
199       status = ZwOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj);
200       if (!NT_SUCCESS (status))
201         break;
202
203       /* ...and scan it. */
204       for (restart = TRUE, cont = 0;
205            NT_SUCCESS (ZwQueryDirectoryObject (dir, odi, 4096, TRUE,
206                                                restart, &cont, NULL));
207            restart = FALSE)
208         {
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))
214             continue;
215           tgtdev.Length = 0;
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);
220           ZwClose (lnk);
221           if (!NT_SUCCESS (status))
222             continue;
223           if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE))
224             {
225               /* If the comparison succeeds, the name of the directory entry is
226                  a valid DOS device name, if prepended with "\\.\".  Return that
227                  valid DOS path. */
228               ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName);
229               free (ret);
230               ret = (char *) malloc (len + 4);
231               strcpy (ret, "\\\\.\\");
232               ans.Length = 0;
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':'))
244                 {
245                   ZwClose (dir);
246                   goto out;
247                 }
248             }
249         }
250       ZwClose (dir);
251     }
252
253 out:
254   free (tgtdev.Buffer);
255   free (ntdev.Buffer);
256   return ret;
257 }
258
259 static char *
260 get_device_paths (char *path)
261 {
262   char *sbuf;
263   char *ptr;
264   int n = 1;
265
266   ptr = path;
267   while ((ptr = strchr (ptr, ';')))
268     {
269       ptr++;
270       n++;
271     }
272
273   char *paths[n];
274   DWORD acc = 0;
275   int i;
276   if (!n)
277     return strdup ("");
278
279   for (i = 0, ptr = path; ptr; i++)
280     {
281       char *next = ptr;
282       ptr = strchr (ptr, ';');
283       if (ptr)
284         *ptr++ = 0;
285       paths[i] = get_device_name (next);
286       acc += strlen (paths[i]) + 1;
287     }
288
289   sbuf = (char *) malloc (acc + 1);
290   if (sbuf == NULL)
291     {
292       fprintf (stderr, "%s: out of memory\n", prog_name);
293       exit (1);
294     }
295
296   sbuf[0] = '\0';
297   for (i = 0; i < n; i++)
298     {
299       strcat (strcat (sbuf, paths[i]), ";");
300       free (paths[i]);
301     }
302
303   strchr (sbuf, '\0')[-1] = '\0';
304   return sbuf;
305 }
306
307 static char *
308 get_short_paths (char *path)
309 {
310   wchar_t *sbuf;
311   wchar_t *sptr;
312   char *next;
313   char *ptr = path;
314   char *end = strrchr (path, 0);
315   DWORD acc = 0;
316   DWORD len;
317
318   while (ptr != NULL)
319     {
320       next = ptr;
321       ptr = strchr (ptr, ';');
322       if (ptr)
323         *ptr++ = 0;
324       wide_path wpath (next);
325       len = GetShortPathNameW (wpath, NULL, 0);
326       if (!len)
327         {
328           fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
329                    next);
330           exit (2);
331         }
332       acc += len + 1;
333     }
334   sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
335   if (sbuf == NULL)
336     {
337       fprintf (stderr, "%s: out of memory\n", prog_name);
338       exit (1);
339     }
340   ptr = path;
341   for (;;)
342     {
343       wide_path wpath (ptr);
344       len = GetShortPathNameW (wpath, sptr, acc);
345       if (!len)
346         {
347           fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
348                    ptr);
349           exit (2);
350         }
351
352       ptr = strrchr (ptr, 0);
353       sptr = wcsrchr (sptr, 0);
354       if (ptr == end)
355         break;
356       *sptr = L';';
357       ++ptr, ++sptr;
358       acc -= len + 1;
359     }
360   len = my_wcstombs (NULL, sbuf, 0) + 1;
361   ptr = (char *) malloc (len);
362   if (ptr == NULL)
363     {
364       fprintf (stderr, "%s: out of memory\n", prog_name);
365       exit (1);
366     }
367   my_wcstombs (ptr, sbuf, len);
368   return ptr;
369 }
370
371 static char *
372 get_short_name (const char *filename)
373 {
374   wchar_t buf[32768];
375   char *sbuf;
376   wide_path wpath (filename);
377   DWORD len = GetShortPathNameW (wpath, buf, 32768);
378   if (!len)
379     {
380       fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
381                filename);
382       exit (2);
383     }
384   len = my_wcstombs (NULL, buf, 0) + 1;
385   sbuf = (char *) malloc (len);
386   if (sbuf == NULL)
387     {
388       fprintf (stderr, "%s: out of memory\n", prog_name);
389       exit (1);
390     }
391   my_wcstombs (sbuf, buf, len);
392   return sbuf;
393 }
394
395 static DWORD WINAPI
396 get_long_path_name_w32impl (LPCWSTR src, LPWSTR sbuf, DWORD)
397 {
398   wchar_t *buf1 = (wchar_t *) malloc (32768);
399   wchar_t *buf2 = (wchar_t *) malloc (32768);
400   wchar_t *ptr;
401   const wchar_t *pelem, *next;
402   WIN32_FIND_DATAW w32_fd;
403   DWORD len;
404
405   wcscpy (buf1, src);
406   *buf2 = L'\0';
407   pelem = src;
408   ptr = buf2;
409   while (pelem)
410     {
411       next = pelem;
412       if (*next == L'\\')
413         {
414           wcscat (ptr++, L"\\");
415           pelem++;
416           if (!*pelem)
417             break;
418           continue;
419         }
420       pelem = wcschr (next, L'\\');
421       len = pelem ? (pelem++ - next) : wcslen (next);
422       wcsncpy (ptr, next, len);
423       ptr[len] = L'\0';
424       if (next[1] != L':' && wcscmp(next, L".") && wcscmp(next, L".."))
425         {
426           HANDLE h;
427           h = FindFirstFileW (buf2, &w32_fd);
428           if (h != INVALID_HANDLE_VALUE)
429             {
430               wcscpy (ptr, w32_fd.cFileName);
431               FindClose (h);
432             }
433         }
434       ptr += wcslen (ptr);
435       if (pelem)
436         {
437           *ptr++ = '\\';
438           *ptr = 0;
439         }
440     }
441   if (sbuf)
442     wcscpy (sbuf, buf2);
443   SetLastError (0);
444   len = wcslen (buf2) + (sbuf ? 0 : 1);
445   free (buf1);
446   free (buf2);
447   return len;
448 }
449
450 static char *
451 get_long_name (const char *filename, DWORD& len)
452 {
453   char *sbuf;
454   wchar_t buf[32768];
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;
460
461   wide_path wpath (filename);
462   len = GetLongPathName (wpath, buf, 32768);
463   if (len == 0)
464     {
465       DWORD err = GetLastError ();
466
467       if (err == ERROR_INVALID_PARAMETER)
468         {
469           fprintf (stderr, "%s: cannot create long name of %s\n", prog_name,
470                    filename);
471           exit (2);
472         }
473       else if (err == ERROR_FILE_NOT_FOUND)
474         get_long_path_name_w32impl (wpath, buf, 32768);
475       else
476         {
477           buf[0] = L'\0';
478           wcsncat (buf, wpath, 32767);
479         }
480     }
481   len = my_wcstombs (NULL, buf, 0);
482   sbuf = (char *) malloc (len + 1);
483   if (!sbuf)
484     {
485       fprintf (stderr, "%s: out of memory\n", prog_name);
486       exit (1);
487     }
488   my_wcstombs (sbuf, buf, len + 1);
489   return sbuf;
490 }
491
492 static char *
493 get_long_paths (char *path)
494 {
495   char *sbuf;
496   char *ptr;
497   int n = 1;
498
499   ptr = path;
500   while ((ptr = strchr (ptr, ';')))
501     {
502       ptr++;
503       n++;
504     }
505
506   char *paths[n];
507   DWORD acc = 0;
508   int i;
509   if (!n)
510     return strdup ("");
511
512   for (i = 0, ptr = path; ptr; i++)
513     {
514       DWORD len;
515       char *next = ptr;
516       ptr = strchr (ptr, ';');
517       if (ptr)
518         *ptr++ = 0;
519       paths[i] = get_long_name (next, len);
520       acc += len + 1;
521     }
522
523   sbuf = (char *) malloc (acc + 1);
524   if (sbuf == NULL)
525     {
526       fprintf (stderr, "%s: out of memory\n", prog_name);
527       exit (1);
528     }
529
530   sbuf[0] = '\0';
531   for (i = 0; i < n; i++)
532     {
533       strcat (strcat (sbuf, paths[i]), ";");
534       free (paths[i]);
535     }
536
537   strchr (sbuf, '\0')[-1] = '\0';
538   return sbuf;
539 }
540
541 static void
542 convert_slashes (char* name)
543 {
544   while ((name = strchr (name, '\\')) != NULL)
545     {
546       if (*name == '\\')
547         *name = '/';
548        name++;
549    }
550 }
551
552 static bool
553 get_special_folder (char* path, int id)
554 {
555   WCHAR wpath[MAX_PATH];
556
557   path[0] = '\0';
558   wpath[0] = L'\0';
559   LPITEMIDLIST pidl = 0;
560   if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
561     return false;
562   if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
563     return false;
564   my_wcstombs (path, wpath, PATH_MAX);
565   return true;
566 }
567
568 static void
569 do_sysfolders (char option)
570 {
571   char *buf, buf1[PATH_MAX], buf2[PATH_MAX];
572   char *tmp = NULL;
573   WCHAR wbuf[MAX_PATH];
574   DWORD len = MAX_PATH;
575   WIN32_FIND_DATAW w32_fd;
576   HINSTANCE k32;
577   BOOL (*GetProfilesDirectoryAPtrW) (LPWSTR, LPDWORD) = 0;
578
579   buf = buf1;
580   buf[0] = 0;
581   switch (option)
582     {
583     case 'D':
584       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
585                                              : CSIDL_DESKTOPDIRECTORY);
586       break;
587
588     case 'P':
589       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_PROGRAMS
590                                              : CSIDL_PROGRAMS);
591       break;
592
593     case 'O':
594       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
595                                              : CSIDL_PERSONAL);
596       break;
597
598     case 'F':
599       {
600         int val = -1, len = -1;
601         if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
602               && len == (int) strlen (output_arg) && val >= 0))
603           {
604             fprintf (stderr, "%s: syntax error in special folder ID %s\n",
605                      prog_name, output_arg);
606             exit (1);
607           }
608         get_special_folder (buf, val);
609       }
610       break;
611
612     case 'H':
613       k32 = LoadLibrary ("userenv");
614       if (k32)
615         GetProfilesDirectoryAPtrW = (BOOL (*) (LPWSTR, LPDWORD))
616           GetProcAddress (k32, "GetProfilesDirectoryW");
617       if (GetProfilesDirectoryAPtrW)
618         (*GetProfilesDirectoryAPtrW) (wbuf, &len);
619       else
620         {
621           GetWindowsDirectoryW (wbuf, MAX_PATH);
622           wcscat (wbuf, L"\\Profiles");
623         }
624       my_wcstombs (buf, wbuf, PATH_MAX);
625       break;
626
627     case 'S':
628       {
629         HANDLE fh;
630
631         GetSystemDirectoryW (wbuf, MAX_PATH);
632         if ((fh = FindFirstFileW (wbuf, &w32_fd)) != INVALID_HANDLE_VALUE)
633           {
634             FindClose (fh);
635             wcscpy (wcsrchr (wbuf, L'\\') + 1, w32_fd.cFileName);
636           }
637         my_wcstombs (buf, wbuf, PATH_MAX);
638       }
639       break;
640
641     case 'W':
642       GetWindowsDirectoryW (wbuf, MAX_PATH);
643       my_wcstombs (buf, wbuf, PATH_MAX);
644       break;
645
646     default:
647       usage (stderr, 1);
648     }
649
650   if (!buf[0])
651     {
652       fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name);
653     }
654   else if (!windows_flag)
655     {
656       if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, buf, buf2,
657           PATH_MAX))
658         fprintf (stderr, "%s: error converting \"%s\" - %s\n",
659                  prog_name, buf, strerror (errno));
660       else
661         buf = buf2;
662     }
663   else
664     {
665       if (shortname_flag)
666           tmp = buf = get_short_name (buf);
667       if (mixed_flag)
668         convert_slashes (buf);
669     }
670   printf ("%s\n", buf);
671   if (tmp)
672     free (tmp);
673 }
674
675 static void
676 report_mode (char *filename)
677 {
678   switch (cygwin_internal (CW_GET_BINMODE, filename))
679     {
680     case O_BINARY:
681       printf ("%s: binary\n", filename);
682       break;
683     case O_TEXT:
684       printf ("%s: text\n", filename);
685       break;
686     default:
687       fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename,
688                strerror (errno));
689       break;
690     }
691 }
692
693 static void
694 do_pathconv (char *filename)
695 {
696   char *buf = NULL, *tmp;
697   wchar_t *buf2 = NULL;
698   DWORD len;
699   ssize_t err;
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);
706
707   if (!path_flag)
708     {
709       len = strlen (filename);
710       if (len)
711         len = 32768;
712       else if (ignore_flag)
713         exit (0);
714       else
715         {
716           fprintf (stderr, "%s: can't convert empty path\n", prog_name);
717           exit (1);
718         }
719     }
720   else
721     len = cygwin_conv_path_list (conv_func, filename, NULL, 0);
722
723   buf = (char *) malloc (len);
724   if (!unix_flag && !path_flag)
725     buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
726   if (buf == NULL)
727     {
728       fprintf (stderr, "%s: out of memory\n", prog_name);
729       exit (1);
730     }
731
732   if (path_flag)
733     {
734       err = cygwin_conv_path_list (conv_func, filename, buf, len);
735       if (!unix_flag)
736         {
737           if (err)
738             /* oops */;
739           buf = get_device_paths (tmp = buf);
740           free (tmp);
741           if (shortname_flag)
742             {
743               buf = get_short_paths (tmp = buf);
744               free (tmp);
745             }
746           if (longname_flag)
747             {
748               buf = get_long_paths (tmp = buf);
749               free (tmp);
750             }
751           if (mixed_flag)
752             convert_slashes (buf);
753         }
754       if (err)
755         {
756           fprintf (stderr, "%s: error converting \"%s\" - %s\n",
757                    prog_name, filename, strerror (errno));
758           exit (1);
759         }
760     }
761   else
762     {
763       err = cygwin_conv_path (conv_func, filename,
764                               unix_flag ? (void *) buf : (void *) buf2, len);
765       if (err)
766         {
767           fprintf (stderr, "%s: error converting \"%s\" - %s\n",
768                    prog_name, filename, strerror (errno));
769           exit (1);
770         }
771       if (!unix_flag)
772         {
773           my_wcstombs (buf, buf2, 32768);
774           buf = get_device_name (tmp = buf);
775           free (tmp);
776           if (shortname_flag)
777             {
778               buf = get_short_name (tmp = buf);
779               free (tmp);
780             }
781           if (longname_flag)
782             {
783               buf = get_long_name (tmp = buf, len);
784               free (tmp);
785             }
786           tmp = buf;
787           if (strncmp (buf, "\\\\?\\", 4) == 0)
788             {
789               len = 4;
790               if (strncmp (buf + 4, "UNC\\", 4) == 0)
791                 len = 6;
792               if (strlen (buf) < MAX_PATH + len)
793                 {
794                   tmp += len;
795                   if (len == 6)
796                     *tmp = '\\';
797                   print_tmp = true;
798                 }
799             }
800           if (mixed_flag)
801             convert_slashes (tmp);
802         }
803     }
804
805   puts (print_tmp ? tmp : buf);
806   if (buf2)
807     free (buf2);
808   if (buf)
809     free (buf);
810 }
811
812 static void
813 print_version ()
814 {
815   const char *v = strchr (version, ':');
816   int len;
817   if (!v)
818     {
819       v = "?";
820       len = 1;
821     }
822   else
823     {
824       v += 2;
825       len = strchr (v, ' ') - v;
826     }
827   printf ("\
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\
832 Compiled on %s\n\
833 ", len, v, __DATE__);
834 }
835
836 static int
837 do_options (int argc, char **argv, int from_file)
838 {
839   int c, o = 0;
840   path_flag = 0;
841   unix_flag = 0;
842   windows_flag = 0;
843   shortname_flag = 0;
844   longname_flag = 0;
845   mixed_flag = 0;
846   ignore_flag = 0;
847   allusers_flag = 0;
848   output_flag = 0;
849   mode_flag = 0;
850   codepage = 0;
851   if (!from_file)
852     options_from_file_flag = 0;
853   optind = 0;
854   while ((c = getopt_long (argc, argv, options,
855                            long_options, (int *) NULL)) != EOF)
856     {
857       switch (c)
858         {
859         case 'a':
860           absolute_flag = 1;
861           break;
862
863         case 'c':
864           if (!optarg)
865             usage (stderr, 1);
866           CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
867           break;
868
869         case 'd':
870           windows_flag = 1;
871           shortname_flag = 1;
872           break;
873
874         case 'f':
875           if (from_file || !optarg)
876             usage (stderr, 1);
877           file_arg = optarg;
878           break;
879
880         case 'M':
881           mode_flag = 1;
882           break;
883
884         case 'o':
885           if (from_file)
886             usage (stderr, 1);
887           options_from_file_flag = 1;
888           break;
889
890         case 'p':
891           path_flag = 1;
892           break;
893
894         case 'u':
895           unix_flag = 1;
896           break;
897
898         case 'w':
899           windows_flag = 1;
900           break;
901
902          case 'm':
903           windows_flag = 1;
904           mixed_flag = 1;
905           break;
906
907         case 'l':
908           longname_flag = 1;
909           break;
910
911         case 's':
912           shortname_flag = 1;
913           break;
914
915         case 't':
916           if (!optarg)
917             usage (stderr, 1);
918
919           format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
920           if (strcasecmp (format_type_arg, "dos") == 0)
921             {
922               windows_flag = 1;
923               shortname_flag = 1;
924             }
925           else if (!strcasecmp (format_type_arg, "mixed"))
926             {
927               windows_flag = 1;
928               mixed_flag = 1;
929             }
930           else if (!strcasecmp (format_type_arg, "unix"))
931             unix_flag = 1;
932           else if (!strcasecmp (format_type_arg, "windows"))
933             windows_flag = 1;
934           else
935             usage (stderr, 1);
936           break;
937
938         case 'A':
939           allusers_flag = 1;
940           break;
941
942         case 'C':
943           if (!optarg)
944             usage (stderr, 1);
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"))
951             codepage = CP_UTF8;
952           else
953             {
954               char *c;
955               codepage = (UINT) strtoul (optarg, &c, 10);
956               if (*c)
957                 usage (stderr, 1);
958             }
959           break;
960
961         case 'D':
962         case 'H':
963         case 'O':
964         case 'P':
965         case 'S':
966         case 'W':
967           ++output_flag;
968           o = c;
969           break;
970
971         case 'F':
972           if (!optarg)
973             usage (stderr, 1);
974           ++output_flag;
975           output_arg = optarg;
976           o = c;
977           break;
978
979         case 'i':
980           ignore_flag = 1;
981           break;
982
983         case 'h':
984           usage (stdout, 0);
985           break;
986
987         case 'v':
988           print_version ();
989           exit (0);
990
991         default:
992           usage (stderr, 1);
993           break;
994         }
995     }
996
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))
1000     unix_flag = 1;
1001
1002   /* Only one of ... */
1003   if (unix_flag + windows_flag + mode_flag > 1
1004       + (!from_file ? options_from_file_flag : 0))
1005     usage (stderr, 1);
1006
1007   /* options_from_file_flag requires a file. */
1008   if (!from_file && options_from_file_flag && !file_arg)
1009     usage (stderr, 1);
1010
1011   /* longname and shortname don't play well together. */
1012   if (longname_flag && shortname_flag)
1013     usage (stderr, 1);
1014
1015   /* longname and shortname only make sense with Windows paths. */
1016   if ((longname_flag || shortname_flag) && !windows_flag)
1017     usage (stderr, 1);
1018
1019   return o;
1020 }
1021
1022 static void
1023 action (int argc, char **argv, int opt)
1024 {
1025   if (output_flag)
1026     {
1027       if (argv[optind])
1028         usage (stderr, 1);
1029
1030       do_sysfolders (opt);
1031     }
1032   else
1033     {
1034       if (optind > argc - 1)
1035         usage (stderr, 1);
1036
1037       for (int i = optind; argv[i]; i++)
1038         if (mode_flag)
1039           report_mode (argv[i]);
1040         else
1041           do_pathconv (argv[i]);
1042     }
1043 }
1044
1045 int
1046 main (int argc, char **argv)
1047 {
1048   int o;
1049
1050   setlocale (LC_CTYPE, "");
1051   prog_name = strrchr (argv[0], '/');
1052   if (!prog_name)
1053     prog_name = strrchr (argv[0], '\\');
1054   if (!prog_name)
1055     prog_name = argv[0];
1056   else
1057     prog_name++;
1058
1059   o = do_options (argc, argv, 0);
1060
1061   if (!file_arg)
1062     action (argc, argv, o);
1063   else
1064     {
1065       FILE *fp;
1066       char buf[PATH_MAX * 2 + 1];
1067
1068       if (argv[optind])
1069         usage (stderr, 1);
1070
1071       if (strcmp (file_arg, "-"))
1072         {
1073           if (!(fp = fopen (file_arg, "rt")))
1074             {
1075               perror ("cygpath");
1076               exit (1);
1077             }
1078         }
1079       else
1080         {
1081           fp = stdin;
1082           setmode (0, O_TEXT);
1083         }
1084       setbuf (stdout, NULL);
1085
1086       while (fgets (buf, sizeof (buf), fp))
1087         {
1088           int ac = 0;
1089           char *av[4] = { NULL, NULL, NULL, NULL };
1090           char *p = strchr (buf, '\n');
1091           if (p)
1092             *p = '\0';
1093           p = buf;
1094           av[ac++] = prog_name;
1095           av[ac++] = p;
1096           if (options_from_file_flag && *p == '-')
1097             {
1098               while (*p && !isspace (*p))
1099                 ++p;
1100               if (*p)
1101                 {
1102                   *p++ = '\0';
1103                   while (*p && isspace (*p))
1104                     ++p;
1105                   av[ac++] = p;
1106                 }
1107               o = do_options (ac, av, 1);
1108             }
1109           else
1110             {
1111               output_flag = 0;
1112               optind = 1;
1113             }
1114           action (ac, av, o);
1115         }
1116     }
1117   exit (0);
1118 }