2 * WCMD - Wine-compatible command line interface - Directory functions.
4 * Copyright (C) 1999 D A Pickles
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * On entry, global variables quals, param1, param2 contain
24 * the qualifiers (uppercased and concatenated) and parameters entered, with
25 * environment-variable and batch parameter substitution already done.
30 int WCMD_dir_sort (const void *a
, const void *b
);
31 void WCMD_list_directory (char *path
, int level
);
32 char * WCMD_filesize64 (__uint64 free
);
33 char * WCMD_strrev (char *buff
);
37 extern char newline
[];
38 extern char version_string
[];
41 extern char quals
[MAX_PATH
], param1
[MAX_PATH
], param2
[MAX_PATH
];
42 extern DWORD errorlevel
;
44 int file_total
, dir_total
, line_count
, page_mode
, recurse
, wide
, bare
,
48 /*****************************************************************************
51 * List a file directory.
55 void WCMD_directory () {
57 char path
[MAX_PATH
], drive
[8];
59 ULARGE_INTEGER avail
, total
, free
;
60 CONSOLE_SCREEN_BUFFER_INFO consoleInfo
;
64 file_total
= dir_total
= 0;
67 page_mode
= (strstr(quals
, "/P") != NULL
);
68 recurse
= (strstr(quals
, "/S") != NULL
);
69 wide
= (strstr(quals
, "/W") != NULL
);
70 bare
= (strstr(quals
, "/B") != NULL
);
72 /* Handle conflicting args and initialization */
73 if (bare
) wide
= FALSE
;
76 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &consoleInfo
);
77 max_width
= consoleInfo
.dwSize
.X
;
80 if (param1
[0] == '\0') strcpy (param1
, ".");
81 status
= GetFullPathName (param1
, sizeof(path
), path
, NULL
);
86 lstrcpyn (drive
, path
, 3);
89 status
= WCMD_volume (0, drive
);
95 WCMD_list_directory (path
, 0);
96 lstrcpyn (drive
, path
, 4);
97 GetDiskFreeSpaceEx (drive
, &avail
, &total
, &free
);
101 WCMD_output ("\n\n Total files listed:\n%8d files%25s bytes\n",
102 file_total
, WCMD_filesize64 (byte_total
));
103 WCMD_output ("%8d directories %18s bytes free\n\n",
104 dir_total
, WCMD_filesize64 (free
.QuadPart
));
106 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free
.QuadPart
));
111 /*****************************************************************************
112 * WCMD_list_directory
114 * List a single file directory. This function (and those below it) can be called
115 * recursively when the /S switch is used.
117 * FIXME: Entries sorted by name only. Should we support DIRCMD??
118 * FIXME: Assumes 24-line display for the /P qualifier.
119 * FIXME: Other command qualifiers not supported.
120 * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
123 void WCMD_list_directory (char *search_path
, int level
) {
125 char string
[1024], datestring
[32], timestring
[32];
126 char mem_err
[] = "Memory Allocation Error";
128 char real_path
[MAX_PATH
];
134 int status
, dir_count
, file_count
, entry_count
, i
, widest
, linesout
, cur_width
, tmp_width
;
135 ULARGE_INTEGER byte_count
, file_size
;
140 byte_count
.QuadPart
= 0;
146 * If the path supplied does not include a wildcard, and the endpoint of the
147 * path references a directory, we need to list the *contents* of that
148 * directory not the directory file itself.
151 if ((strchr(search_path
, '*') == NULL
) && (strchr(search_path
, '%') == NULL
)) {
152 status
= GetFileAttributes (search_path
);
153 if ((status
!= -1) && (status
& FILE_ATTRIBUTE_DIRECTORY
)) {
154 if (search_path
[strlen(search_path
)-1] == '\\') {
155 strcat (search_path
, "*");
158 strcat (search_path
, "\\*");
163 /* Work out the actual current directory name */
164 p
= strrchr (search_path
, '\\');
165 memset(real_path
, 0x00, sizeof(real_path
));
166 lstrcpyn (real_path
, search_path
, (p
-search_path
+2));
168 /* Load all files into an in memory structure */
169 fd
= malloc (sizeof(WIN32_FIND_DATA
));
170 hff
= FindFirstFile (search_path
, fd
);
171 if (hff
== INVALID_HANDLE_VALUE
) {
172 SetLastError (ERROR_FILE_NOT_FOUND
);
180 /* Keep running track of longest filename for wide output */
182 int tmpLen
= strlen((fd
+(entry_count
-1))->cFileName
) + 3;
183 if ((fd
+(entry_count
-1))->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) tmpLen
= tmpLen
+ 2;
184 if (tmpLen
> widest
) widest
= tmpLen
;
187 fd
= realloc (fd
, (entry_count
+1)*sizeof(WIN32_FIND_DATA
));
190 WCMD_output (mem_err
);
193 } while (FindNextFile(hff
, (fd
+entry_count
)) != 0);
196 /* Sort the list of files */
197 qsort (fd
, entry_count
, sizeof(WIN32_FIND_DATA
), WCMD_dir_sort
);
199 /* Output the results */
201 if (level
!= 0) WCMD_output ("\n\n");
202 WCMD_output ("Directory of %s\n\n", real_path
);
205 if (line_count
> 23) {
207 WCMD_output (anykey
);
208 ReadFile (GetStdHandle(STD_INPUT_HANDLE
), string
, sizeof(string
), &count
, NULL
);
213 for (i
=0; i
<entry_count
; i
++) {
214 FileTimeToLocalFileTime (&(fd
+i
)->ftLastWriteTime
, &ft
);
215 FileTimeToSystemTime (&ft
, &st
);
216 GetDateFormat (0, DATE_SHORTDATE
, &st
, NULL
, datestring
,
218 GetTimeFormat (0, TIME_NOSECONDS
, &st
,
219 NULL
, timestring
, sizeof(timestring
));
223 tmp_width
= cur_width
;
224 if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
226 WCMD_output ("%s", (fd
+i
)->cFileName
);
229 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) + 2;
231 WCMD_output ("%s", (fd
+i
)->cFileName
);
232 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) ;
234 #ifndef NONAMELESSSTRUCT
235 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
236 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
238 file_size
.s
.LowPart
= (fd
+i
)->nFileSizeLow
;
239 file_size
.s
.HighPart
= (fd
+i
)->nFileSizeHigh
;
241 byte_count
.QuadPart
+= file_size
.QuadPart
;
243 cur_width
= cur_width
+ widest
;
245 if ((cur_width
+ widest
) > max_width
) {
250 WCMD_output ("%*.s", (tmp_width
- cur_width
) ,"");
253 } else if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
257 WCMD_output ("%8s %8s <DIR> %s\n",
258 datestring
, timestring
, (fd
+i
)->cFileName
);
261 if (!((strcmp((fd
+i
)->cFileName
, ".") == 0) ||
262 (strcmp((fd
+i
)->cFileName
, "..") == 0))) {
263 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
270 #ifndef NONAMELESSSTRUCT
271 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
272 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
274 file_size
.s
.LowPart
= (fd
+i
)->nFileSizeLow
;
275 file_size
.s
.HighPart
= (fd
+i
)->nFileSizeHigh
;
277 byte_count
.QuadPart
+= file_size
.QuadPart
;
279 WCMD_output ("%8s %8s %10s %s\n",
280 datestring
, timestring
,
281 WCMD_filesize64(file_size
.QuadPart
), (fd
+i
)->cFileName
);
284 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
289 line_count
= line_count
+ linesout
;
291 if (line_count
> 23) {
293 WCMD_output (anykey
);
294 ReadFile (GetStdHandle(STD_INPUT_HANDLE
), string
, sizeof(string
), &count
, NULL
);
299 if (wide
&& cur_width
>0) {
302 if (++line_count
> 23) {
304 WCMD_output (anykey
);
305 ReadFile (GetStdHandle(STD_INPUT_HANDLE
), string
, sizeof(string
), &count
, NULL
);
311 if (file_count
== 1) {
312 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count
.QuadPart
));
315 WCMD_output ("%8d files %24s bytes\n", file_count
, WCMD_filesize64 (byte_count
.QuadPart
));
318 if (++line_count
> 23) {
320 WCMD_output (anykey
);
321 ReadFile (GetStdHandle(STD_INPUT_HANDLE
), string
, sizeof(string
), &count
, NULL
);
325 byte_total
= byte_total
+ byte_count
.QuadPart
;
326 file_total
= file_total
+ file_count
;
327 dir_total
= dir_total
+ dir_count
;
330 if (dir_count
== 1) WCMD_output ("1 directory ");
331 else WCMD_output ("%8d directories", dir_count
);
333 if (++line_count
> 23) {
335 WCMD_output (anykey
);
336 ReadFile (GetStdHandle(STD_INPUT_HANDLE
), string
, sizeof(string
), &count
, NULL
);
340 for (i
=0; i
<entry_count
; i
++) {
342 ((fd
+i
)->cFileName
[0] != '.') &&
343 ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
345 GetFullPathName ((fd
+i
)->cFileName
, sizeof(string
), string
, NULL
);
347 p
= strrchr (search_path
, '\\');
348 lstrcpyn (string
, search_path
, (p
-search_path
+2));
349 lstrcat (string
, (fd
+i
)->cFileName
);
351 WCMD_list_directory (string
, 1);
358 /*****************************************************************************
361 * Convert a 64-bit number into a character string, with commas every three digits.
362 * Result is returned in a static string overwritten with each call.
363 * FIXME: There must be a better algorithm!
366 char * WCMD_filesize64 (__uint64 n
) {
371 static char buff
[32];
376 if ((++i
)%3 == 1) *p
++ = ',';
387 /*****************************************************************************
390 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
393 char * WCMD_strrev (char *buff
) {
399 for (i
=0; i
<r
/2; i
++) {
401 buff
[i
] = buff
[r
-i
-1];
408 int WCMD_dir_sort (const void *a
, const void *b
) {
410 return (lstrcmpi(((WIN32_FIND_DATA
*)a
)->cFileName
,
411 ((WIN32_FIND_DATA
*)b
)->cFileName
));