]>
Commit | Line | Data |
---|---|---|
e05f3227 | 1 | /* cygpath.cc -- convert pathnames between Windows and Unix format |
71148bfc | 2 | Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, |
5aa8817e | 3 | 2009, 2010, 2011, 2012, 2013, 2015 Red Hat, Inc. |
1fd5e000 CF |
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 | #include <stdio.h> | |
12 | #include <string.h> | |
26fb7ef5 CV |
13 | #include <wchar.h> |
14 | #include <locale.h> | |
1fd5e000 CF |
15 | #include <stdlib.h> |
16 | #include <limits.h> | |
17 | #include <getopt.h> | |
138d4f51 CF |
18 | #include <io.h> |
19 | #include <sys/fcntl.h> | |
1fd5e000 | 20 | #include <sys/cygwin.h> |
92b499ac | 21 | #include <cygwin/version.h> |
418068d4 | 22 | #include <ctype.h> |
e70dbe77 | 23 | #include <wctype.h> |
7c03f799 | 24 | #include <errno.h> |
9cfc9511 CV |
25 | |
26 | #define _WIN32_WINNT 0x0602 | |
27 | #define WINVER 0x0602 | |
28 | #define NOCOMATTRIBUTE | |
29 | #include <windows.h> | |
30 | #include <userenv.h> | |
31 | #include <shlobj.h> | |
32 | #include <ntdef.h> | |
33 | #include <ntdll.h> | |
34 | ||
26fb7ef5 | 35 | #include "wide_path.h" |
71d8f118 | 36 | #include "loadlib.h" |
1fd5e000 CF |
37 | |
38 | static char *prog_name; | |
ca902991 | 39 | static char *file_arg, *output_arg; |
5aa8817e | 40 | static int path_flag, unix_flag, windows_flag, absolute_flag, cygdrive_flag; |
5bb52de4 CV |
41 | static int shortname_flag, longname_flag; |
42 | static int ignore_flag, allusers_flag, output_flag; | |
fc200025 | 43 | static int mixed_flag, options_from_file_flag, mode_flag; |
0a23799a | 44 | static UINT codepage; |
fc200025 | 45 | |
1050e57c | 46 | static const char *format_type_arg; |
1fd5e000 | 47 | |
6a344609 | 48 | static struct option long_options[] = { |
6a344609 | 49 | {(char *) "absolute", no_argument, NULL, 'a'}, |
1050e57c CF |
50 | {(char *) "close", required_argument, NULL, 'c'}, |
51 | {(char *) "dos", no_argument, NULL, 'd'}, | |
52 | {(char *) "file", required_argument, NULL, 'f'}, | |
53 | {(char *) "help", no_argument, NULL, 'h'}, | |
54 | {(char *) "ignore", no_argument, NULL, 'i'}, | |
55 | {(char *) "long-name", no_argument, NULL, 'l'}, | |
56 | {(char *) "mixed", no_argument, NULL, 'm'}, | |
f135dd3e | 57 | {(char *) "mode", no_argument, NULL, 'M'}, |
6a344609 CF |
58 | {(char *) "option", no_argument, NULL, 'o'}, |
59 | {(char *) "path", no_argument, NULL, 'p'}, | |
5aa8817e | 60 | {(char *) "proc-cygdrive", no_argument, NULL, 'U'}, |
1050e57c CF |
61 | {(char *) "short-name", no_argument, NULL, 's'}, |
62 | {(char *) "type", required_argument, NULL, 't'}, | |
6a344609 | 63 | {(char *) "unix", no_argument, NULL, 'u'}, |
92b499ac | 64 | {(char *) "version", no_argument, NULL, 'V'}, |
6a344609 | 65 | {(char *) "windows", no_argument, NULL, 'w'}, |
6a344609 CF |
66 | {(char *) "allusers", no_argument, NULL, 'A'}, |
67 | {(char *) "desktop", no_argument, NULL, 'D'}, | |
e355de81 | 68 | {(char *) "homeroot", no_argument, NULL, 'H'}, |
ca902991 | 69 | {(char *) "mydocs", no_argument, NULL, 'O'}, |
1050e57c CF |
70 | {(char *) "smprograms", no_argument, NULL, 'P'}, |
71 | {(char *) "sysdir", no_argument, NULL, 'S'}, | |
72 | {(char *) "windir", no_argument, NULL, 'W'}, | |
ca902991 | 73 | {(char *) "folder", required_argument, NULL, 'F'}, |
0a23799a | 74 | {(char *) "codepage", required_argument, NULL, 'C'}, |
6a344609 | 75 | {0, no_argument, 0, 0} |
1fd5e000 CF |
76 | }; |
77 | ||
5aa8817e | 78 | static char options[] = "ac:df:hilmMopst:uUVwAC:DHOPSWF:"; |
9b566b96 | 79 | |
1fd5e000 | 80 | static void |
6a344609 | 81 | usage (FILE * stream, int status) |
1fd5e000 | 82 | { |
45b80bb4 CF |
83 | if (!ignore_flag || !status) |
84 | fprintf (stream, "\ | |
92b499ac CV |
85 | Usage: %1$s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\ |
86 | %1$s [-c HANDLE] \n\ | |
87 | %1$s [-ADHOPSW] \n\ | |
88 | %1$s [-F ID] \n\ | |
89 | \n\ | |
aa275fe0 | 90 | Convert Unix and Windows format paths, or output system path information\n\ |
bd79b736 | 91 | \n\ |
1050e57c | 92 | Output type options:\n\ |
92b499ac | 93 | \n\ |
ca902991 | 94 | -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\ |
1050e57c | 95 | -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\ |
ca902991 CV |
96 | -M, --mode report on mode of file (binmode or textmode)\n\ |
97 | -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\ | |
4c634492 | 98 | -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\ |
1050e57c | 99 | -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\ |
92b499ac | 100 | \n\ |
33bd2d12 | 101 | Path conversion options:\n\ |
92b499ac | 102 | \n\ |
1050e57c | 103 | -a, --absolute output absolute path\n\ |
ca902991 CV |
104 | -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\ |
105 | -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\ | |
5aa8817e CV |
106 | -U, --proc-cygdrive Emit /proc/cygdrive path instead of cygdrive prefix\n\ |
107 | when converting Windows path to UNIX path.\n\ | |
ca902991 | 108 | -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\ |
0a23799a CV |
109 | -C, --codepage CP print DOS, Windows, or mixed pathname in Windows\n\ |
110 | codepage CP. CP can be a numeric codepage identifier,\n\ | |
111 | or one of the reserved words ANSI, OEM, or UTF8.\n\ | |
92b499ac | 112 | If this option is missing, %1$s defaults to the\n\ |
0a23799a | 113 | character set defined by the current locale.\n\ |
92b499ac | 114 | \n\ |
1050e57c | 115 | System information:\n\ |
92b499ac | 116 | \n\ |
ca902991 CV |
117 | -A, --allusers use `All Users' instead of current user for -D, -O, -P\n\ |
118 | -D, --desktop output `Desktop' directory and exit\n\ | |
1050e57c | 119 | -H, --homeroot output `Profiles' directory (home root) and exit\n\ |
ca902991 CV |
120 | -O, --mydocs output `My Documents' directory and exit\n\ |
121 | -P, --smprograms output Start Menu `Programs' directory and exit\n\ | |
122 | -S, --sysdir output system directory and exit\n\ | |
123 | -W, --windir output `Windows' directory and exit\n\ | |
124 | -F, --folder ID output special folder with numeric ID and exit\n\ | |
92b499ac | 125 | ", prog_name); |
befdf18b CF |
126 | if (ignore_flag) |
127 | /* nothing to do */; | |
128 | else if (stream != stdout) | |
129 | fprintf(stream, "Try `%s --help' for more information.\n", prog_name); | |
130 | else | |
1050e57c | 131 | { |
befdf18b | 132 | fprintf (stream, "\ |
92b499ac | 133 | \n\ |
33bd2d12 | 134 | Other options:\n\ |
92b499ac | 135 | \n\ |
1050e57c CF |
136 | -f, --file FILE read FILE for input; use - to read from STDIN\n\ |
137 | -o, --option read options from FILE as well (for use with --file)\n\ | |
138 | -c, --close HANDLE close HANDLE (for use in captured process)\n\ | |
ca902991 | 139 | -i, --ignore ignore missing argument\n\ |
1050e57c | 140 | -h, --help output usage information and exit\n\ |
92b499ac CV |
141 | -V, --version output version information and exit\n\ |
142 | \n"); | |
1050e57c | 143 | } |
45b80bb4 CF |
144 | exit (ignore_flag ? 0 : status); |
145 | } | |
146 | ||
2dba45f4 CV |
147 | static inline BOOLEAN |
148 | RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size) | |
149 | { | |
150 | uni->Length = 0; | |
d2cc418e | 151 | uni->MaximumLength = size / sizeof (WCHAR); |
2dba45f4 CV |
152 | uni->Buffer = (WCHAR *) malloc (size); |
153 | return uni->Buffer != NULL; | |
154 | } | |
155 | ||
0a23799a CV |
156 | static size_t |
157 | my_wcstombs (char *dest, const wchar_t *src, size_t n) | |
158 | { | |
159 | if (codepage) | |
160 | return WideCharToMultiByte (codepage, 0, src, -1, dest, n, NULL, NULL); | |
161 | else | |
162 | return wcstombs (dest, src, n); | |
163 | } | |
164 | ||
d2cc418e CV |
165 | #define HARDDISK_PREFIX L"\\Device\\Harddisk" |
166 | #define GLOBALROOT_PREFIX "\\\\.\\GLOBALROOT" | |
167 | ||
2dba45f4 CV |
168 | static char * |
169 | get_device_name (char *path) | |
170 | { | |
171 | UNICODE_STRING ntdev, tgtdev, ntdevdir; | |
172 | ANSI_STRING ans; | |
173 | OBJECT_ATTRIBUTES ntobj; | |
174 | NTSTATUS status; | |
175 | HANDLE lnk, dir; | |
d2cc418e | 176 | bool got_one = false; |
2dba45f4 CV |
177 | char *ret = strdup (path); |
178 | PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION) | |
179 | alloca (4096); | |
180 | BOOLEAN restart; | |
181 | ULONG cont; | |
182 | ||
d2cc418e CV |
183 | if (!strncasecmp (path, GLOBALROOT_PREFIX "\\", sizeof (GLOBALROOT_PREFIX))) |
184 | path += sizeof (GLOBALROOT_PREFIX) - 1; | |
2dba45f4 CV |
185 | if (strncasecmp (path, "\\Device\\", 8)) |
186 | return ret; | |
187 | ||
d2cc418e | 188 | if (!RtlAllocateUnicodeString (&ntdev, 65534)) |
2dba45f4 | 189 | return ret; |
d2cc418e | 190 | if (!RtlAllocateUnicodeString (&tgtdev, 65534)) |
2dba45f4 CV |
191 | return ret; |
192 | RtlInitAnsiString (&ans, path); | |
193 | RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE); | |
194 | ||
195 | /* First check if the given device name is a symbolic link itself. If so, | |
196 | query it and use the new name as actual device name to search for in the | |
197 | DOS device name directory. If not, just use the incoming device name. */ | |
198 | InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
9cfc9511 | 199 | status = NtOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); |
2dba45f4 CV |
200 | if (NT_SUCCESS (status)) |
201 | { | |
9cfc9511 CV |
202 | status = NtQuerySymbolicLinkObject (lnk, &tgtdev, NULL); |
203 | NtClose (lnk); | |
2dba45f4 CV |
204 | if (!NT_SUCCESS (status)) |
205 | goto out; | |
206 | RtlCopyUnicodeString (&ntdev, &tgtdev); | |
207 | } | |
d2cc418e CV |
208 | else if (status != STATUS_OBJECT_TYPE_MISMATCH |
209 | && status != STATUS_OBJECT_PATH_SYNTAX_BAD) | |
2dba45f4 CV |
210 | goto out; |
211 | ||
212 | for (int i = 0; i < 2; ++i) | |
213 | { | |
214 | /* There are two DOS device directories, the local and the global dir. | |
215 | Try both, local first. */ | |
216 | RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??"); | |
217 | ||
218 | /* Open the directory... */ | |
219 | InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE, | |
220 | NULL, NULL); | |
9cfc9511 | 221 | status = NtOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj); |
2dba45f4 CV |
222 | if (!NT_SUCCESS (status)) |
223 | break; | |
224 | ||
225 | /* ...and scan it. */ | |
226 | for (restart = TRUE, cont = 0; | |
9cfc9511 | 227 | NT_SUCCESS (NtQueryDirectoryObject (dir, odi, 4096, TRUE, |
2dba45f4 CV |
228 | restart, &cont, NULL)); |
229 | restart = FALSE) | |
230 | { | |
231 | /* For each entry check if it's a symbolic link. */ | |
232 | InitializeObjectAttributes (&ntobj, &odi->ObjectName, | |
233 | OBJ_CASE_INSENSITIVE, dir, NULL); | |
9cfc9511 | 234 | status = NtOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); |
2dba45f4 CV |
235 | if (!NT_SUCCESS (status)) |
236 | continue; | |
237 | tgtdev.Length = 0; | |
238 | tgtdev.MaximumLength = 512; | |
239 | /* If so, query it and compare the target of the symlink with the | |
240 | incoming device name. */ | |
9cfc9511 CV |
241 | status = NtQuerySymbolicLinkObject (lnk, &tgtdev, NULL); |
242 | NtClose (lnk); | |
2dba45f4 CV |
243 | if (!NT_SUCCESS (status)) |
244 | continue; | |
d2cc418e CV |
245 | if (tgtdev.Length /* There's actually a symlink pointing to an |
246 | empty string: \??\GLOBALROOT -> "" */ | |
247 | && RtlEqualUnicodePathPrefix (&ntdev, &tgtdev, TRUE)) | |
2dba45f4 CV |
248 | { |
249 | /* If the comparison succeeds, the name of the directory entry is | |
250 | a valid DOS device name, if prepended with "\\.\". Return that | |
251 | valid DOS path. */ | |
d2cc418e CV |
252 | wchar_t *trailing = NULL; |
253 | if (ntdev.Length > tgtdev.Length) | |
254 | trailing = ntdev.Buffer + tgtdev.Length / sizeof (WCHAR); | |
2dba45f4 | 255 | ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName); |
d2cc418e CV |
256 | if (trailing) |
257 | len += my_wcstombs (NULL, trailing, 0); | |
bf4b8020 | 258 | free (ret); |
2dba45f4 CV |
259 | ret = (char *) malloc (len + 4); |
260 | strcpy (ret, "\\\\.\\"); | |
261 | ans.Length = 0; | |
262 | ans.MaximumLength = len; | |
263 | ans.Buffer = ret + 4; | |
264 | RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE); | |
d2cc418e CV |
265 | if (trailing) |
266 | my_wcstombs (ans.Buffer + ans.Length, trailing, | |
267 | ans.MaximumLength - ans.Length); | |
268 | ans.Buffer[ans.MaximumLength - 1] = '\0'; | |
269 | got_one = true; | |
708bbfd0 CV |
270 | /* Special case for local disks: It's most feasible if the |
271 | DOS device name reflects the DOS drive, so we check for this | |
272 | explicitly and only return prematurely if so. */ | |
708bbfd0 CV |
273 | if (ntdev.Length < wcslen (HARDDISK_PREFIX) |
274 | || wcsncasecmp (ntdev.Buffer, HARDDISK_PREFIX, 8) != 0 | |
275 | || (odi->ObjectName.Length == 2 * sizeof (WCHAR) | |
276 | && odi->ObjectName.Buffer[1] == L':')) | |
277 | { | |
d2cc418e CV |
278 | if (trailing) |
279 | { | |
280 | /* If there's a trailing path, it's a perfectly valid | |
281 | DOS pathname without the \\.\ prefix. Unless it's | |
1b23b30b | 282 | longer than MAX_PATH - 1 in which case it needs |
d2cc418e | 283 | the \\?\ prefix. */ |
ac5d7dcd | 284 | if ((len = strlen (ret + 4)) >= MAX_PATH) |
d2cc418e CV |
285 | ret[2] = '?'; |
286 | else | |
287 | memmove (ret, ret + 4, strlen (ret + 4) + 1); | |
288 | } | |
9cfc9511 | 289 | NtClose (dir); |
708bbfd0 CV |
290 | goto out; |
291 | } | |
2dba45f4 CV |
292 | } |
293 | } | |
9cfc9511 | 294 | NtClose (dir); |
2dba45f4 CV |
295 | } |
296 | ||
297 | out: | |
298 | free (tgtdev.Buffer); | |
299 | free (ntdev.Buffer); | |
d2cc418e CV |
300 | if (!got_one) |
301 | { | |
302 | free (ret); | |
303 | ret = (char *) malloc (sizeof (GLOBALROOT_PREFIX) + strlen (path)); | |
304 | if (ret) | |
305 | stpcpy (stpcpy (ret, GLOBALROOT_PREFIX), path); | |
306 | } | |
2dba45f4 CV |
307 | return ret; |
308 | } | |
309 | ||
310 | static char * | |
311 | get_device_paths (char *path) | |
312 | { | |
313 | char *sbuf; | |
314 | char *ptr; | |
315 | int n = 1; | |
316 | ||
317 | ptr = path; | |
318 | while ((ptr = strchr (ptr, ';'))) | |
319 | { | |
320 | ptr++; | |
321 | n++; | |
322 | } | |
323 | ||
324 | char *paths[n]; | |
325 | DWORD acc = 0; | |
326 | int i; | |
327 | if (!n) | |
328 | return strdup (""); | |
329 | ||
330 | for (i = 0, ptr = path; ptr; i++) | |
331 | { | |
332 | char *next = ptr; | |
333 | ptr = strchr (ptr, ';'); | |
334 | if (ptr) | |
335 | *ptr++ = 0; | |
336 | paths[i] = get_device_name (next); | |
337 | acc += strlen (paths[i]) + 1; | |
338 | } | |
339 | ||
340 | sbuf = (char *) malloc (acc + 1); | |
341 | if (sbuf == NULL) | |
342 | { | |
343 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
344 | exit (1); | |
345 | } | |
346 | ||
347 | sbuf[0] = '\0'; | |
348 | for (i = 0; i < n; i++) | |
349 | { | |
350 | strcat (strcat (sbuf, paths[i]), ";"); | |
351 | free (paths[i]); | |
352 | } | |
353 | ||
354 | strchr (sbuf, '\0')[-1] = '\0'; | |
355 | return sbuf; | |
356 | } | |
357 | ||
45b80bb4 CF |
358 | static char * |
359 | get_short_paths (char *path) | |
360 | { | |
26fb7ef5 CV |
361 | wchar_t *sbuf; |
362 | wchar_t *sptr; | |
45b80bb4 CF |
363 | char *next; |
364 | char *ptr = path; | |
365 | char *end = strrchr (path, 0); | |
366 | DWORD acc = 0; | |
367 | DWORD len; | |
368 | ||
369 | while (ptr != NULL) | |
45b80bb4 | 370 | { |
6a344609 CF |
371 | next = ptr; |
372 | ptr = strchr (ptr, ';'); | |
373 | if (ptr) | |
374 | *ptr++ = 0; | |
26fb7ef5 CV |
375 | wide_path wpath (next); |
376 | len = GetShortPathNameW (wpath, NULL, 0); | |
cc3ce0bb | 377 | if (!len) |
6a344609 | 378 | { |
92b499ac CV |
379 | fprintf (stderr, "%s: cannot create short name of %s\n", |
380 | prog_name, next); | |
6a344609 CF |
381 | exit (2); |
382 | } | |
383 | acc += len + 1; | |
45b80bb4 | 384 | } |
26fb7ef5 | 385 | sptr = sbuf = (wchar_t *) malloc ((acc + 1) * sizeof (wchar_t)); |
45b80bb4 | 386 | if (sbuf == NULL) |
45b80bb4 | 387 | { |
6a344609 CF |
388 | fprintf (stderr, "%s: out of memory\n", prog_name); |
389 | exit (1); | |
45b80bb4 | 390 | } |
6a344609 CF |
391 | ptr = path; |
392 | for (;;) | |
393 | { | |
26fb7ef5 CV |
394 | wide_path wpath (ptr); |
395 | len = GetShortPathNameW (wpath, sptr, acc); | |
cc3ce0bb | 396 | if (!len) |
6a344609 | 397 | { |
92b499ac CV |
398 | fprintf (stderr, "%s: cannot create short name of %s\n", |
399 | prog_name, ptr); | |
6a344609 CF |
400 | exit (2); |
401 | } | |
45b80bb4 | 402 | |
6a344609 | 403 | ptr = strrchr (ptr, 0); |
26fb7ef5 | 404 | sptr = wcsrchr (sptr, 0); |
6a344609 CF |
405 | if (ptr == end) |
406 | break; | |
26fb7ef5 | 407 | *sptr = L';'; |
6a344609 | 408 | ++ptr, ++sptr; |
5bb52de4 | 409 | acc -= len + 1; |
6a344609 | 410 | } |
0a23799a | 411 | len = my_wcstombs (NULL, sbuf, 0) + 1; |
26fb7ef5 CV |
412 | ptr = (char *) malloc (len); |
413 | if (ptr == NULL) | |
414 | { | |
415 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
416 | exit (1); | |
417 | } | |
0a23799a | 418 | my_wcstombs (ptr, sbuf, len); |
26fb7ef5 | 419 | return ptr; |
45b80bb4 CF |
420 | } |
421 | ||
422 | static char * | |
423 | get_short_name (const char *filename) | |
424 | { | |
26fb7ef5 CV |
425 | wchar_t buf[32768]; |
426 | char *sbuf; | |
427 | wide_path wpath (filename); | |
428 | DWORD len = GetShortPathNameW (wpath, buf, 32768); | |
cc3ce0bb | 429 | if (!len) |
6a344609 | 430 | { |
92b499ac CV |
431 | fprintf (stderr, "%s: cannot create short name of %s\n", |
432 | prog_name, filename); | |
6a344609 CF |
433 | exit (2); |
434 | } | |
0a23799a | 435 | len = my_wcstombs (NULL, buf, 0) + 1; |
26fb7ef5 | 436 | sbuf = (char *) malloc (len); |
45b80bb4 | 437 | if (sbuf == NULL) |
6a344609 CF |
438 | { |
439 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
440 | exit (1); | |
441 | } | |
0a23799a | 442 | my_wcstombs (sbuf, buf, len); |
26fb7ef5 | 443 | return sbuf; |
5bb52de4 CV |
444 | } |
445 | ||
5bb52de4 | 446 | static char * |
cf157504 | 447 | get_long_name (const char *filename, DWORD& len) |
5bb52de4 | 448 | { |
26fb7ef5 CV |
449 | char *sbuf; |
450 | wchar_t buf[32768]; | |
26fb7ef5 | 451 | wide_path wpath (filename); |
acc31d1a | 452 | |
71148bfc CV |
453 | if (!GetLongPathNameW (wpath, buf, 32768)) |
454 | wcscpy (buf, wpath); | |
0a23799a | 455 | len = my_wcstombs (NULL, buf, 0); |
cf157504 CF |
456 | sbuf = (char *) malloc (len + 1); |
457 | if (!sbuf) | |
5bb52de4 CV |
458 | { |
459 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
460 | exit (1); | |
461 | } | |
0a23799a | 462 | my_wcstombs (sbuf, buf, len + 1); |
26fb7ef5 | 463 | return sbuf; |
5bb52de4 CV |
464 | } |
465 | ||
466 | static char * | |
cf157504 | 467 | get_long_paths (char *path) |
5bb52de4 | 468 | { |
cf157504 CF |
469 | char *sbuf; |
470 | char *ptr; | |
471 | int n = 1; | |
472 | ||
473 | ptr = path; | |
474 | while ((ptr = strchr (ptr, ';'))) | |
6a344609 | 475 | { |
cf157504 CF |
476 | ptr++; |
477 | n++; | |
6a344609 | 478 | } |
cf157504 CF |
479 | |
480 | char *paths[n]; | |
481 | DWORD acc = 0; | |
482 | int i; | |
483 | if (!n) | |
484 | return strdup (""); | |
485 | ||
486 | for (i = 0, ptr = path; ptr; i++) | |
487 | { | |
488 | DWORD len; | |
489 | char *next = ptr; | |
490 | ptr = strchr (ptr, ';'); | |
491 | if (ptr) | |
492 | *ptr++ = 0; | |
493 | paths[i] = get_long_name (next, len); | |
494 | acc += len + 1; | |
495 | } | |
496 | ||
497 | sbuf = (char *) malloc (acc + 1); | |
5bb52de4 CV |
498 | if (sbuf == NULL) |
499 | { | |
500 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
501 | exit (1); | |
502 | } | |
cf157504 CF |
503 | |
504 | sbuf[0] = '\0'; | |
505 | for (i = 0; i < n; i++) | |
506 | { | |
507 | strcat (strcat (sbuf, paths[i]), ";"); | |
508 | free (paths[i]); | |
509 | } | |
510 | ||
511 | strchr (sbuf, '\0')[-1] = '\0'; | |
512 | return sbuf; | |
5bb52de4 CV |
513 | } |
514 | ||
33bd2d12 CF |
515 | static void |
516 | convert_slashes (char* name) | |
517 | { | |
518 | while ((name = strchr (name, '\\')) != NULL) | |
519 | { | |
520 | if (*name == '\\') | |
521 | *name = '/'; | |
522 | name++; | |
523 | } | |
524 | } | |
525 | ||
ca902991 | 526 | static bool |
71148bfc | 527 | get_special_folder (PWCHAR wpath, int id) |
ca902991 | 528 | { |
ca902991 CV |
529 | LPITEMIDLIST pidl = 0; |
530 | if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK) | |
531 | return false; | |
175e39bb | 532 | if (!SHGetPathFromIDListW (pidl, wpath) || !wpath[0]) |
ca902991 CV |
533 | return false; |
534 | return true; | |
535 | } | |
536 | ||
5bb52de4 | 537 | static void |
fc200025 | 538 | do_sysfolders (char option) |
5bb52de4 | 539 | { |
175e39bb | 540 | WCHAR wbuf[MAX_PATH]; |
71148bfc | 541 | char buf[PATH_MAX]; |
79b1b77b | 542 | BOOL iswow64 = FALSE; |
cf157504 | 543 | |
71148bfc | 544 | wbuf[0] = L'\0'; |
5bb52de4 CV |
545 | switch (option) |
546 | { | |
547 | case 'D': | |
71148bfc | 548 | get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY |
175e39bb | 549 | : CSIDL_DESKTOPDIRECTORY); |
5bb52de4 CV |
550 | break; |
551 | ||
552 | case 'P': | |
71148bfc | 553 | get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_PROGRAMS |
175e39bb | 554 | : CSIDL_PROGRAMS); |
ca902991 CV |
555 | break; |
556 | ||
557 | case 'O': | |
71148bfc | 558 | get_special_folder (wbuf, allusers_flag ? CSIDL_COMMON_DOCUMENTS |
175e39bb | 559 | : CSIDL_PERSONAL); |
ca902991 CV |
560 | break; |
561 | ||
562 | case 'F': | |
563 | { | |
564 | int val = -1, len = -1; | |
565 | if (!(sscanf (output_arg, "%i%n", &val, &len) == 1 | |
566 | && len == (int) strlen (output_arg) && val >= 0)) | |
567 | { | |
568 | fprintf (stderr, "%s: syntax error in special folder ID %s\n", | |
569 | prog_name, output_arg); | |
570 | exit (1); | |
571 | } | |
71148bfc | 572 | get_special_folder (wbuf, val); |
ca902991 | 573 | } |
5bb52de4 CV |
574 | break; |
575 | ||
576 | case 'H': | |
71148bfc CV |
577 | { |
578 | DWORD len = MAX_PATH; | |
579 | GetProfilesDirectoryW (wbuf, &len); | |
580 | } | |
5bb52de4 CV |
581 | break; |
582 | ||
583 | case 'S': | |
e70dbe77 | 584 | GetSystemDirectoryW (wbuf, MAX_PATH); |
79b1b77b CV |
585 | if (!windows_flag |
586 | && IsWow64Process (GetCurrentProcess (), &iswow64) && iswow64) | |
587 | { | |
588 | /* When calling NtQueryInformationFile(FileNameInformation) on WOW64, | |
589 | the returned path will point to SysWOW64. This breaks path | |
590 | redirection to the network related files under device/etc. This | |
591 | here is a bad hack to make sure that the conversion will convert | |
592 | the case *and* stick to System32. */ | |
593 | PWCHAR last_bs = wcsrchr (wbuf, L'\\'); | |
594 | if (last_bs) | |
595 | wcpcpy (last_bs + 1, L"Sysnative"); | |
596 | } | |
5bb52de4 CV |
597 | break; |
598 | ||
599 | case 'W': | |
71148bfc | 600 | GetSystemWindowsDirectoryW (wbuf, MAX_PATH); |
5bb52de4 CV |
601 | break; |
602 | ||
603 | default: | |
604 | usage (stderr, 1); | |
605 | } | |
606 | ||
71148bfc | 607 | if (!wbuf[0]) |
ca902991 | 608 | { |
92b499ac CV |
609 | fprintf (stderr, "%s: failed to retrieve special folder path\n", |
610 | prog_name); | |
e70dbe77 | 611 | return; |
ca902991 CV |
612 | } |
613 | else if (!windows_flag) | |
5bb52de4 | 614 | { |
e70dbe77 CV |
615 | /* The system folders are not necessarily case-correct. To allow |
616 | case-sensitivity, try to correct the case. Note that this only | |
617 | works for local filesystems. */ | |
618 | if (iswalpha (wbuf[0]) && wbuf[1] == L':' && wbuf[2] == L'\\') | |
619 | { | |
620 | OBJECT_ATTRIBUTES attr; | |
621 | NTSTATUS status; | |
622 | HANDLE h; | |
623 | IO_STATUS_BLOCK io; | |
624 | UNICODE_STRING upath; | |
625 | const ULONG size = sizeof (FILE_NAME_INFORMATION) | |
626 | + PATH_MAX * sizeof (WCHAR); | |
627 | PFILE_NAME_INFORMATION pfni = (PFILE_NAME_INFORMATION) alloca (size); | |
628 | ||
629 | /* Avoid another buffer, reuse pfni. */ | |
630 | wcpcpy (wcpcpy (pfni->FileName, L"\\??\\"), wbuf); | |
631 | RtlInitUnicodeString (&upath, pfni->FileName); | |
632 | InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, | |
633 | NULL, NULL); | |
634 | status = NtOpenFile (&h, READ_CONTROL, &attr, &io, | |
635 | FILE_SHARE_VALID_FLAGS, FILE_OPEN_REPARSE_POINT); | |
636 | if (NT_SUCCESS (status)) | |
637 | { | |
638 | status = NtQueryInformationFile (h, &io, pfni, size, | |
639 | FileNameInformation); | |
640 | if (NT_SUCCESS (status)) | |
641 | { | |
642 | pfni->FileName[pfni->FileNameLength / sizeof (WCHAR)] = L'\0'; | |
643 | wcscpy (wbuf + 2, pfni->FileName); | |
644 | } | |
645 | NtClose (h); | |
646 | } | |
647 | } | |
5aa8817e CV |
648 | if (cygwin_conv_path (CCP_WIN_W_TO_POSIX | cygdrive_flag, |
649 | wbuf, buf, PATH_MAX)) | |
71148bfc CV |
650 | fprintf (stderr, "%s: error converting \"%ls\" - %s\n", |
651 | prog_name, wbuf, strerror (errno)); | |
5bb52de4 CV |
652 | } |
653 | else | |
654 | { | |
655 | if (shortname_flag) | |
71148bfc CV |
656 | /* System paths are never longer than MAX_PATH. The buffer pointers |
657 | in a call to GetShortPathNameW may point to the same buffer. */ | |
658 | GetShortPathNameW (wbuf, wbuf, MAX_PATH); | |
659 | my_wcstombs (buf, wbuf, MAX_PATH); | |
33bd2d12 | 660 | if (mixed_flag) |
1b526b48 | 661 | convert_slashes (buf); |
5bb52de4 CV |
662 | } |
663 | printf ("%s\n", buf); | |
1fd5e000 CF |
664 | } |
665 | ||
f135dd3e CF |
666 | static void |
667 | report_mode (char *filename) | |
668 | { | |
669 | switch (cygwin_internal (CW_GET_BINMODE, filename)) | |
670 | { | |
671 | case O_BINARY: | |
672 | printf ("%s: binary\n", filename); | |
673 | break; | |
674 | case O_TEXT: | |
675 | printf ("%s: text\n", filename); | |
676 | break; | |
677 | default: | |
92b499ac CV |
678 | fprintf (stderr, "%s: file '%s' - %s\n", prog_name, |
679 | filename, strerror (errno)); | |
f135dd3e CF |
680 | break; |
681 | } | |
682 | } | |
683 | ||
138d4f51 | 684 | static void |
fc200025 | 685 | do_pathconv (char *filename) |
138d4f51 | 686 | { |
bf4b8020 CV |
687 | char *buf = NULL, *tmp; |
688 | wchar_t *buf2 = NULL; | |
71148bfc | 689 | DWORD len = 32768; |
2b2b42cf | 690 | ssize_t err; |
80206d7f | 691 | bool print_tmp = false; |
2b2b42cf | 692 | cygwin_conv_path_t conv_func = |
71148bfc | 693 | (unix_flag ? CCP_WIN_W_TO_POSIX : CCP_POSIX_TO_WIN_W) |
5aa8817e | 694 | | absolute_flag | cygdrive_flag; |
138d4f51 | 695 | |
71148bfc | 696 | if (!filename || !filename[0]) |
c02e32c9 | 697 | { |
71148bfc CV |
698 | if (ignore_flag) |
699 | return; | |
700 | fprintf (stderr, "%s: can't convert empty path\n", prog_name); | |
701 | exit (1); | |
c02e32c9 | 702 | } |
138d4f51 CF |
703 | |
704 | buf = (char *) malloc (len); | |
71148bfc | 705 | if (!unix_flag) |
26fb7ef5 | 706 | buf2 = (wchar_t *) malloc (len * sizeof (wchar_t)); |
138d4f51 CF |
707 | if (buf == NULL) |
708 | { | |
709 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
710 | exit (1); | |
711 | } | |
712 | ||
713 | if (path_flag) | |
714 | { | |
71148bfc CV |
715 | if (unix_flag) |
716 | { | |
733b554b | 717 | wide_path wpath (filename, false); |
71148bfc CV |
718 | err = cygwin_conv_path_list (conv_func, wpath, buf, len); |
719 | } | |
720 | else | |
721 | err = cygwin_conv_path_list (conv_func, filename, buf2, len); | |
722 | if (err) | |
723 | { | |
724 | fprintf (stderr, "%s: error converting \"%s\" - %s\n", | |
725 | prog_name, filename, strerror (errno)); | |
726 | exit (1); | |
727 | } | |
2b2b42cf | 728 | if (!unix_flag) |
6a344609 | 729 | { |
71148bfc | 730 | my_wcstombs (buf, buf2, 32768); |
bf4b8020 CV |
731 | buf = get_device_paths (tmp = buf); |
732 | free (tmp); | |
6a344609 | 733 | if (shortname_flag) |
bf4b8020 CV |
734 | { |
735 | buf = get_short_paths (tmp = buf); | |
736 | free (tmp); | |
737 | } | |
5bb52de4 | 738 | if (longname_flag) |
bf4b8020 CV |
739 | { |
740 | buf = get_long_paths (tmp = buf); | |
741 | free (tmp); | |
742 | } | |
2bd6505b | 743 | if (mixed_flag) |
1b526b48 | 744 | convert_slashes (buf); |
6a344609 | 745 | } |
138d4f51 CF |
746 | } |
747 | else | |
748 | { | |
71148bfc CV |
749 | if (unix_flag) |
750 | { | |
751 | wide_path wpath (filename); | |
752 | err = cygwin_conv_path (conv_func, wpath, (void *) buf, len); | |
753 | } | |
754 | else | |
755 | err = cygwin_conv_path (conv_func, filename, (void *) buf2, len); | |
7c03f799 | 756 | if (err) |
6a344609 | 757 | { |
7c03f799 CF |
758 | fprintf (stderr, "%s: error converting \"%s\" - %s\n", |
759 | prog_name, filename, strerror (errno)); | |
6a344609 CF |
760 | exit (1); |
761 | } | |
5bb52de4 CV |
762 | if (!unix_flag) |
763 | { | |
0a23799a | 764 | my_wcstombs (buf, buf2, 32768); |
bf4b8020 CV |
765 | buf = get_device_name (tmp = buf); |
766 | free (tmp); | |
7ca68b7e | 767 | if (shortname_flag) |
bf4b8020 CV |
768 | { |
769 | buf = get_short_name (tmp = buf); | |
770 | free (tmp); | |
771 | } | |
7ca68b7e | 772 | if (longname_flag) |
bf4b8020 CV |
773 | { |
774 | buf = get_long_name (tmp = buf, len); | |
775 | free (tmp); | |
776 | } | |
bf4b8020 | 777 | tmp = buf; |
8c3a79bb | 778 | if (strncmp (buf, "\\\\?\\", 4) == 0) |
26fb7ef5 | 779 | { |
d2cc418e CV |
780 | len = 0; |
781 | if (buf[5] == ':') | |
782 | len = 4; | |
783 | else if (!strncmp (buf + 4, "UNC\\", 4)) | |
8c3a79bb | 784 | len = 6; |
d2cc418e | 785 | if (len && strlen (buf) < MAX_PATH + len) |
8c3a79bb | 786 | { |
03ac74c1 | 787 | tmp += len; |
8c3a79bb | 788 | if (len == 6) |
03ac74c1 | 789 | *tmp = '\\'; |
80206d7f | 790 | print_tmp = true; |
8c3a79bb | 791 | } |
26fb7ef5 | 792 | } |
8c3a79bb | 793 | if (mixed_flag) |
03ac74c1 | 794 | convert_slashes (tmp); |
5bb52de4 | 795 | } |
138d4f51 CF |
796 | } |
797 | ||
80206d7f | 798 | puts (print_tmp ? tmp : buf); |
bf4b8020 CV |
799 | if (buf2) |
800 | free (buf2); | |
801 | if (buf) | |
03ac74c1 | 802 | free (buf); |
138d4f51 CF |
803 | } |
804 | ||
6a344609 CF |
805 | static void |
806 | print_version () | |
807 | { | |
92b499ac CV |
808 | printf ("cygpath (cygwin) %d.%d.%d\n" |
809 | "Path Conversion Utility\n" | |
810 | "Copyright (C) 1998 - %s Red Hat, Inc.\n" | |
811 | "This is free software; see the source for copying conditions. There is NO\n" | |
812 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", | |
813 | CYGWIN_VERSION_DLL_MAJOR / 1000, | |
814 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
815 | CYGWIN_VERSION_DLL_MINOR, | |
816 | strrchr (__DATE__, ' ') + 1); | |
6a344609 CF |
817 | } |
818 | ||
fc200025 CV |
819 | static int |
820 | do_options (int argc, char **argv, int from_file) | |
1fd5e000 | 821 | { |
6a344609 | 822 | int c, o = 0; |
1fd5e000 | 823 | path_flag = 0; |
fc200025 | 824 | unix_flag = 0; |
1fd5e000 | 825 | windows_flag = 0; |
45b80bb4 | 826 | shortname_flag = 0; |
5bb52de4 | 827 | longname_flag = 0; |
2bd6505b | 828 | mixed_flag = 0; |
45b80bb4 | 829 | ignore_flag = 0; |
3cdacffc CV |
830 | allusers_flag = 0; |
831 | output_flag = 0; | |
f135dd3e | 832 | mode_flag = 0; |
0a23799a | 833 | codepage = 0; |
5aa8817e CV |
834 | cygdrive_flag = 0; |
835 | absolute_flag = CCP_RELATIVE; | |
fc200025 CV |
836 | if (!from_file) |
837 | options_from_file_flag = 0; | |
838 | optind = 0; | |
9b566b96 | 839 | while ((c = getopt_long (argc, argv, options, |
2bd6505b | 840 | long_options, (int *) NULL)) != EOF) |
1fd5e000 CF |
841 | { |
842 | switch (c) | |
843 | { | |
418068d4 | 844 | case 'a': |
5aa8817e | 845 | absolute_flag = CCP_ABSOLUTE; |
418068d4 CF |
846 | break; |
847 | ||
848 | case 'c': | |
fc200025 CV |
849 | if (!optarg) |
850 | usage (stderr, 1); | |
418068d4 CF |
851 | CloseHandle ((HANDLE) strtoul (optarg, NULL, 16)); |
852 | break; | |
853 | ||
1050e57c | 854 | case 'd': |
1050e57c CF |
855 | windows_flag = 1; |
856 | shortname_flag = 1; | |
857 | break; | |
858 | ||
138d4f51 | 859 | case 'f': |
fc200025 CV |
860 | if (from_file || !optarg) |
861 | usage (stderr, 1); | |
138d4f51 CF |
862 | file_arg = optarg; |
863 | break; | |
864 | ||
f135dd3e CF |
865 | case 'M': |
866 | mode_flag = 1; | |
867 | break; | |
868 | ||
418068d4 | 869 | case 'o': |
fc200025 CV |
870 | if (from_file) |
871 | usage (stderr, 1); | |
418068d4 CF |
872 | options_from_file_flag = 1; |
873 | break; | |
874 | ||
1fd5e000 CF |
875 | case 'p': |
876 | path_flag = 1; | |
877 | break; | |
878 | ||
879 | case 'u': | |
1fd5e000 CF |
880 | unix_flag = 1; |
881 | break; | |
882 | ||
883 | case 'w': | |
1fd5e000 CF |
884 | windows_flag = 1; |
885 | break; | |
886 | ||
1050e57c | 887 | case 'm': |
1050e57c CF |
888 | windows_flag = 1; |
889 | mixed_flag = 1; | |
890 | break; | |
891 | ||
5bb52de4 | 892 | case 'l': |
5bb52de4 CV |
893 | longname_flag = 1; |
894 | break; | |
895 | ||
45b80bb4 | 896 | case 's': |
45b80bb4 CF |
897 | shortname_flag = 1; |
898 | break; | |
899 | ||
fc200025 CV |
900 | case 't': |
901 | if (!optarg) | |
2bd6505b CF |
902 | usage (stderr, 1); |
903 | ||
1050e57c CF |
904 | format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg); |
905 | if (strcasecmp (format_type_arg, "dos") == 0) | |
cf157504 | 906 | { |
fc200025 CV |
907 | windows_flag = 1; |
908 | shortname_flag = 1; | |
cf157504 | 909 | } |
fc200025 | 910 | else if (!strcasecmp (format_type_arg, "mixed")) |
cf157504 | 911 | { |
fc200025 CV |
912 | windows_flag = 1; |
913 | mixed_flag = 1; | |
cf157504 | 914 | } |
fc200025 | 915 | else if (!strcasecmp (format_type_arg, "unix")) |
1050e57c | 916 | unix_flag = 1; |
fc200025 | 917 | else if (!strcasecmp (format_type_arg, "windows")) |
cf157504 | 918 | windows_flag = 1; |
2bd6505b CF |
919 | else |
920 | usage (stderr, 1); | |
921 | break; | |
922 | ||
3cdacffc CV |
923 | case 'A': |
924 | allusers_flag = 1; | |
925 | break; | |
926 | ||
5aa8817e CV |
927 | case 'U': |
928 | cygdrive_flag = CCP_PROC_CYGDRIVE; | |
929 | break; | |
930 | ||
0a23799a CV |
931 | case 'C': |
932 | if (!optarg) | |
933 | usage (stderr, 1); | |
934 | if (!strcasecmp (optarg, "ANSI")) | |
935 | codepage = GetACP (); | |
936 | else if (!strcasecmp (optarg, "OEM")) | |
937 | codepage = GetOEMCP (); | |
938 | else if (!strcasecmp (optarg, "UTF8") | |
939 | || !strcasecmp (optarg, "UTF-8")) | |
940 | codepage = CP_UTF8; | |
941 | else | |
942 | { | |
943 | char *c; | |
944 | codepage = (UINT) strtoul (optarg, &c, 10); | |
945 | if (*c) | |
946 | usage (stderr, 1); | |
947 | } | |
948 | break; | |
949 | ||
3cdacffc | 950 | case 'D': |
e355de81 | 951 | case 'H': |
ca902991 | 952 | case 'O': |
3cdacffc | 953 | case 'P': |
f00c1d2c | 954 | case 'S': |
3cdacffc | 955 | case 'W': |
fc200025 | 956 | ++output_flag; |
e355de81 | 957 | o = c; |
3cdacffc | 958 | break; |
f00c1d2c | 959 | |
ca902991 | 960 | case 'F': |
fc200025 | 961 | if (!optarg) |
ca902991 | 962 | usage (stderr, 1); |
fc200025 | 963 | ++output_flag; |
ca902991 CV |
964 | output_arg = optarg; |
965 | o = c; | |
966 | break; | |
967 | ||
45b80bb4 CF |
968 | case 'i': |
969 | ignore_flag = 1; | |
970 | break; | |
971 | ||
1fd5e000 CF |
972 | case 'h': |
973 | usage (stdout, 0); | |
974 | break; | |
975 | ||
92b499ac | 976 | case 'V': |
6a344609 | 977 | print_version (); |
1fd5e000 CF |
978 | exit (0); |
979 | ||
980 | default: | |
92b499ac CV |
981 | fprintf (stderr, "Try `%s --help' for more information.\n", |
982 | prog_name); | |
983 | exit (1); | |
1fd5e000 CF |
984 | } |
985 | } | |
986 | ||
fc200025 | 987 | /* If none of the "important" flags are set, -u is default. */ |
df1841c3 CV |
988 | if (!unix_flag && !windows_flag && !mode_flag |
989 | && (!from_file ? !options_from_file_flag : 1)) | |
fc200025 CV |
990 | unix_flag = 1; |
991 | ||
992 | /* Only one of ... */ | |
df1841c3 | 993 | if (unix_flag + windows_flag + mode_flag > 1 |
fc200025 | 994 | + (!from_file ? options_from_file_flag : 0)) |
418068d4 CF |
995 | usage (stderr, 1); |
996 | ||
fc200025 CV |
997 | /* options_from_file_flag requires a file. */ |
998 | if (!from_file && options_from_file_flag && !file_arg) | |
1050e57c CF |
999 | usage (stderr, 1); |
1000 | ||
fc200025 CV |
1001 | /* longname and shortname don't play well together. */ |
1002 | if (longname_flag && shortname_flag) | |
1050e57c CF |
1003 | usage (stderr, 1); |
1004 | ||
fc200025 CV |
1005 | /* longname and shortname only make sense with Windows paths. */ |
1006 | if ((longname_flag || shortname_flag) && !windows_flag) | |
1fd5e000 CF |
1007 | usage (stderr, 1); |
1008 | ||
fc200025 CV |
1009 | return o; |
1010 | } | |
1011 | ||
1012 | static void | |
1013 | action (int argc, char **argv, int opt) | |
1014 | { | |
1015 | if (output_flag) | |
1fd5e000 | 1016 | { |
fc200025 CV |
1017 | if (argv[optind]) |
1018 | usage (stderr, 1); | |
5bb52de4 | 1019 | |
fc200025 CV |
1020 | do_sysfolders (opt); |
1021 | } | |
1022 | else | |
1023 | { | |
0a5ea947 | 1024 | if (optind > argc - 1) |
138d4f51 | 1025 | usage (stderr, 1); |
1fd5e000 | 1026 | |
f135dd3e CF |
1027 | for (int i = optind; argv[i]; i++) |
1028 | if (mode_flag) | |
1029 | report_mode (argv[i]); | |
1030 | else | |
fc200025 | 1031 | do_pathconv (argv[i]); |
138d4f51 | 1032 | } |
fc200025 CV |
1033 | } |
1034 | ||
1035 | int | |
1036 | main (int argc, char **argv) | |
1037 | { | |
1038 | int o; | |
1039 | ||
73535010 | 1040 | setlocale (LC_CTYPE, ""); |
92b499ac | 1041 | prog_name = program_invocation_short_name; |
fc200025 CV |
1042 | |
1043 | o = do_options (argc, argv, 0); | |
1044 | ||
1045 | if (!file_arg) | |
1046 | action (argc, argv, o); | |
1fd5e000 CF |
1047 | else |
1048 | { | |
138d4f51 CF |
1049 | FILE *fp; |
1050 | char buf[PATH_MAX * 2 + 1]; | |
1fd5e000 | 1051 | |
138d4f51 CF |
1052 | if (argv[optind]) |
1053 | usage (stderr, 1); | |
1fd5e000 | 1054 | |
fc200025 CV |
1055 | if (strcmp (file_arg, "-")) |
1056 | { | |
1057 | if (!(fp = fopen (file_arg, "rt"))) | |
1058 | { | |
1059 | perror ("cygpath"); | |
1060 | exit (1); | |
1061 | } | |
1062 | } | |
1fd5e000 | 1063 | else |
138d4f51 CF |
1064 | { |
1065 | fp = stdin; | |
1066 | setmode (0, O_TEXT); | |
1067 | } | |
418068d4 | 1068 | setbuf (stdout, NULL); |
fc200025 CV |
1069 | |
1070 | while (fgets (buf, sizeof (buf), fp)) | |
138d4f51 | 1071 | { |
6004370b CV |
1072 | int ac = 0; |
1073 | char *av[4] = { NULL, NULL, NULL, NULL }; | |
fc200025 | 1074 | char *p = strchr (buf, '\n'); |
138d4f51 CF |
1075 | if (p) |
1076 | *p = '\0'; | |
6004370b CV |
1077 | p = buf; |
1078 | av[ac++] = prog_name; | |
1079 | av[ac++] = p; | |
1080 | if (options_from_file_flag && *p == '-') | |
fc200025 | 1081 | { |
6004370b CV |
1082 | while (*p && !isspace (*p)) |
1083 | ++p; | |
1084 | if (*p) | |
1085 | { | |
1086 | *p++ = '\0'; | |
1087 | while (*p && isspace (*p)) | |
1088 | ++p; | |
1089 | av[ac++] = p; | |
1090 | } | |
1091 | o = do_options (ac, av, 1); | |
fc200025 | 1092 | } |
6004370b | 1093 | else |
418068d4 | 1094 | { |
6004370b CV |
1095 | output_flag = 0; |
1096 | optind = 1; | |
418068d4 | 1097 | } |
fc200025 | 1098 | action (ac, av, o); |
138d4f51 CF |
1099 | } |
1100 | } | |
1fd5e000 CF |
1101 | exit (0); |
1102 | } |