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