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