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.
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
32 int WCMD_dir_sort (const void *a
, const void *b
);
33 void WCMD_list_directory (char *path
, int level
);
34 char * WCMD_filesize64 (ULONGLONG free
);
35 char * WCMD_strrev (char *buff
);
39 extern char newline
[];
40 extern char version_string
[];
43 extern char quals
[MAX_PATH
], param1
[MAX_PATH
], param2
[MAX_PATH
];
44 extern DWORD errorlevel
;
46 static int file_total
, dir_total
, recurse
, wide
, bare
, max_width
;
47 static ULONGLONG byte_total
;
49 /*****************************************************************************
52 * List a file directory.
56 void WCMD_directory (void) {
58 char path
[MAX_PATH
], drive
[8];
59 int status
, paged_mode
;
60 ULARGE_INTEGER avail
, total
, free
;
61 CONSOLE_SCREEN_BUFFER_INFO consoleInfo
;
64 file_total
= dir_total
= 0;
67 paged_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 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &consoleInfo
))
77 max_width
= consoleInfo
.dwSize
.X
;
82 WCMD_enter_paged_mode();
85 if (param1
[0] == '\0') strcpy (param1
, ".");
86 status
= GetFullPathName (param1
, sizeof(path
), path
, NULL
);
89 if (paged_mode
) WCMD_leave_paged_mode();
92 lstrcpyn (drive
, path
, 3);
95 status
= WCMD_volume (0, drive
);
97 if (paged_mode
) WCMD_leave_paged_mode();
102 WCMD_list_directory (path
, 0);
103 lstrcpyn (drive
, path
, 4);
104 GetDiskFreeSpaceEx (drive
, &avail
, &total
, &free
);
108 WCMD_output ("\n\n Total files listed:\n%8d files%25s bytes\n",
109 file_total
, WCMD_filesize64 (byte_total
));
110 WCMD_output ("%8d directories %18s bytes free\n\n",
111 dir_total
, WCMD_filesize64 (free
.QuadPart
));
113 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free
.QuadPart
));
116 if (paged_mode
) WCMD_leave_paged_mode();
119 /*****************************************************************************
120 * WCMD_list_directory
122 * List a single file directory. This function (and those below it) can be called
123 * recursively when the /S switch is used.
125 * FIXME: Entries sorted by name only. Should we support DIRCMD??
126 * FIXME: Assumes 24-line display for the /P qualifier.
127 * FIXME: Other command qualifiers not supported.
128 * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
131 void WCMD_list_directory (char *search_path
, int level
) {
133 char string
[1024], datestring
[32], timestring
[32];
134 char mem_err
[] = "Memory Allocation Error";
136 char real_path
[MAX_PATH
];
141 int status
, dir_count
, file_count
, entry_count
, i
, widest
, cur_width
, tmp_width
;
142 ULARGE_INTEGER byte_count
, file_size
;
147 byte_count
.QuadPart
= 0;
152 * If the path supplied does not include a wildcard, and the endpoint of the
153 * path references a directory, we need to list the *contents* of that
154 * directory not the directory file itself.
157 if ((strchr(search_path
, '*') == NULL
) && (strchr(search_path
, '%') == NULL
)) {
158 status
= GetFileAttributes (search_path
);
159 if ((status
!= INVALID_FILE_ATTRIBUTES
) && (status
& FILE_ATTRIBUTE_DIRECTORY
)) {
160 if (search_path
[strlen(search_path
)-1] == '\\') {
161 strcat (search_path
, "*");
164 strcat (search_path
, "\\*");
169 /* Work out the actual current directory name */
170 p
= strrchr (search_path
, '\\');
171 memset(real_path
, 0x00, sizeof(real_path
));
172 lstrcpyn (real_path
, search_path
, (p
-search_path
+2));
174 /* Load all files into an in memory structure */
175 fd
= malloc (sizeof(WIN32_FIND_DATA
));
176 hff
= FindFirstFile (search_path
, fd
);
177 if (hff
== INVALID_HANDLE_VALUE
) {
178 SetLastError (ERROR_FILE_NOT_FOUND
);
186 /* Keep running track of longest filename for wide output */
188 int tmpLen
= strlen((fd
+(entry_count
-1))->cFileName
) + 3;
189 if ((fd
+(entry_count
-1))->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) tmpLen
= tmpLen
+ 2;
190 if (tmpLen
> widest
) widest
= tmpLen
;
193 fd
= realloc (fd
, (entry_count
+1)*sizeof(WIN32_FIND_DATA
));
196 WCMD_output (mem_err
);
199 } while (FindNextFile(hff
, (fd
+entry_count
)) != 0);
202 /* Sort the list of files */
203 qsort (fd
, entry_count
, sizeof(WIN32_FIND_DATA
), WCMD_dir_sort
);
205 /* Output the results */
207 if (level
!= 0) WCMD_output ("\n\n");
208 WCMD_output ("Directory of %s\n\n", real_path
);
211 for (i
=0; i
<entry_count
; i
++) {
212 FileTimeToLocalFileTime (&(fd
+i
)->ftLastWriteTime
, &ft
);
213 FileTimeToSystemTime (&ft
, &st
);
214 GetDateFormat (0, DATE_SHORTDATE
, &st
, NULL
, datestring
,
216 GetTimeFormat (0, TIME_NOSECONDS
, &st
,
217 NULL
, timestring
, sizeof(timestring
));
221 tmp_width
= cur_width
;
222 if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
223 WCMD_output ("[%s]", (fd
+i
)->cFileName
);
225 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) + 2;
227 WCMD_output ("%s", (fd
+i
)->cFileName
);
228 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) ;
230 #ifndef NONAMELESSSTRUCT
231 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
232 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
234 file_size
.s
.LowPart
= (fd
+i
)->nFileSizeLow
;
235 file_size
.s
.HighPart
= (fd
+i
)->nFileSizeHigh
;
237 byte_count
.QuadPart
+= file_size
.QuadPart
;
239 cur_width
= cur_width
+ widest
;
241 if ((cur_width
+ widest
) > max_width
) {
245 WCMD_output ("%*.s", (tmp_width
- cur_width
) ,"");
248 } else if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
252 WCMD_output ("%10s %8s <DIR> %s\n",
253 datestring
, timestring
, (fd
+i
)->cFileName
);
255 if (!((strcmp((fd
+i
)->cFileName
, ".") == 0) ||
256 (strcmp((fd
+i
)->cFileName
, "..") == 0))) {
257 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
263 #ifndef NONAMELESSSTRUCT
264 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
265 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
267 file_size
.s
.LowPart
= (fd
+i
)->nFileSizeLow
;
268 file_size
.s
.HighPart
= (fd
+i
)->nFileSizeHigh
;
270 byte_count
.QuadPart
+= file_size
.QuadPart
;
272 WCMD_output ("%10s %8s %10s %s\n",
273 datestring
, timestring
,
274 WCMD_filesize64(file_size
.QuadPart
), (fd
+i
)->cFileName
);
276 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
281 if (wide
&& cur_width
>0) {
286 if (file_count
== 1) {
287 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count
.QuadPart
));
290 WCMD_output ("%8d files %24s bytes\n", file_count
, WCMD_filesize64 (byte_count
.QuadPart
));
293 byte_total
= byte_total
+ byte_count
.QuadPart
;
294 file_total
= file_total
+ file_count
;
295 dir_total
= dir_total
+ dir_count
;
298 if (dir_count
== 1) WCMD_output ("1 directory ");
299 else WCMD_output ("%8d directories", dir_count
);
301 for (i
=0; i
<entry_count
; i
++) {
303 ((fd
+i
)->cFileName
[0] != '.') &&
304 ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
306 GetFullPathName ((fd
+i
)->cFileName
, sizeof(string
), string
, NULL
);
308 p
= strrchr (search_path
, '\\');
309 lstrcpyn (string
, search_path
, (p
-search_path
+2));
310 lstrcat (string
, (fd
+i
)->cFileName
);
312 WCMD_list_directory (string
, 1);
319 /*****************************************************************************
322 * Convert a 64-bit number into a character string, with commas every three digits.
323 * Result is returned in a static string overwritten with each call.
324 * FIXME: There must be a better algorithm!
327 char * WCMD_filesize64 (ULONGLONG n
) {
332 static char buff
[32];
337 if ((++i
)%3 == 1) *p
++ = ',';
348 /*****************************************************************************
351 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
354 char * WCMD_strrev (char *buff
) {
360 for (i
=0; i
<r
/2; i
++) {
362 buff
[i
] = buff
[r
-i
-1];
369 int WCMD_dir_sort (const void *a
, const void *b
) {
371 return (lstrcmpi(((WIN32_FIND_DATA
*)a
)->cFileName
,
372 ((WIN32_FIND_DATA
*)b
)->cFileName
));