]>
Commit | Line | Data |
---|---|---|
e05f3227 | 1 | /* cygpath.cc -- convert pathnames between Windows and Unix format |
c99e136f CF |
2 | Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, |
3 | 2006, 2007 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 | ||
3cdacffc CV |
11 | #define NOCOMATTRIBUTE |
12 | ||
13 | #include <shlobj.h> | |
1fd5e000 CF |
14 | #include <stdio.h> |
15 | #include <string.h> | |
16 | #include <stdlib.h> | |
fc200025 | 17 | #include <argz.h> |
1fd5e000 CF |
18 | #include <limits.h> |
19 | #include <getopt.h> | |
e73a56e9 | 20 | #include <windows.h> |
138d4f51 CF |
21 | #include <io.h> |
22 | #include <sys/fcntl.h> | |
1fd5e000 | 23 | #include <sys/cygwin.h> |
418068d4 | 24 | #include <ctype.h> |
7c03f799 | 25 | #include <errno.h> |
2dba45f4 CV |
26 | #include <ddk/ntddk.h> |
27 | #include <ddk/winddk.h> | |
28 | #include <ddk/ntifs.h> | |
1fd5e000 | 29 | |
6a344609 CF |
30 | static const char version[] = "$Revision$"; |
31 | ||
1fd5e000 | 32 | static char *prog_name; |
ca902991 | 33 | static char *file_arg, *output_arg; |
418068d4 | 34 | static int path_flag, unix_flag, windows_flag, absolute_flag; |
5bb52de4 CV |
35 | static int shortname_flag, longname_flag; |
36 | static int ignore_flag, allusers_flag, output_flag; | |
fc200025 CV |
37 | static int mixed_flag, options_from_file_flag, mode_flag; |
38 | ||
1050e57c | 39 | static const char *format_type_arg; |
1fd5e000 | 40 | |
6a344609 | 41 | static struct option long_options[] = { |
6a344609 | 42 | {(char *) "absolute", no_argument, NULL, 'a'}, |
1050e57c CF |
43 | {(char *) "close", required_argument, NULL, 'c'}, |
44 | {(char *) "dos", no_argument, NULL, 'd'}, | |
45 | {(char *) "file", required_argument, NULL, 'f'}, | |
46 | {(char *) "help", no_argument, NULL, 'h'}, | |
47 | {(char *) "ignore", no_argument, NULL, 'i'}, | |
48 | {(char *) "long-name", no_argument, NULL, 'l'}, | |
49 | {(char *) "mixed", no_argument, NULL, 'm'}, | |
f135dd3e | 50 | {(char *) "mode", no_argument, NULL, 'M'}, |
6a344609 CF |
51 | {(char *) "option", no_argument, NULL, 'o'}, |
52 | {(char *) "path", no_argument, NULL, 'p'}, | |
1050e57c CF |
53 | {(char *) "short-name", no_argument, NULL, 's'}, |
54 | {(char *) "type", required_argument, NULL, 't'}, | |
6a344609 | 55 | {(char *) "unix", no_argument, NULL, 'u'}, |
6a344609 CF |
56 | {(char *) "version", no_argument, NULL, 'v'}, |
57 | {(char *) "windows", no_argument, NULL, 'w'}, | |
6a344609 CF |
58 | {(char *) "allusers", no_argument, NULL, 'A'}, |
59 | {(char *) "desktop", no_argument, NULL, 'D'}, | |
e355de81 | 60 | {(char *) "homeroot", no_argument, NULL, 'H'}, |
ca902991 | 61 | {(char *) "mydocs", no_argument, NULL, 'O'}, |
1050e57c CF |
62 | {(char *) "smprograms", no_argument, NULL, 'P'}, |
63 | {(char *) "sysdir", no_argument, NULL, 'S'}, | |
64 | {(char *) "windir", no_argument, NULL, 'W'}, | |
ca902991 | 65 | {(char *) "folder", required_argument, NULL, 'F'}, |
6a344609 | 66 | {0, no_argument, 0, 0} |
1fd5e000 CF |
67 | }; |
68 | ||
ca902991 | 69 | static char options[] = "ac:df:hilmMopst:uvwADHOPSWF:"; |
9b566b96 | 70 | |
1fd5e000 | 71 | static void |
6a344609 | 72 | usage (FILE * stream, int status) |
1fd5e000 | 73 | { |
45b80bb4 CF |
74 | if (!ignore_flag || !status) |
75 | fprintf (stream, "\ | |
4c634492 JDF |
76 | Usage: %s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\ |
77 | %s [-c HANDLE] \n\ | |
ca902991 CV |
78 | %s [-ADHOPSW] \n\ |
79 | %s [-F ID] \n\ | |
aa275fe0 | 80 | Convert Unix and Windows format paths, or output system path information\n\ |
bd79b736 | 81 | \n\ |
1050e57c | 82 | Output type options:\n\ |
ca902991 | 83 | -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\ |
1050e57c | 84 | -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\ |
ca902991 CV |
85 | -M, --mode report on mode of file (binmode or textmode)\n\ |
86 | -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\ | |
4c634492 | 87 | -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\ |
1050e57c | 88 | -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\ |
33bd2d12 | 89 | Path conversion options:\n\ |
1050e57c | 90 | -a, --absolute output absolute path\n\ |
ca902991 CV |
91 | -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\ |
92 | -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\ | |
93 | -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\ | |
1050e57c | 94 | System information:\n\ |
ca902991 CV |
95 | -A, --allusers use `All Users' instead of current user for -D, -O, -P\n\ |
96 | -D, --desktop output `Desktop' directory and exit\n\ | |
1050e57c | 97 | -H, --homeroot output `Profiles' directory (home root) and exit\n\ |
ca902991 CV |
98 | -O, --mydocs output `My Documents' directory and exit\n\ |
99 | -P, --smprograms output Start Menu `Programs' directory and exit\n\ | |
100 | -S, --sysdir output system directory and exit\n\ | |
101 | -W, --windir output `Windows' directory and exit\n\ | |
102 | -F, --folder ID output special folder with numeric ID and exit\n\ | |
103 | ", prog_name, prog_name, prog_name, prog_name); | |
befdf18b CF |
104 | if (ignore_flag) |
105 | /* nothing to do */; | |
106 | else if (stream != stdout) | |
107 | fprintf(stream, "Try `%s --help' for more information.\n", prog_name); | |
108 | else | |
1050e57c | 109 | { |
befdf18b | 110 | fprintf (stream, "\ |
33bd2d12 | 111 | Other options:\n\ |
1050e57c CF |
112 | -f, --file FILE read FILE for input; use - to read from STDIN\n\ |
113 | -o, --option read options from FILE as well (for use with --file)\n\ | |
114 | -c, --close HANDLE close HANDLE (for use in captured process)\n\ | |
ca902991 | 115 | -i, --ignore ignore missing argument\n\ |
1050e57c | 116 | -h, --help output usage information and exit\n\ |
ca902991 | 117 | -v, --version output version information and exit\n\ |
1050e57c CF |
118 | "); |
119 | } | |
45b80bb4 CF |
120 | exit (ignore_flag ? 0 : status); |
121 | } | |
122 | ||
2dba45f4 CV |
123 | static inline BOOLEAN |
124 | RtlAllocateUnicodeString (PUNICODE_STRING uni, ULONG size) | |
125 | { | |
126 | uni->Length = 0; | |
127 | uni->MaximumLength = 512; | |
128 | uni->Buffer = (WCHAR *) malloc (size); | |
129 | return uni->Buffer != NULL; | |
130 | } | |
131 | ||
132 | static char * | |
133 | get_device_name (char *path) | |
134 | { | |
135 | UNICODE_STRING ntdev, tgtdev, ntdevdir; | |
136 | ANSI_STRING ans; | |
137 | OBJECT_ATTRIBUTES ntobj; | |
138 | NTSTATUS status; | |
139 | HANDLE lnk, dir; | |
140 | char *ret = strdup (path); | |
141 | PDIRECTORY_BASIC_INFORMATION odi = (PDIRECTORY_BASIC_INFORMATION) | |
142 | alloca (4096); | |
143 | BOOLEAN restart; | |
144 | ULONG cont; | |
145 | ||
146 | if (strncasecmp (path, "\\Device\\", 8)) | |
147 | return ret; | |
148 | ||
149 | if (!RtlAllocateUnicodeString (&ntdev, MAX_PATH * 2)) | |
150 | return ret; | |
151 | if (!RtlAllocateUnicodeString (&tgtdev, MAX_PATH * 2)) | |
152 | return ret; | |
153 | RtlInitAnsiString (&ans, path); | |
154 | RtlAnsiStringToUnicodeString (&ntdev, &ans, FALSE); | |
155 | ||
156 | /* First check if the given device name is a symbolic link itself. If so, | |
157 | query it and use the new name as actual device name to search for in the | |
158 | DOS device name directory. If not, just use the incoming device name. */ | |
159 | InitializeObjectAttributes (&ntobj, &ntdev, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
160 | status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); | |
161 | if (NT_SUCCESS (status)) | |
162 | { | |
163 | status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL); | |
164 | ZwClose (lnk); | |
165 | if (!NT_SUCCESS (status)) | |
166 | goto out; | |
167 | RtlCopyUnicodeString (&ntdev, &tgtdev); | |
168 | } | |
169 | else if (status != STATUS_OBJECT_TYPE_MISMATCH) | |
170 | goto out; | |
171 | ||
172 | for (int i = 0; i < 2; ++i) | |
173 | { | |
174 | /* There are two DOS device directories, the local and the global dir. | |
175 | Try both, local first. */ | |
176 | RtlInitUnicodeString (&ntdevdir, i ? L"\\GLOBAL??" : L"\\??"); | |
177 | ||
178 | /* Open the directory... */ | |
179 | InitializeObjectAttributes (&ntobj, &ntdevdir, OBJ_CASE_INSENSITIVE, | |
180 | NULL, NULL); | |
181 | status = ZwOpenDirectoryObject (&dir, DIRECTORY_QUERY, &ntobj); | |
182 | if (!NT_SUCCESS (status)) | |
183 | break; | |
184 | ||
185 | /* ...and scan it. */ | |
186 | for (restart = TRUE, cont = 0; | |
187 | NT_SUCCESS (ZwQueryDirectoryObject (dir, odi, 4096, TRUE, | |
188 | restart, &cont, NULL)); | |
189 | restart = FALSE) | |
190 | { | |
191 | /* For each entry check if it's a symbolic link. */ | |
192 | InitializeObjectAttributes (&ntobj, &odi->ObjectName, | |
193 | OBJ_CASE_INSENSITIVE, dir, NULL); | |
194 | status = ZwOpenSymbolicLinkObject (&lnk, SYMBOLIC_LINK_QUERY, &ntobj); | |
195 | if (!NT_SUCCESS (status)) | |
196 | continue; | |
197 | tgtdev.Length = 0; | |
198 | tgtdev.MaximumLength = 512; | |
199 | /* If so, query it and compare the target of the symlink with the | |
200 | incoming device name. */ | |
201 | status = ZwQuerySymbolicLinkObject (lnk, &tgtdev, NULL); | |
202 | ZwClose (lnk); | |
203 | if (!NT_SUCCESS (status)) | |
204 | continue; | |
205 | if (RtlEqualUnicodeString (&ntdev, &tgtdev, TRUE)) | |
206 | { | |
207 | /* If the comparison succeeds, the name of the directory entry is | |
208 | a valid DOS device name, if prepended with "\\.\". Return that | |
209 | valid DOS path. */ | |
210 | ULONG len = RtlUnicodeStringToAnsiSize (&odi->ObjectName); | |
211 | ret = (char *) malloc (len + 4); | |
212 | strcpy (ret, "\\\\.\\"); | |
213 | ans.Length = 0; | |
214 | ans.MaximumLength = len; | |
215 | ans.Buffer = ret + 4; | |
216 | RtlUnicodeStringToAnsiString (&ans, &odi->ObjectName, FALSE); | |
217 | ZwClose (dir); | |
218 | goto out; | |
219 | } | |
220 | } | |
221 | ZwClose (dir); | |
222 | } | |
223 | ||
224 | out: | |
225 | free (tgtdev.Buffer); | |
226 | free (ntdev.Buffer); | |
227 | return ret; | |
228 | } | |
229 | ||
230 | static char * | |
231 | get_device_paths (char *path) | |
232 | { | |
233 | char *sbuf; | |
234 | char *ptr; | |
235 | int n = 1; | |
236 | ||
237 | ptr = path; | |
238 | while ((ptr = strchr (ptr, ';'))) | |
239 | { | |
240 | ptr++; | |
241 | n++; | |
242 | } | |
243 | ||
244 | char *paths[n]; | |
245 | DWORD acc = 0; | |
246 | int i; | |
247 | if (!n) | |
248 | return strdup (""); | |
249 | ||
250 | for (i = 0, ptr = path; ptr; i++) | |
251 | { | |
252 | char *next = ptr; | |
253 | ptr = strchr (ptr, ';'); | |
254 | if (ptr) | |
255 | *ptr++ = 0; | |
256 | paths[i] = get_device_name (next); | |
257 | acc += strlen (paths[i]) + 1; | |
258 | } | |
259 | ||
260 | sbuf = (char *) malloc (acc + 1); | |
261 | if (sbuf == NULL) | |
262 | { | |
263 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
264 | exit (1); | |
265 | } | |
266 | ||
267 | sbuf[0] = '\0'; | |
268 | for (i = 0; i < n; i++) | |
269 | { | |
270 | strcat (strcat (sbuf, paths[i]), ";"); | |
271 | free (paths[i]); | |
272 | } | |
273 | ||
274 | strchr (sbuf, '\0')[-1] = '\0'; | |
275 | return sbuf; | |
276 | } | |
277 | ||
45b80bb4 CF |
278 | static char * |
279 | get_short_paths (char *path) | |
280 | { | |
281 | char *sbuf; | |
282 | char *sptr; | |
283 | char *next; | |
284 | char *ptr = path; | |
285 | char *end = strrchr (path, 0); | |
286 | DWORD acc = 0; | |
287 | DWORD len; | |
288 | ||
289 | while (ptr != NULL) | |
45b80bb4 | 290 | { |
6a344609 CF |
291 | next = ptr; |
292 | ptr = strchr (ptr, ';'); | |
293 | if (ptr) | |
294 | *ptr++ = 0; | |
295 | len = GetShortPathName (next, NULL, 0); | |
cc3ce0bb | 296 | if (!len) |
6a344609 CF |
297 | { |
298 | fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, | |
299 | next); | |
300 | exit (2); | |
301 | } | |
302 | acc += len + 1; | |
45b80bb4 | 303 | } |
6a344609 | 304 | sptr = sbuf = (char *) malloc (acc + 1); |
45b80bb4 | 305 | if (sbuf == NULL) |
45b80bb4 | 306 | { |
6a344609 CF |
307 | fprintf (stderr, "%s: out of memory\n", prog_name); |
308 | exit (1); | |
45b80bb4 | 309 | } |
6a344609 CF |
310 | ptr = path; |
311 | for (;;) | |
312 | { | |
e355de81 | 313 | len = GetShortPathName (ptr, sptr, acc); |
cc3ce0bb | 314 | if (!len) |
6a344609 CF |
315 | { |
316 | fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, | |
317 | ptr); | |
318 | exit (2); | |
319 | } | |
45b80bb4 | 320 | |
6a344609 CF |
321 | ptr = strrchr (ptr, 0); |
322 | sptr = strrchr (sptr, 0); | |
323 | if (ptr == end) | |
324 | break; | |
325 | *sptr = ';'; | |
326 | ++ptr, ++sptr; | |
5bb52de4 | 327 | acc -= len + 1; |
6a344609 | 328 | } |
45b80bb4 CF |
329 | return sbuf; |
330 | } | |
331 | ||
332 | static char * | |
333 | get_short_name (const char *filename) | |
334 | { | |
5bb52de4 CV |
335 | char *sbuf, buf[MAX_PATH]; |
336 | DWORD len = GetShortPathName (filename, buf, MAX_PATH); | |
cc3ce0bb | 337 | if (!len) |
6a344609 CF |
338 | { |
339 | fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, | |
340 | filename); | |
341 | exit (2); | |
342 | } | |
343 | sbuf = (char *) malloc (++len); | |
45b80bb4 | 344 | if (sbuf == NULL) |
6a344609 CF |
345 | { |
346 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
347 | exit (1); | |
348 | } | |
5bb52de4 CV |
349 | return strcpy (sbuf, buf); |
350 | } | |
351 | ||
cf157504 | 352 | static DWORD WINAPI |
5bb52de4 CV |
353 | get_long_path_name_w32impl (LPCSTR src, LPSTR sbuf, DWORD) |
354 | { | |
355 | char buf1[MAX_PATH], buf2[MAX_PATH], *ptr; | |
356 | const char *pelem, *next; | |
357 | WIN32_FIND_DATA w32_fd; | |
358 | int len; | |
cf157504 | 359 | |
5bb52de4 CV |
360 | strcpy (buf1, src); |
361 | *buf2 = 0; | |
362 | pelem = src; | |
363 | ptr = buf2; | |
364 | while (pelem) | |
365 | { | |
366 | next = pelem; | |
367 | if (*next == '\\') | |
368 | { | |
369 | strcat (ptr++, "\\"); | |
370 | pelem++; | |
371 | if (!*pelem) | |
372 | break; | |
373 | continue; | |
374 | } | |
375 | pelem = strchr (next, '\\'); | |
376 | len = pelem ? (pelem++ - next) : strlen (next); | |
377 | strncpy (ptr, next, len); | |
378 | ptr[len] = 0; | |
379 | if (next[1] != ':' && strcmp(next, ".") && strcmp(next, "..")) | |
380 | { | |
c99e136f CF |
381 | HANDLE h; |
382 | h = FindFirstFile (buf2, &w32_fd); | |
383 | if (h != INVALID_HANDLE_VALUE) | |
384 | { | |
5bb52de4 | 385 | strcpy (ptr, w32_fd.cFileName); |
c99e136f CF |
386 | FindClose (h); |
387 | } | |
5bb52de4 CV |
388 | } |
389 | ptr += strlen (ptr); | |
390 | if (pelem) | |
391 | { | |
392 | *ptr++ = '\\'; | |
393 | *ptr = 0; | |
394 | } | |
395 | } | |
396 | if (sbuf) | |
397 | strcpy (sbuf, buf2); | |
398 | SetLastError (0); | |
399 | return strlen (buf2) + (sbuf ? 0 : 1); | |
400 | } | |
401 | ||
402 | static char * | |
cf157504 | 403 | get_long_name (const char *filename, DWORD& len) |
5bb52de4 | 404 | { |
cf157504 CF |
405 | char *sbuf, buf[MAX_PATH]; |
406 | static HINSTANCE k32 = LoadLibrary ("kernel32.dll"); | |
407 | static DWORD (WINAPI *GetLongPathName) (LPCSTR, LPSTR, DWORD) = | |
4fc92aa7 | 408 | (DWORD (WINAPI *) (LPCSTR, LPSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameA"); |
cf157504 CF |
409 | if (!GetLongPathName) |
410 | GetLongPathName = get_long_path_name_w32impl; | |
5bb52de4 | 411 | |
cf157504 | 412 | len = GetLongPathName (filename, buf, MAX_PATH); |
bc31293a | 413 | if (len == 0) |
5bb52de4 | 414 | { |
acc31d1a CV |
415 | DWORD err = GetLastError (); |
416 | ||
417 | if (err == ERROR_INVALID_PARAMETER) | |
bc31293a CV |
418 | { |
419 | fprintf (stderr, "%s: cannot create long name of %s\n", prog_name, | |
420 | filename); | |
421 | exit (2); | |
422 | } | |
acc31d1a CV |
423 | else if (err == ERROR_FILE_NOT_FOUND) |
424 | len = get_long_path_name_w32impl (filename, buf, MAX_PATH); | |
425 | else | |
c99e136f | 426 | { |
acc31d1a CV |
427 | buf[0] = '\0'; |
428 | strncat (buf, filename, MAX_PATH - 1); | |
429 | len = strlen (buf); | |
430 | } | |
5bb52de4 | 431 | } |
cf157504 CF |
432 | sbuf = (char *) malloc (len + 1); |
433 | if (!sbuf) | |
5bb52de4 CV |
434 | { |
435 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
436 | exit (1); | |
437 | } | |
cf157504 | 438 | return strcpy (sbuf, buf); |
5bb52de4 CV |
439 | } |
440 | ||
441 | static char * | |
cf157504 | 442 | get_long_paths (char *path) |
5bb52de4 | 443 | { |
cf157504 CF |
444 | char *sbuf; |
445 | char *ptr; | |
446 | int n = 1; | |
447 | ||
448 | ptr = path; | |
449 | while ((ptr = strchr (ptr, ';'))) | |
6a344609 | 450 | { |
cf157504 CF |
451 | ptr++; |
452 | n++; | |
6a344609 | 453 | } |
cf157504 CF |
454 | |
455 | char *paths[n]; | |
456 | DWORD acc = 0; | |
457 | int i; | |
458 | if (!n) | |
459 | return strdup (""); | |
460 | ||
461 | for (i = 0, ptr = path; ptr; i++) | |
462 | { | |
463 | DWORD len; | |
464 | char *next = ptr; | |
465 | ptr = strchr (ptr, ';'); | |
466 | if (ptr) | |
467 | *ptr++ = 0; | |
468 | paths[i] = get_long_name (next, len); | |
469 | acc += len + 1; | |
470 | } | |
471 | ||
472 | sbuf = (char *) malloc (acc + 1); | |
5bb52de4 CV |
473 | if (sbuf == NULL) |
474 | { | |
475 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
476 | exit (1); | |
477 | } | |
cf157504 CF |
478 | |
479 | sbuf[0] = '\0'; | |
480 | for (i = 0; i < n; i++) | |
481 | { | |
482 | strcat (strcat (sbuf, paths[i]), ";"); | |
483 | free (paths[i]); | |
484 | } | |
485 | ||
486 | strchr (sbuf, '\0')[-1] = '\0'; | |
487 | return sbuf; | |
5bb52de4 CV |
488 | } |
489 | ||
33bd2d12 CF |
490 | static void |
491 | convert_slashes (char* name) | |
492 | { | |
493 | while ((name = strchr (name, '\\')) != NULL) | |
494 | { | |
495 | if (*name == '\\') | |
496 | *name = '/'; | |
497 | name++; | |
498 | } | |
499 | } | |
500 | ||
501 | static char * | |
502 | get_mixed_name (const char* filename) | |
503 | { | |
504 | char* mixed_buf = strdup (filename); | |
505 | ||
506 | if (mixed_buf == NULL) | |
507 | { | |
508 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
509 | exit (1); | |
510 | } | |
511 | ||
512 | convert_slashes (mixed_buf); | |
513 | ||
514 | return mixed_buf; | |
515 | } | |
516 | ||
ca902991 CV |
517 | static bool |
518 | get_special_folder (char* path, int id) | |
519 | { | |
520 | path[0] = 0; | |
521 | LPITEMIDLIST pidl = 0; | |
522 | if (SHGetSpecialFolderLocation (NULL, id, &pidl) != S_OK) | |
523 | return false; | |
524 | if (!SHGetPathFromIDList (pidl, path) || !path[0]) | |
525 | return false; | |
526 | return true; | |
527 | } | |
528 | ||
529 | static void | |
530 | get_user_folder (char* path, int id, int allid) | |
531 | { | |
532 | if (!get_special_folder (path, allusers_flag ? allid : id) && allusers_flag) | |
533 | get_special_folder (path, id); // Fix for Win9x without any "All Users" | |
534 | } | |
535 | ||
5bb52de4 | 536 | static void |
fc200025 | 537 | do_sysfolders (char option) |
5bb52de4 CV |
538 | { |
539 | char *buf, buf1[MAX_PATH], buf2[MAX_PATH]; | |
540 | DWORD len = MAX_PATH; | |
541 | WIN32_FIND_DATA w32_fd; | |
cf157504 | 542 | HINSTANCE k32; |
5bb52de4 | 543 | BOOL (*GetProfilesDirectoryAPtr) (LPSTR, LPDWORD) = 0; |
cf157504 | 544 | |
5bb52de4 | 545 | buf = buf1; |
ca902991 | 546 | buf[0] = 0; |
5bb52de4 CV |
547 | switch (option) |
548 | { | |
549 | case 'D': | |
ca902991 CV |
550 | get_user_folder (buf, CSIDL_DESKTOPDIRECTORY, |
551 | CSIDL_COMMON_DESKTOPDIRECTORY); | |
5bb52de4 CV |
552 | break; |
553 | ||
554 | case 'P': | |
ca902991 CV |
555 | get_user_folder (buf, CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS); |
556 | break; | |
557 | ||
558 | case 'O': | |
559 | get_user_folder (buf, CSIDL_PERSONAL, CSIDL_COMMON_DOCUMENTS); | |
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 | } | |
c99e136f | 572 | get_special_folder (buf, val); |
ca902991 | 573 | } |
5bb52de4 CV |
574 | break; |
575 | ||
576 | case 'H': | |
cf157504 CF |
577 | k32 = LoadLibrary ("userenv"); |
578 | if (k32) | |
5bb52de4 | 579 | GetProfilesDirectoryAPtr = (BOOL (*) (LPSTR, LPDWORD)) |
cf157504 | 580 | GetProcAddress (k32, "GetProfilesDirectoryA"); |
5bb52de4 | 581 | if (GetProfilesDirectoryAPtr) |
33bd2d12 | 582 | (*GetProfilesDirectoryAPtr) (buf, &len); |
5bb52de4 CV |
583 | else |
584 | { | |
585 | GetWindowsDirectory (buf, MAX_PATH); | |
586 | strcat (buf, "\\Profiles"); | |
587 | } | |
588 | break; | |
589 | ||
590 | case 'S': | |
591 | GetSystemDirectory (buf, MAX_PATH); | |
592 | FindFirstFile (buf, &w32_fd); | |
593 | strcpy (strrchr (buf, '\\') + 1, w32_fd.cFileName); | |
594 | break; | |
595 | ||
596 | case 'W': | |
597 | GetWindowsDirectory (buf, MAX_PATH); | |
598 | break; | |
599 | ||
600 | default: | |
601 | usage (stderr, 1); | |
602 | } | |
603 | ||
ca902991 CV |
604 | if (!buf[0]) |
605 | { | |
606 | fprintf (stderr, "%s: failed to retrieve special folder path\n", prog_name); | |
607 | } | |
608 | else if (!windows_flag) | |
5bb52de4 | 609 | { |
7c03f799 CF |
610 | if (cygwin_conv_to_posix_path (buf, buf2)) |
611 | fprintf (stderr, "%s: error converting \"%s\" - %s\n", | |
612 | prog_name, buf, strerror (errno)); | |
613 | else | |
614 | buf = buf2; | |
5bb52de4 CV |
615 | } |
616 | else | |
617 | { | |
618 | if (shortname_flag) | |
33bd2d12 CF |
619 | buf = get_short_name (buf); |
620 | if (mixed_flag) | |
621 | buf = get_mixed_name (buf); | |
5bb52de4 CV |
622 | } |
623 | printf ("%s\n", buf); | |
1fd5e000 CF |
624 | } |
625 | ||
f135dd3e CF |
626 | static void |
627 | report_mode (char *filename) | |
628 | { | |
629 | switch (cygwin_internal (CW_GET_BINMODE, filename)) | |
630 | { | |
631 | case O_BINARY: | |
632 | printf ("%s: binary\n", filename); | |
633 | break; | |
634 | case O_TEXT: | |
635 | printf ("%s: text\n", filename); | |
636 | break; | |
637 | default: | |
638 | fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename, | |
639 | strerror (errno)); | |
640 | break; | |
641 | } | |
642 | } | |
643 | ||
138d4f51 | 644 | static void |
fc200025 | 645 | do_pathconv (char *filename) |
138d4f51 CF |
646 | { |
647 | char *buf; | |
cf157504 | 648 | DWORD len; |
7c03f799 | 649 | int err; |
6a344609 | 650 | int (*conv_func) (const char *, char *); |
138d4f51 | 651 | |
6a344609 | 652 | if (!path_flag) |
c02e32c9 | 653 | { |
cf157504 CF |
654 | len = strlen (filename); |
655 | if (len) | |
7ca68b7e | 656 | len += MAX_PATH + 1001; |
cf157504 CF |
657 | else if (ignore_flag) |
658 | exit (0); | |
659 | else | |
6a344609 | 660 | { |
cf157504 CF |
661 | fprintf (stderr, "%s: can't convert empty path\n", prog_name); |
662 | exit (1); | |
6a344609 | 663 | } |
c02e32c9 | 664 | } |
cf157504 CF |
665 | else if (unix_flag) |
666 | len = cygwin_win32_to_posix_path_list_buf_size (filename); | |
138d4f51 | 667 | else |
cf157504 | 668 | len = cygwin_posix_to_win32_path_list_buf_size (filename); |
138d4f51 CF |
669 | |
670 | buf = (char *) malloc (len); | |
671 | if (buf == NULL) | |
672 | { | |
673 | fprintf (stderr, "%s: out of memory\n", prog_name); | |
674 | exit (1); | |
675 | } | |
676 | ||
677 | if (path_flag) | |
678 | { | |
679 | if (unix_flag) | |
7c03f799 | 680 | err = cygwin_win32_to_posix_path_list (filename, buf); |
138d4f51 | 681 | else |
6a344609 | 682 | { |
7c03f799 CF |
683 | err = cygwin_posix_to_win32_path_list (filename, buf); |
684 | if (err) | |
685 | /* oops */; | |
2dba45f4 | 686 | buf = get_device_paths (buf); |
6a344609 CF |
687 | if (shortname_flag) |
688 | buf = get_short_paths (buf); | |
5bb52de4 CV |
689 | if (longname_flag) |
690 | buf = get_long_paths (buf); | |
2bd6505b CF |
691 | if (mixed_flag) |
692 | buf = get_mixed_name (buf); | |
6a344609 | 693 | } |
7c03f799 CF |
694 | if (err) |
695 | { | |
696 | fprintf (stderr, "%s: error converting \"%s\" - %s\n", | |
697 | prog_name, filename, strerror (errno)); | |
698 | exit (1); | |
699 | } | |
138d4f51 CF |
700 | } |
701 | else | |
702 | { | |
703 | if (unix_flag) | |
6a344609 CF |
704 | conv_func = (absolute_flag ? cygwin_conv_to_full_posix_path : |
705 | cygwin_conv_to_posix_path); | |
138d4f51 | 706 | else |
6a344609 CF |
707 | conv_func = (absolute_flag ? cygwin_conv_to_full_win32_path : |
708 | cygwin_conv_to_win32_path); | |
7c03f799 | 709 | err = conv_func (filename, buf); |
7c03f799 | 710 | if (err) |
6a344609 | 711 | { |
7c03f799 CF |
712 | fprintf (stderr, "%s: error converting \"%s\" - %s\n", |
713 | prog_name, filename, strerror (errno)); | |
6a344609 CF |
714 | exit (1); |
715 | } | |
5bb52de4 CV |
716 | if (!unix_flag) |
717 | { | |
2dba45f4 | 718 | buf = get_device_name (buf); |
7ca68b7e CF |
719 | if (shortname_flag) |
720 | buf = get_short_name (buf); | |
721 | if (longname_flag) | |
722 | buf = get_long_name (buf, len); | |
4fc92aa7 CV |
723 | if (mixed_flag) |
724 | buf = get_mixed_name (buf); | |
5bb52de4 | 725 | } |
138d4f51 CF |
726 | } |
727 | ||
728 | puts (buf); | |
729 | } | |
730 | ||
6a344609 CF |
731 | static void |
732 | print_version () | |
733 | { | |
734 | const char *v = strchr (version, ':'); | |
735 | int len; | |
736 | if (!v) | |
737 | { | |
738 | v = "?"; | |
739 | len = 1; | |
740 | } | |
741 | else | |
742 | { | |
743 | v += 2; | |
744 | len = strchr (v, ' ') - v; | |
745 | } | |
746 | printf ("\ | |
747 | cygpath (cygwin) %.*s\n\ | |
748 | Path Conversion Utility\n\ | |
fc200025 CV |
749 | Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, \n\ |
750 | 2007 Red Hat, Inc.\n\ | |
9b566b96 JDF |
751 | Compiled on %s\n\ |
752 | ", len, v, __DATE__); | |
6a344609 CF |
753 | } |
754 | ||
fc200025 CV |
755 | static int |
756 | do_options (int argc, char **argv, int from_file) | |
1fd5e000 | 757 | { |
6a344609 | 758 | int c, o = 0; |
1fd5e000 | 759 | path_flag = 0; |
fc200025 | 760 | unix_flag = 0; |
1fd5e000 | 761 | windows_flag = 0; |
45b80bb4 | 762 | shortname_flag = 0; |
5bb52de4 | 763 | longname_flag = 0; |
2bd6505b | 764 | mixed_flag = 0; |
45b80bb4 | 765 | ignore_flag = 0; |
3cdacffc CV |
766 | allusers_flag = 0; |
767 | output_flag = 0; | |
f135dd3e | 768 | mode_flag = 0; |
fc200025 CV |
769 | if (!from_file) |
770 | options_from_file_flag = 0; | |
771 | optind = 0; | |
9b566b96 | 772 | while ((c = getopt_long (argc, argv, options, |
2bd6505b | 773 | long_options, (int *) NULL)) != EOF) |
1fd5e000 CF |
774 | { |
775 | switch (c) | |
776 | { | |
418068d4 CF |
777 | case 'a': |
778 | absolute_flag = 1; | |
779 | break; | |
780 | ||
781 | case 'c': | |
fc200025 CV |
782 | if (!optarg) |
783 | usage (stderr, 1); | |
418068d4 CF |
784 | CloseHandle ((HANDLE) strtoul (optarg, NULL, 16)); |
785 | break; | |
786 | ||
1050e57c | 787 | case 'd': |
1050e57c CF |
788 | windows_flag = 1; |
789 | shortname_flag = 1; | |
790 | break; | |
791 | ||
138d4f51 | 792 | case 'f': |
fc200025 CV |
793 | if (from_file || !optarg) |
794 | usage (stderr, 1); | |
138d4f51 CF |
795 | file_arg = optarg; |
796 | break; | |
797 | ||
f135dd3e CF |
798 | case 'M': |
799 | mode_flag = 1; | |
800 | break; | |
801 | ||
418068d4 | 802 | case 'o': |
fc200025 CV |
803 | if (from_file) |
804 | usage (stderr, 1); | |
418068d4 CF |
805 | options_from_file_flag = 1; |
806 | break; | |
807 | ||
1fd5e000 CF |
808 | case 'p': |
809 | path_flag = 1; | |
810 | break; | |
811 | ||
812 | case 'u': | |
1fd5e000 CF |
813 | unix_flag = 1; |
814 | break; | |
815 | ||
816 | case 'w': | |
1fd5e000 CF |
817 | windows_flag = 1; |
818 | break; | |
819 | ||
1050e57c | 820 | case 'm': |
1050e57c CF |
821 | windows_flag = 1; |
822 | mixed_flag = 1; | |
823 | break; | |
824 | ||
5bb52de4 | 825 | case 'l': |
5bb52de4 CV |
826 | longname_flag = 1; |
827 | break; | |
828 | ||
45b80bb4 | 829 | case 's': |
45b80bb4 CF |
830 | shortname_flag = 1; |
831 | break; | |
832 | ||
fc200025 CV |
833 | case 't': |
834 | if (!optarg) | |
2bd6505b CF |
835 | usage (stderr, 1); |
836 | ||
1050e57c CF |
837 | format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg); |
838 | if (strcasecmp (format_type_arg, "dos") == 0) | |
cf157504 | 839 | { |
fc200025 CV |
840 | windows_flag = 1; |
841 | shortname_flag = 1; | |
cf157504 | 842 | } |
fc200025 | 843 | else if (!strcasecmp (format_type_arg, "mixed")) |
cf157504 | 844 | { |
fc200025 CV |
845 | windows_flag = 1; |
846 | mixed_flag = 1; | |
cf157504 | 847 | } |
fc200025 | 848 | else if (!strcasecmp (format_type_arg, "unix")) |
1050e57c | 849 | unix_flag = 1; |
fc200025 | 850 | else if (!strcasecmp (format_type_arg, "windows")) |
cf157504 | 851 | windows_flag = 1; |
2bd6505b CF |
852 | else |
853 | usage (stderr, 1); | |
854 | break; | |
855 | ||
3cdacffc CV |
856 | case 'A': |
857 | allusers_flag = 1; | |
858 | break; | |
859 | ||
860 | case 'D': | |
e355de81 | 861 | case 'H': |
ca902991 | 862 | case 'O': |
3cdacffc | 863 | case 'P': |
f00c1d2c | 864 | case 'S': |
3cdacffc | 865 | case 'W': |
fc200025 | 866 | ++output_flag; |
e355de81 | 867 | o = c; |
3cdacffc | 868 | break; |
f00c1d2c | 869 | |
ca902991 | 870 | case 'F': |
fc200025 | 871 | if (!optarg) |
ca902991 | 872 | usage (stderr, 1); |
fc200025 | 873 | ++output_flag; |
ca902991 CV |
874 | output_arg = optarg; |
875 | o = c; | |
876 | break; | |
877 | ||
45b80bb4 CF |
878 | case 'i': |
879 | ignore_flag = 1; | |
880 | break; | |
881 | ||
1fd5e000 CF |
882 | case 'h': |
883 | usage (stdout, 0); | |
884 | break; | |
885 | ||
886 | case 'v': | |
6a344609 | 887 | print_version (); |
1fd5e000 CF |
888 | exit (0); |
889 | ||
890 | default: | |
891 | usage (stderr, 1); | |
892 | break; | |
893 | } | |
894 | } | |
895 | ||
fc200025 | 896 | /* If none of the "important" flags are set, -u is default. */ |
df1841c3 CV |
897 | if (!unix_flag && !windows_flag && !mode_flag |
898 | && (!from_file ? !options_from_file_flag : 1)) | |
fc200025 CV |
899 | unix_flag = 1; |
900 | ||
901 | /* Only one of ... */ | |
df1841c3 | 902 | if (unix_flag + windows_flag + mode_flag > 1 |
fc200025 | 903 | + (!from_file ? options_from_file_flag : 0)) |
418068d4 CF |
904 | usage (stderr, 1); |
905 | ||
fc200025 CV |
906 | /* options_from_file_flag requires a file. */ |
907 | if (!from_file && options_from_file_flag && !file_arg) | |
1050e57c CF |
908 | usage (stderr, 1); |
909 | ||
fc200025 CV |
910 | /* longname and shortname don't play well together. */ |
911 | if (longname_flag && shortname_flag) | |
1050e57c CF |
912 | usage (stderr, 1); |
913 | ||
fc200025 CV |
914 | /* longname and shortname only make sense with Windows paths. */ |
915 | if ((longname_flag || shortname_flag) && !windows_flag) | |
1fd5e000 CF |
916 | usage (stderr, 1); |
917 | ||
fc200025 CV |
918 | return o; |
919 | } | |
920 | ||
921 | static void | |
922 | action (int argc, char **argv, int opt) | |
923 | { | |
924 | if (output_flag) | |
1fd5e000 | 925 | { |
fc200025 CV |
926 | if (argv[optind]) |
927 | usage (stderr, 1); | |
5bb52de4 | 928 | |
fc200025 CV |
929 | do_sysfolders (opt); |
930 | } | |
931 | else | |
932 | { | |
0a5ea947 | 933 | if (optind > argc - 1) |
138d4f51 | 934 | usage (stderr, 1); |
1fd5e000 | 935 | |
f135dd3e CF |
936 | for (int i = optind; argv[i]; i++) |
937 | if (mode_flag) | |
938 | report_mode (argv[i]); | |
939 | else | |
fc200025 | 940 | do_pathconv (argv[i]); |
138d4f51 | 941 | } |
fc200025 CV |
942 | } |
943 | ||
944 | int | |
945 | main (int argc, char **argv) | |
946 | { | |
947 | int o; | |
948 | ||
949 | prog_name = strrchr (argv[0], '/'); | |
950 | if (!prog_name) | |
951 | prog_name = strrchr (argv[0], '\\'); | |
952 | if (!prog_name) | |
953 | prog_name = argv[0]; | |
954 | else | |
955 | prog_name++; | |
956 | ||
957 | o = do_options (argc, argv, 0); | |
958 | ||
959 | if (!file_arg) | |
960 | action (argc, argv, o); | |
1fd5e000 CF |
961 | else |
962 | { | |
138d4f51 CF |
963 | FILE *fp; |
964 | char buf[PATH_MAX * 2 + 1]; | |
1fd5e000 | 965 | |
138d4f51 CF |
966 | if (argv[optind]) |
967 | usage (stderr, 1); | |
1fd5e000 | 968 | |
fc200025 CV |
969 | if (strcmp (file_arg, "-")) |
970 | { | |
971 | if (!(fp = fopen (file_arg, "rt"))) | |
972 | { | |
973 | perror ("cygpath"); | |
974 | exit (1); | |
975 | } | |
976 | } | |
1fd5e000 | 977 | else |
138d4f51 CF |
978 | { |
979 | fp = stdin; | |
980 | setmode (0, O_TEXT); | |
981 | } | |
418068d4 | 982 | setbuf (stdout, NULL); |
fc200025 CV |
983 | |
984 | while (fgets (buf, sizeof (buf), fp)) | |
138d4f51 | 985 | { |
fc200025 CV |
986 | size_t azl = 0; |
987 | int ac; | |
988 | char *az, **av; | |
989 | char *p = strchr (buf, '\n'); | |
138d4f51 CF |
990 | if (p) |
991 | *p = '\0'; | |
fc200025 CV |
992 | if (argz_create_sep (buf, ' ', &az, &azl)) |
993 | { | |
994 | perror ("cygpath"); | |
995 | exit (1); | |
996 | } | |
df1841c3 CV |
997 | if (!az) |
998 | continue; | |
fc200025 CV |
999 | ac = argz_count (az, azl) + 1; |
1000 | av = (char **) malloc ((ac + 1) * sizeof (char *)); | |
1001 | if (!av) | |
418068d4 | 1002 | { |
fc200025 CV |
1003 | perror ("cygpath"); |
1004 | exit (1); | |
418068d4 | 1005 | } |
fc200025 CV |
1006 | av[0] = prog_name; |
1007 | argz_extract (az, azl, av + 1); | |
fc200025 CV |
1008 | if (options_from_file_flag) |
1009 | o = do_options (ac, av, 1); | |
1010 | else | |
df1841c3 | 1011 | optind = 1; |
fc200025 | 1012 | action (ac, av, o); |
df1841c3 | 1013 | free (az); |
fc200025 | 1014 | free (av); |
138d4f51 CF |
1015 | } |
1016 | } | |
1fd5e000 CF |
1017 | exit (0); |
1018 | } |