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
30 #define WIN32_LEAN_AND_MEAN
34 int WCMD_dir_sort (const void *a
, const void *b
);
35 void WCMD_list_directory (char *path
, int level
);
36 char * WCMD_filesize64 (ULONGLONG free
);
37 char * WCMD_strrev (char *buff
);
41 extern char quals
[MAX_PATH
], param1
[MAX_PATH
], param2
[MAX_PATH
];
42 extern DWORD errorlevel
;
44 static int file_total
, dir_total
, recurse
, wide
, bare
, max_width
;
45 static ULONGLONG byte_total
;
47 /*****************************************************************************
50 * List a file directory.
54 void WCMD_directory (void) {
56 char path
[MAX_PATH
], drive
[8];
57 int status
, paged_mode
;
58 ULARGE_INTEGER avail
, total
, free
;
59 CONSOLE_SCREEN_BUFFER_INFO consoleInfo
;
62 file_total
= dir_total
= 0;
65 paged_mode
= (strstr(quals
, "/P") != NULL
);
66 recurse
= (strstr(quals
, "/S") != NULL
);
67 wide
= (strstr(quals
, "/W") != NULL
);
68 bare
= (strstr(quals
, "/B") != NULL
);
70 /* Handle conflicting args and initialization */
71 if (bare
) wide
= FALSE
;
74 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &consoleInfo
))
75 max_width
= consoleInfo
.dwSize
.X
;
80 WCMD_enter_paged_mode();
83 if (param1
[0] == '\0') strcpy (param1
, ".");
84 status
= GetFullPathName (param1
, sizeof(path
), path
, NULL
);
87 if (paged_mode
) WCMD_leave_paged_mode();
90 lstrcpyn (drive
, path
, 3);
93 status
= WCMD_volume (0, drive
);
95 if (paged_mode
) WCMD_leave_paged_mode();
100 WCMD_list_directory (path
, 0);
101 lstrcpyn (drive
, path
, 4);
102 GetDiskFreeSpaceEx (drive
, &avail
, &total
, &free
);
106 WCMD_output ("\n\n Total files listed:\n%8d files%25s bytes\n",
107 file_total
, WCMD_filesize64 (byte_total
));
108 WCMD_output ("%8d directories %18s bytes free\n\n",
109 dir_total
, WCMD_filesize64 (free
.QuadPart
));
111 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free
.QuadPart
));
114 if (paged_mode
) WCMD_leave_paged_mode();
117 /*****************************************************************************
118 * WCMD_list_directory
120 * List a single file directory. This function (and those below it) can be called
121 * recursively when the /S switch is used.
123 * FIXME: Entries sorted by name only. Should we support DIRCMD??
124 * FIXME: Assumes 24-line display for the /P qualifier.
125 * FIXME: Other command qualifiers not supported.
126 * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
129 void WCMD_list_directory (char *search_path
, int level
) {
131 char string
[1024], datestring
[32], timestring
[32];
133 char real_path
[MAX_PATH
];
138 int status
, dir_count
, file_count
, entry_count
, i
, widest
, cur_width
, tmp_width
;
139 ULARGE_INTEGER byte_count
, file_size
;
144 byte_count
.QuadPart
= 0;
149 * If the path supplied does not include a wildcard, and the endpoint of the
150 * path references a directory, we need to list the *contents* of that
151 * directory not the directory file itself.
154 if ((strchr(search_path
, '*') == NULL
) && (strchr(search_path
, '%') == NULL
)) {
155 status
= GetFileAttributes (search_path
);
156 if ((status
!= INVALID_FILE_ATTRIBUTES
) && (status
& FILE_ATTRIBUTE_DIRECTORY
)) {
157 if (search_path
[strlen(search_path
)-1] == '\\') {
158 strcat (search_path
, "*");
161 strcat (search_path
, "\\*");
166 /* Work out the actual current directory name */
167 p
= strrchr (search_path
, '\\');
168 memset(real_path
, 0x00, sizeof(real_path
));
169 lstrcpyn (real_path
, search_path
, (p
-search_path
+2));
171 /* Load all files into an in memory structure */
172 fd
= malloc (sizeof(WIN32_FIND_DATA
));
173 hff
= FindFirstFile (search_path
, fd
);
174 if (hff
== INVALID_HANDLE_VALUE
) {
175 SetLastError (ERROR_FILE_NOT_FOUND
);
183 /* Keep running track of longest filename for wide output */
185 int tmpLen
= strlen((fd
+(entry_count
-1))->cFileName
) + 3;
186 if ((fd
+(entry_count
-1))->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) tmpLen
= tmpLen
+ 2;
187 if (tmpLen
> widest
) widest
= tmpLen
;
190 fd
= realloc (fd
, (entry_count
+1)*sizeof(WIN32_FIND_DATA
));
193 WCMD_output ("Memory Allocation Error");
196 } while (FindNextFile(hff
, (fd
+entry_count
)) != 0);
199 /* Sort the list of files */
200 qsort (fd
, entry_count
, sizeof(WIN32_FIND_DATA
), WCMD_dir_sort
);
202 /* Output the results */
204 if (level
!= 0) WCMD_output ("\n\n");
205 WCMD_output ("Directory of %s\n\n", real_path
);
208 for (i
=0; i
<entry_count
; i
++) {
209 FileTimeToLocalFileTime (&(fd
+i
)->ftLastWriteTime
, &ft
);
210 FileTimeToSystemTime (&ft
, &st
);
211 GetDateFormat (0, DATE_SHORTDATE
, &st
, NULL
, datestring
,
213 GetTimeFormat (0, TIME_NOSECONDS
, &st
,
214 NULL
, timestring
, sizeof(timestring
));
218 tmp_width
= cur_width
;
219 if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
220 WCMD_output ("[%s]", (fd
+i
)->cFileName
);
222 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) + 2;
224 WCMD_output ("%s", (fd
+i
)->cFileName
);
225 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) ;
227 #ifndef NONAMELESSSTRUCT
228 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
229 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
231 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
232 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
234 byte_count
.QuadPart
+= file_size
.QuadPart
;
236 cur_width
= cur_width
+ widest
;
238 if ((cur_width
+ widest
) > max_width
) {
242 WCMD_output ("%*.s", (tmp_width
- cur_width
) ,"");
245 } else if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
249 WCMD_output ("%10s %8s <DIR> %s\n",
250 datestring
, timestring
, (fd
+i
)->cFileName
);
252 if (!((strcmp((fd
+i
)->cFileName
, ".") == 0) ||
253 (strcmp((fd
+i
)->cFileName
, "..") == 0))) {
254 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
260 #ifndef NONAMELESSSTRUCT
261 file_size
.LowPart
= (fd
+i
)->nFileSizeLow
;
262 file_size
.HighPart
= (fd
+i
)->nFileSizeHigh
;
264 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
265 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
267 byte_count
.QuadPart
+= file_size
.QuadPart
;
269 WCMD_output ("%10s %8s %10s %s\n",
270 datestring
, timestring
,
271 WCMD_filesize64(file_size
.QuadPart
), (fd
+i
)->cFileName
);
273 WCMD_output ("%s%s\n", recurse
?real_path
:"", (fd
+i
)->cFileName
);
278 if (wide
&& cur_width
>0) {
283 if (file_count
== 1) {
284 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count
.QuadPart
));
287 WCMD_output ("%8d files %24s bytes\n", file_count
, WCMD_filesize64 (byte_count
.QuadPart
));
290 byte_total
= byte_total
+ byte_count
.QuadPart
;
291 file_total
= file_total
+ file_count
;
292 dir_total
= dir_total
+ dir_count
;
295 if (dir_count
== 1) WCMD_output ("1 directory ");
296 else WCMD_output ("%8d directories", dir_count
);
298 for (i
=0; i
<entry_count
; i
++) {
300 ((fd
+i
)->cFileName
[0] != '.') &&
301 ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
303 GetFullPathName ((fd
+i
)->cFileName
, sizeof(string
), string
, NULL
);
305 p
= strrchr (search_path
, '\\');
306 lstrcpyn (string
, search_path
, (p
-search_path
+2));
307 lstrcat (string
, (fd
+i
)->cFileName
);
309 WCMD_list_directory (string
, 1);
316 /*****************************************************************************
319 * Convert a 64-bit number into a character string, with commas every three digits.
320 * Result is returned in a static string overwritten with each call.
321 * FIXME: There must be a better algorithm!
324 char * WCMD_filesize64 (ULONGLONG n
) {
329 static char buff
[32];
334 if ((++i
)%3 == 1) *p
++ = ',';
345 /*****************************************************************************
348 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
351 char * WCMD_strrev (char *buff
) {
357 for (i
=0; i
<r
/2; i
++) {
359 buff
[i
] = buff
[r
-i
-1];
366 int WCMD_dir_sort (const void *a
, const void *b
)
368 return (lstrcmpi(((const WIN32_FIND_DATA
*)a
)->cFileName
,
369 ((const WIN32_FIND_DATA
*)b
)->cFileName
));