OSDN Git Service

* utils.sgml (kill): Add SIGIO, SIGCLD, and SIGPWR.
[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               ZwClose (dir);
237               goto out;
238             }
239         }
240       ZwClose (dir);
241     }
242
243 out:
244   free (tgtdev.Buffer);
245   free (ntdev.Buffer);
246   return ret;
247 }
248
249 static char *
250 get_device_paths (char *path)
251 {
252   char *sbuf;
253   char *ptr;
254   int n = 1;
255
256   ptr = path;
257   while ((ptr = strchr (ptr, ';')))
258     {
259       ptr++;
260       n++;
261     }
262
263   char *paths[n];
264   DWORD acc = 0;
265   int i;
266   if (!n)
267     return strdup ("");
268
269   for (i = 0, ptr = path; ptr; i++)
270     {
271       char *next = ptr;
272       ptr = strchr (ptr, ';');
273       if (ptr)
274         *ptr++ = 0;
275       paths[i] = get_device_name (next);
276       acc += strlen (paths[i]) + 1;
277     }
278
279   sbuf = (char *) malloc (acc + 1);
280   if (sbuf == NULL)
281     {
282       fprintf (stderr, "%s: out of memory\n", prog_name);
283       exit (1);
284     }
285
286   sbuf[0] = '\0';
287   for (i = 0; i < n; i++)
288     {
289       strcat (strcat (sbuf, paths[i]), ";");
290       free (paths[i]);
291     }
292
293   strchr (sbuf, '\0')[-1] = '\0';
294   return sbuf;
295 }
296
297 static char *
298 get_short_paths (char *path)
299 {
300   wchar_t *sbuf;
301   wchar_t *sptr;
302   char *next;
303   char *ptr = path;
304   char *end = strrchr (path, 0);
305   DWORD acc = 0;
306   DWORD len;
307
308   while (ptr != NULL)
309     {
310       next = ptr;
311       ptr = strchr (ptr, ';');
312       if (ptr)
313         *ptr++ = 0;
314       wide_path wpath (next);
315       len = GetShortPathNameW (wpath, NULL, 0);
316       if (!len)
317         {
318           fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
319                    next);
320           exit (2);
321         }
322       acc += len + 1;
323     }
324   sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t));
325   if (sbuf == NULL)
326     {
327       fprintf (stderr, "%s: out of memory\n", prog_name);
328       exit (1);
329     }
330   ptr = path;
331   for (;;)
332     {
333       wide_path wpath (ptr);
334       len = GetShortPathNameW (wpath, sptr, acc);
335       if (!len)
336         {
337           fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
338                    ptr);
339           exit (2);
340         }
341
342       ptr = strrchr (ptr, 0);
343       sptr = wcsrchr (sptr, 0);
344       if (ptr == end)
345         break;
346       *sptr = L';';
347       ++ptr, ++sptr;
348       acc -= len + 1;
349     }
350   len = my_wcstombs (NULL, sbuf, 0) + 1;
351   ptr = (char *) malloc (len);
352   if (ptr == NULL)
353     {
354       fprintf (stderr, "%s: out of memory\n", prog_name);
355       exit (1);
356     }
357   my_wcstombs (ptr, sbuf, len);
358   return ptr;
359 }
360
361 static char *
362 get_short_name (const char *filename)
363 {
364   wchar_t buf[32768];
365   char *sbuf;
366   wide_path wpath (filename);
367   DWORD len = GetShortPathNameW (wpath, buf, 32768);
368   if (!len)
369     {
370       fprintf (stderr, "%s: cannot create short name of %s\n", prog_name,
371                filename);
372       exit (2);
373     }
374   len = my_wcstombs (NULL, buf, 0) + 1;
375   sbuf = (char *) malloc (len);
376   if (sbuf == NULL)
377     {
378       fprintf (stderr, "%s: out of memory\n", prog_name);
379       exit (1);
380     }
381   my_wcstombs (sbuf, buf, len);
382   return sbuf;
383 }
384
385 static DWORD WINAPI
386 get_long_path_name_w32impl (LPCWSTR src, LPWSTR sbuf, DWORD)
387 {
388   wchar_t *buf1 = (wchar_t *) malloc (32768);
389   wchar_t *buf2 = (wchar_t *) malloc (32768);
390   wchar_t *ptr;
391   const wchar_t *pelem, *next;
392   WIN32_FIND_DATAW w32_fd;
393   DWORD len;
394
395   wcscpy (buf1, src);
396   *buf2 = L'\0';
397   pelem = src;
398   ptr = buf2;
399   while (pelem)
400     {
401       next = pelem;
402       if (*next == L'\\')
403         {
404           wcscat (ptr++, L"\\");
405           pelem++;
406           if (!*pelem)
407             break;
408           continue;
409         }
410       pelem = wcschr (next, L'\\');
411       len = pelem ? (pelem++ - next) : wcslen (next);
412       wcsncpy (ptr, next, len);
413       ptr[len] = L'\0';
414       if (next[1] != L':' && wcscmp(next, L".") && wcscmp(next, L".."))
415         {
416           HANDLE h;
417           h = FindFirstFileW (buf2, &w32_fd);
418           if (h != INVALID_HANDLE_VALUE)
419             {
420               wcscpy (ptr, w32_fd.cFileName);
421               FindClose (h);
422             }
423         }
424       ptr += wcslen (ptr);
425       if (pelem)
426         {
427           *ptr++ = '\\';
428           *ptr = 0;
429         }
430     }
431   if (sbuf)
432     wcscpy (sbuf, buf2);
433   SetLastError (0);
434   len = wcslen (buf2) + (sbuf ? 0 : 1);
435   free (buf1);
436   free (buf2);
437   return len;
438 }
439
440 static char *
441 get_long_name (const char *filename, DWORD& len)
442 {
443   char *sbuf;
444   wchar_t buf[32768];
445   static HINSTANCE k32 = LoadLibrary ("kernel32.dll");
446   static DWORD (WINAPI *GetLongPathName) (LPCWSTR, LPWSTR, DWORD) =
447     (DWORD (WINAPI *) (LPCWSTR, LPWSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameW");
448   if (!GetLongPathName)
449     GetLongPathName = get_long_path_name_w32impl;
450
451   wide_path wpath (filename);
452   len = GetLongPathName (wpath, buf, 32768);
453   if (len == 0)
454     {
455       DWORD err = GetLastError ();
456
457       if (err == ERROR_INVALID_PARAMETER)
458         {
459           fprintf (stderr, "%s: cannot create long name of %s\n", prog_name,
460                    filename);
461           exit (2);
462         }
463       else if (err == ERROR_FILE_NOT_FOUND)
464         get_long_path_name_w32impl (wpath, buf, 32768);
465       else
466         {
467           buf[0] = L'\0';
468           wcsncat (buf, wpath, 32767);
469         }
470     }
471   len = my_wcstombs (NULL, buf, 0);
472   sbuf = (char *) malloc (len + 1);
473   if (!sbuf)
474     {
475       fprintf (stderr, "%s: out of memory\n", prog_name);
476       exit (1);
477     }
478   my_wcstombs (sbuf, buf, len + 1);
479   return sbuf;
480 }
481
482 static char *
483 get_long_paths (char *path)
484 {
485   char *sbuf;
486   char *ptr;
487   int n = 1;
488
489   ptr = path;
490   while ((ptr = strchr (ptr, ';')))
491     {
492       ptr++;
493       n++;
494     }
495
496   char *paths[n];
497   DWORD acc = 0;
498   int i;
499   if (!n)
500     return strdup ("");
501
502   for (i = 0, ptr = path; ptr; i++)
503     {
504       DWORD len;
505       char *next = ptr;
506       ptr = strchr (ptr, ';');
507       if (ptr)
508         *ptr++ = 0;
509       paths[i] = get_long_name (next, len);
510       acc += len + 1;
511     }
512
513   sbuf = (char *) malloc (acc + 1);
514   if (sbuf == NULL)
515     {
516       fprintf (stderr, "%s: out of memory\n", prog_name);
517       exit (1);
518     }
519
520   sbuf[0] = '\0';
521   for (i = 0; i < n; i++)
522     {
523       strcat (strcat (sbuf, paths[i]), ";");
524       free (paths[i]);
525     }
526
527   strchr (sbuf, '\0')[-1] = '\0';
528   return sbuf;
529 }
530
531 static void
532 convert_slashes (char* name)
533 {
534   while ((name = strchr (name, '\\')) != NULL)
535     {
536       if (*name == '\\')
537         *name = '/';
538        name++;
539    }
540 }
541
542 static bool
543 get_special_folder (char* path, int id)
544 {
545   WCHAR wpath[MAX_PATH];
546
547   path[0] = '\0';
548   wpath[0] = L'\0';
549   LPITEMIDLIST pidl = 0;
550   if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK)
551     return false;
552   if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0])
553     return false;
554   my_wcstombs (path, wpath, PATH_MAX);
555   return true;
556 }
557
558 static void
559 do_sysfolders (char option)
560 {
561   char *buf, buf1[PATH_MAX], buf2[PATH_MAX];
562   char *tmp = NULL;
563   WCHAR wbuf[MAX_PATH];
564   DWORD len = MAX_PATH;
565   WIN32_FIND_DATAW w32_fd;
566   HINSTANCE k32;
567   BOOL (*GetProfilesDirectoryAPtrW) (LPWSTR, LPDWORD) = 0;
568
569   buf = buf1;
570   buf[0] = 0;
571   switch (option)
572     {
573     case 'D':
574       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY
575                                              : CSIDL_DESKTOPDIRECTORY);
576       break;
577
578     case 'P':
579       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_PROGRAMS
580                                              : CSIDL_PROGRAMS);
581       break;
582
583     case 'O':
584       get_special_folder (buf, allusers_flag ? CSIDL_COMMON_DOCUMENTS
585                                              : CSIDL_PERSONAL);
586       break;
587
588     case 'F':
589       {
590         int val = -1, len = -1;
591         if (!(sscanf (output_arg, "%i%n", &val, &len) == 1
592               && len == (int) strlen (output_arg) && val >= 0))
593           {
594             fprintf (stderr, "%s: syntax error in special folder ID %s\n",
595                      prog_name, output_arg);
596             exit (1);
597           }
598         get_special_folder (buf, val);
599       }
600       break;
601
602     case 'H':
603       k32 = LoadLibrary ("userenv");
604       if (k32)
605         GetProfilesDirectoryAPtrW = (BOOL (*) (LPWSTR, LPDWORD))
606           GetProcAddress (k32, "GetProfilesDirectoryW");
607       if (GetProfilesDirectoryAPtrW)
608         (*GetProfilesDirectoryAPtrW) (wbuf, &len);
609       else
610         {
611           GetWindowsDirectoryW (wbuf, MAX_PATH);
612           wcscat (wbuf, L"\\Profiles");
613         }
614       my_wcstombs (buf, wbuf, PATH_MAX);
615       break;
616
617     case 'S':
618       {
619         HANDLE fh;
620
621         GetSystemDirectoryW (wbuf, MAX_PATH);
622         if ((fh = FindFirstFileW (wbuf, &w32_fd)) != INVALID_HANDLE_VALUE)
623           {
624             FindClose (fh);
625             wcscpy (wcsrchr (wbuf, L'\\') + 1, w32_fd.cFileName);
626           }
627         my_wcstombs (buf, wbuf, PATH_MAX);
628       }
629       break;
630
631     case 'W':
632       GetWindowsDirectoryW (wbuf, MAX_PATH);
633       my_wcstombs (buf, wbuf, PATH_MAX);
634       break;
635
636     default:
637       usage (stderr, 1);
638     }
639
640   if (!buf[0])
641     {
642       fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name);
643     }
644   else if (!windows_flag)
645     {
646       if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, buf, buf2,
647           PATH_MAX))
648         fprintf (stderr, "%s: error converting \"%s\" - %s\n",
649                  prog_name, buf, strerror (errno));
650       else
651         buf = buf2;
652     }
653   else
654     {
655       if (shortname_flag)
656           tmp = buf = get_short_name (buf);
657       if (mixed_flag)
658         convert_slashes (buf);
659     }
660   printf ("%s\n", buf);
661   if (tmp)
662     free (tmp);
663 }
664
665 static void
666 report_mode (char *filename)
667 {
668   switch (cygwin_internal (CW_GET_BINMODE, filename))
669     {
670     case O_BINARY:
671       printf ("%s: binary\n", filename);
672       break;
673     case O_TEXT:
674       printf ("%s: text\n", filename);
675       break;
676     default:
677       fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename,
678                strerror (errno));
679       break;
680     }
681 }
682
683 static void
684 do_pathconv (char *filename)
685 {
686   char *buf = NULL, *tmp;
687   wchar_t *buf2 = NULL;
688   DWORD len;
689   ssize_t err;
690   bool print_tmp = false;
691   cygwin_conv_path_t conv_func =
692                       (unix_flag ? CCP_WIN_A_TO_POSIX
693                                  : (path_flag ? CCP_POSIX_TO_WIN_A
694                                               : CCP_POSIX_TO_WIN_W))
695                     | (absolute_flag ? CCP_ABSOLUTE : CCP_RELATIVE);
696
697   if (!path_flag)
698     {
699       len = strlen (filename);
700       if (len)
701         len = 32768;
702       else if (ignore_flag)
703         exit (0);
704       else
705         {
706           fprintf (stderr, "%s: can't convert empty path\n", prog_name);
707           exit (1);
708         }
709     }
710   else
711     len = cygwin_conv_path_list (conv_func, filename, NULL, 0);
712
713   buf = (char *) malloc (len);
714   if (!unix_flag && !path_flag)
715     buf2 = (wchar_t *) malloc (len * sizeof (wchar_t));
716   if (buf == NULL)
717     {
718       fprintf (stderr, "%s: out of memory\n", prog_name);
719       exit (1);
720     }
721
722   if (path_flag)
723     {
724       err = cygwin_conv_path_list (conv_func, filename, buf, len);
725       if (!unix_flag)
726         {
727           if (err)
728             /* oops */;
729           buf = get_device_paths (tmp = buf);
730           free (tmp);
731           if (shortname_flag)
732             {
733               buf = get_short_paths (tmp = buf);
734               free (tmp);
735             }
736           if (longname_flag)
737             {
738               buf = get_long_paths (tmp = buf);
739               free (tmp);
740             }
741           if (mixed_flag)
742             convert_slashes (buf);
743         }
744       if (err)
745         {
746           fprintf (stderr, "%s: error converting \"%s\" - %s\n",
747                    prog_name, filename, strerror (errno));
748           exit (1);
749         }
750     }
751   else
752     {
753       err = cygwin_conv_path (conv_func, filename,
754                               unix_flag ? (void *) buf : (void *) buf2, len);
755       if (err)
756         {
757           fprintf (stderr, "%s: error converting \"%s\" - %s\n",
758                    prog_name, filename, strerror (errno));
759           exit (1);
760         }
761       if (!unix_flag)
762         {
763           my_wcstombs (buf, buf2, 32768);
764           buf = get_device_name (tmp = buf);
765           free (tmp);
766           if (shortname_flag)
767             {
768               buf = get_short_name (tmp = buf);
769               free (tmp);
770             }
771           if (longname_flag)
772             {
773               buf = get_long_name (tmp = buf, len);
774               free (tmp);
775             }
776           tmp = buf;
777           if (strncmp (buf, "\\\\?\\", 4) == 0)
778             {
779               len = 4;
780               if (strncmp (buf + 4, "UNC\\", 4) == 0)
781                 len = 6;
782               if (strlen (buf) < MAX_PATH + len)
783                 {
784                   tmp += len;
785                   if (len == 6)
786                     *tmp = '\\';
787                   print_tmp = true;
788                 }
789             }
790           if (mixed_flag)
791             convert_slashes (tmp);
792         }
793     }
794
795   puts (print_tmp ? tmp : buf);
796   if (buf2)
797     free (buf2);
798   if (buf)
799     free (buf);
800 }
801
802 static void
803 print_version ()
804 {
805   const char *v = strchr (version, ':');
806   int len;
807   if (!v)
808     {
809       v = "?";
810       len = 1;
811     }
812   else
813     {
814       v += 2;
815       len = strchr (v, ' ') - v;
816     }
817   printf ("\
818 cygpath (cygwin) %.*s\n\
819 Path Conversion Utility\n\
820 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\
821           2007, 2008 Red Hat, Inc.\n\
822 Compiled on %s\n\
823 ", len, v, __DATE__);
824 }
825
826 static int
827 do_options (int argc, char **argv, int from_file)
828 {
829   int c, o = 0;
830   path_flag = 0;
831   unix_flag = 0;
832   windows_flag = 0;
833   shortname_flag = 0;
834   longname_flag = 0;
835   mixed_flag = 0;
836   ignore_flag = 0;
837   allusers_flag = 0;
838   output_flag = 0;
839   mode_flag = 0;
840   codepage = 0;
841   if (!from_file)
842     options_from_file_flag = 0;
843   optind = 0;
844   while ((c = getopt_long (argc, argv, options,
845                            long_options, (int *) NULL)) != EOF)
846     {
847       switch (c)
848         {
849         case 'a':
850           absolute_flag = 1;
851           break;
852
853         case 'c':
854           if (!optarg)
855             usage (stderr, 1);
856           CloseHandle ((HANDLE) strtoul (optarg, NULL, 16));
857           break;
858
859         case 'd':
860           windows_flag = 1;
861           shortname_flag = 1;
862           break;
863
864         case 'f':
865           if (from_file || !optarg)
866             usage (stderr, 1);
867           file_arg = optarg;
868           break;
869
870         case 'M':
871           mode_flag = 1;
872           break;
873
874         case 'o':
875           if (from_file)
876             usage (stderr, 1);
877           options_from_file_flag = 1;
878           break;
879
880         case 'p':
881           path_flag = 1;
882           break;
883
884         case 'u':
885           unix_flag = 1;
886           break;
887
888         case 'w':
889           windows_flag = 1;
890           break;
891
892          case 'm':
893           windows_flag = 1;
894           mixed_flag = 1;
895           break;
896
897         case 'l':
898           longname_flag = 1;
899           break;
900
901         case 's':
902           shortname_flag = 1;
903           break;
904
905         case 't':
906           if (!optarg)
907             usage (stderr, 1);
908
909           format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg);
910           if (strcasecmp (format_type_arg, "dos") == 0)
911             {
912               windows_flag = 1;
913               shortname_flag = 1;
914             }
915           else if (!strcasecmp (format_type_arg, "mixed"))
916             {
917               windows_flag = 1;
918               mixed_flag = 1;
919             }
920           else if (!strcasecmp (format_type_arg, "unix"))
921             unix_flag = 1;
922           else if (!strcasecmp (format_type_arg, "windows"))
923             windows_flag = 1;
924           else
925             usage (stderr, 1);
926           break;
927
928         case 'A':
929           allusers_flag = 1;
930           break;
931
932         case 'C':
933           if (!optarg)
934             usage (stderr, 1);
935           if (!strcasecmp (optarg, "ANSI"))
936             codepage = GetACP ();
937           else if (!strcasecmp (optarg, "OEM"))
938             codepage = GetOEMCP ();
939           else if (!strcasecmp (optarg, "UTF8")
940                    || !strcasecmp (optarg, "UTF-8"))
941             codepage = CP_UTF8;
942           else
943             {
944               char *c;
945               codepage = (UINT) strtoul (optarg, &c, 10);
946               if (*c)
947                 usage (stderr, 1);
948             }
949           break;
950
951         case 'D':
952         case 'H':
953         case 'O':
954         case 'P':
955         case 'S':
956         case 'W':
957           ++output_flag;
958           o = c;
959           break;
960
961         case 'F':
962           if (!optarg)
963             usage (stderr, 1);
964           ++output_flag;
965           output_arg = optarg;
966           o = c;
967           break;
968
969         case 'i':
970           ignore_flag = 1;
971           break;
972
973         case 'h':
974           usage (stdout, 0);
975           break;
976
977         case 'v':
978           print_version ();
979           exit (0);
980
981         default:
982           usage (stderr, 1);
983           break;
984         }
985     }
986
987   /* If none of the "important" flags are set, -u is default. */
988   if (!unix_flag && !windows_flag && !mode_flag
989       && (!from_file ? !options_from_file_flag : 1))
990     unix_flag = 1;
991
992   /* Only one of ... */
993   if (unix_flag + windows_flag + mode_flag > 1
994       + (!from_file ? options_from_file_flag : 0))
995     usage (stderr, 1);
996
997   /* options_from_file_flag requires a file. */
998   if (!from_file && options_from_file_flag && !file_arg)
999     usage (stderr, 1);
1000
1001   /* longname and shortname don't play well together. */
1002   if (longname_flag && shortname_flag)
1003     usage (stderr, 1);
1004
1005   /* longname and shortname only make sense with Windows paths. */
1006   if ((longname_flag || shortname_flag) && !windows_flag)
1007     usage (stderr, 1);
1008
1009   return o;
1010 }
1011
1012 static void
1013 action (int argc, char **argv, int opt)
1014 {
1015   if (output_flag)
1016     {
1017       if (argv[optind])
1018         usage (stderr, 1);
1019
1020       do_sysfolders (opt);
1021     }
1022   else
1023     {
1024       if (optind > argc - 1)
1025         usage (stderr, 1);
1026
1027       for (int i = optind; argv[i]; i++)
1028         if (mode_flag)
1029           report_mode (argv[i]);
1030         else
1031           do_pathconv (argv[i]);
1032     }
1033 }
1034
1035 int
1036 main (int argc, char **argv)
1037 {
1038   int o;
1039
1040   setlocale (LC_CTYPE, "");
1041   prog_name = strrchr (argv[0], '/');
1042   if (!prog_name)
1043     prog_name = strrchr (argv[0], '\\');
1044   if (!prog_name)
1045     prog_name = argv[0];
1046   else
1047     prog_name++;
1048
1049   o = do_options (argc, argv, 0);
1050
1051   if (!file_arg)
1052     action (argc, argv, o);
1053   else
1054     {
1055       FILE *fp;
1056       char buf[PATH_MAX * 2 + 1];
1057
1058       if (argv[optind])
1059         usage (stderr, 1);
1060
1061       if (strcmp (file_arg, "-"))
1062         {
1063           if (!(fp = fopen (file_arg, "rt")))
1064             {
1065               perror ("cygpath");
1066               exit (1);
1067             }
1068         }
1069       else
1070         {
1071           fp = stdin;
1072           setmode (0, O_TEXT);
1073         }
1074       setbuf (stdout, NULL);
1075
1076       while (fgets (buf, sizeof (buf), fp))
1077         {
1078           int ac = 0;
1079           char *av[4] = { NULL, NULL, NULL, NULL };
1080           char *p = strchr (buf, '\n');
1081           if (p)
1082             *p = '\0';
1083           p = buf;
1084           av[ac++] = prog_name;
1085           av[ac++] = p;
1086           if (options_from_file_flag && *p == '-')
1087             {
1088               while (*p && !isspace (*p))
1089                 ++p;
1090               if (*p)
1091                 {
1092                   *p++ = '\0';
1093                   while (*p && isspace (*p))
1094                     ++p;
1095                   av[ac++] = p;
1096                 }
1097               o = do_options (ac, av, 1);
1098             }
1099           else
1100             {
1101               output_flag = 0;
1102               optind = 1;
1103             }
1104           action (ac, av, o);
1105         }
1106     }
1107   exit (0);
1108 }