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 quals
[MAX_PATH
], param1
[MAX_PATH
], param2
[MAX_PATH
];
40 extern DWORD errorlevel
;
42 static int file_total
, dir_total
, recurse
, wide
, bare
, max_width
;
43 static ULONGLONG byte_total
;
45 /*****************************************************************************
48 * List a file directory.
52 void WCMD_directory (void) {
54 char path
[MAX_PATH
], drive
[8];
55 int status
, paged_mode
;
56 ULARGE_INTEGER avail
, total
, free
;
57 CONSOLE_SCREEN_BUFFER_INFO consoleInfo
;
60 file_total
= dir_total
= 0;
63 paged_mode
= (strstr(quals
, "/P") != NULL
);
64 recurse
= (strstr(quals
, "/S") != NULL
);
65 wide
= (strstr(quals
, "/W") != NULL
);
66 bare
= (strstr(quals
, "/B") != NULL
);
68 /* Handle conflicting args and initialization */
69 if (bare
) wide
= FALSE
;
72 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &consoleInfo
))
73 max_width
= consoleInfo
.dwSize
.X
;
78 WCMD_enter_paged_mode();
81 if (param1
[0] == '\0') strcpy (param1
, ".");
82 status
= GetFullPathName (param1
, sizeof(path
), path
, NULL
);
85 if (paged_mode
) WCMD_leave_paged_mode();
88 lstrcpyn (drive
, path
, 3);
91 status
= WCMD_volume (0, drive
);
93 if (paged_mode
) WCMD_leave_paged_mode();
98 WCMD_list_directory (path
, 0);
99 lstrcpyn (drive
, path
, 4);
100 GetDiskFreeSpaceEx (drive
, &avail
, &total
, &free
);
104 WCMD_output ("\n\n Total files listed:\n%8d files%25s bytes\n",
105 file_total
, WCMD_filesize64 (byte_total
));
106 WCMD_output ("%8d directories %18s bytes free\n\n",
107 dir_total
, WCMD_filesize64 (free
.QuadPart
));
109 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free
.QuadPart
));
112 if (paged_mode
) WCMD_leave_paged_mode();
115 /*****************************************************************************
116 * WCMD_list_directory
118 * List a single file directory. This function (and those below it) can be called
119 * recursively when the /S switch is used.
121 * FIXME: Entries sorted by name only. Should we support DIRCMD??
122 * FIXME: Assumes 24-line display for the /P qualifier.
123 * FIXME: Other command qualifiers not supported.
124 * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
127 void WCMD_list_directory (char *search_path
, int level
) {
129 char string
[1024], datestring
[32], timestring
[32];
131 char real_path
[MAX_PATH
];
136 int status
, dir_count
, file_count
, entry_count
, i
, widest
, cur_width
, tmp_width
;
137 ULARGE_INTEGER byte_count
, file_size
;
142 byte_count
.QuadPart
= 0;
147 * If the path supplied does not include a wildcard, and the endpoint of the
148 * path references a directory, we need to list the *contents* of that
149 * directory not the directory file itself.
152 if ((strchr(search_path
, '*') == NULL
) && (strchr(search_path
, '%') == NULL
)) {
153 status
= GetFileAttributes (search_path
);
154 if ((status
!= INVALID_FILE_ATTRIBUTES
) && (status
& FILE_ATTRIBUTE_DIRECTORY
)) {
155 if (search_path
[strlen(search_path
)-1] == '\\') {
156 strcat (search_path
, "*");
159 strcat (search_path
, "\\*");
164 /* Work out the actual current directory name */
165 p
= strrchr (search_path
, '\\');
166 memset(real_path
, 0x00, sizeof(real_path
));
167 lstrcpyn (real_path
, search_path
, (p
-search_path
+2));
169 /* Load all files into an in memory structure */
170 fd
= malloc (sizeof(WIN32_FIND_DATA
));
171 hff
= FindFirstFile (search_path
, fd
);
172 if (hff
== INVALID_HANDLE_VALUE
) {
173 SetLastError (ERROR_FILE_NOT_FOUND
);
181 /* Keep running track of longest filename for wide output */
183 int tmpLen
= strlen((fd
+(entry_count
-1))->cFileName
) + 3;
184 if ((fd
+(entry_count
-1))->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) tmpLen
= tmpLen
+ 2;
185 if (tmpLen
> widest
) widest
= tmpLen
;
188 fd
= realloc (fd
, (entry_count
+1)*sizeof(WIN32_FIND_DATA
));
191 WCMD_output ("Memory Allocation Error");
194 } while (FindNextFile(hff
, (fd
+entry_count
)) != 0);
197 /* Sort the list of files */
198 qsort (fd
, entry_count
, sizeof(WIN32_FIND_DATA
), WCMD_dir_sort
);
200 /* Output the results */
202 if (level
!= 0) WCMD_output ("\n\n");
203 WCMD_output ("Directory of %s\n\n", real_path
);
206 for (i
=0; i
<entry_count
; i
++) {
207 FileTimeToLocalFileTime (&(fd
+i
)->ftLastWriteTime
, &ft
);
208 FileTimeToSystemTime (&ft
, &st
);
209 GetDateFormat (0, DATE_SHORTDATE
, &st
, NULL
, datestring
,
211 GetTimeFormat (0, TIME_NOSECONDS
, &st
,
212 NULL
, timestring
, sizeof(timestring
));
216 tmp_width
= cur_width
;
217 if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
218 WCMD_output ("[%s]", (fd
+i
)->cFileName
);
220 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) + 2;
222 WCMD_output ("%s", (fd
+i
)->cFileName
);
223 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) ;
225 #ifndef NONAMELESSSTRUCT
226 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
227 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
229 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
230 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
232 byte_count
.QuadPart
+= file_size
.QuadPart
;
234 cur_width
= cur_width
+ widest
;
236 if ((cur_width
+ widest
) > max_width
) {
240 WCMD_output ("%*.s", (tmp_width
- cur_width
) ,"");
243 } else if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
247 WCMD_output ("%10s %8s <DIR> %s\n",
248 datestring
, timestring
, (fd
+i
)->cFileName
);
250 if (!((strcmp((fd
+i
)->cFileName
, ".") == 0) ||
251 (strcmp((fd
+i
)->cFileName
, "..") == 0))) {
252 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
258 #ifndef NONAMELESSSTRUCT
259 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
260 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
262 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
263 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
265 byte_count
.QuadPart
+= file_size
.QuadPart
;
267 WCMD_output ("%10s %8s %10s %s\n",
268 datestring
, timestring
,
269 WCMD_filesize64(file_size
.QuadPart
), (fd
+i
)->cFileName
);
271 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
276 if (wide
&& cur_width
>0) {
281 if (file_count
== 1) {
282 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count
.QuadPart
));
285 WCMD_output ("%8d files %24s bytes\n", file_count
, WCMD_filesize64 (byte_count
.QuadPart
));
288 byte_total
= byte_total
+ byte_count
.QuadPart
;
289 file_total
= file_total
+ file_count
;
290 dir_total
= dir_total
+ dir_count
;
293 if (dir_count
== 1) WCMD_output ("1 directory ");
294 else WCMD_output ("%8d directories", dir_count
);
296 for (i
=0; i
<entry_count
; i
++) {
298 ((fd
+i
)->cFileName
[0] != '.') &&
299 ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
301 GetFullPathName ((fd
+i
)->cFileName
, sizeof(string
), string
, NULL
);
303 p
= strrchr (search_path
, '\\');
304 lstrcpyn (string
, search_path
, (p
-search_path
+2));
305 lstrcat (string
, (fd
+i
)->cFileName
);
307 WCMD_list_directory (string
, 1);
314 /*****************************************************************************
317 * Convert a 64-bit number into a character string, with commas every three digits.
318 * Result is returned in a static string overwritten with each call.
319 * FIXME: There must be a better algorithm!
322 char * WCMD_filesize64 (ULONGLONG n
) {
327 static char buff
[32];
332 if ((++i
)%3 == 1) *p
++ = ',';
343 /*****************************************************************************
346 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
349 char * WCMD_strrev (char *buff
) {
355 for (i
=0; i
<r
/2; i
++) {
357 buff
[i
] = buff
[r
-i
-1];
364 int WCMD_dir_sort (const void *a
, const void *b
) {
366 return (lstrcmpi(((WIN32_FIND_DATA
*)a
)->cFileName
,
367 ((WIN32_FIND_DATA
*)b
)->cFileName
));