OSDN Git Service

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