Added an unknown VxD error code.
[wine] / programs / wcmd / directory.c
1 /*
2  * WCMD - Wine-compatible command line interface - Directory functions.
3  *
4  * (C) 1999 D A Pickles
5  *
6  * On entry, global variables quals, param1, param2 contain
7  * the qualifiers (uppercased and concatenated) and parameters entered, with
8  * environment-variable and batch parameter substitution already done.
9  */
10
11 /*
12  * FIXME:
13  * - 32-bit limit on individual file sizes (directories and free space are 64-bit)
14  * - DIR /S fails if the starting directory is not the current default.
15  */
16
17 #include "wcmd.h"
18
19 int WCMD_dir_sort (const void *a, const void *b);
20 void WCMD_list_directory (char *path, int level);
21 char * WCMD_filesize64 (__int64 n);
22 char * WCMD_filesize32 (int n);
23 char * WCMD_strrev (char *buff);
24
25
26 extern char nyi[];
27 extern char newline[];
28 extern char version_string[];
29 extern char anykey[];
30 extern int echo_mode;
31 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
32 extern DWORD errorlevel;
33
34 int file_total, dir_total, line_count, page_mode, recurse;
35 __int64 byte_total;
36
37 /*****************************************************************************
38  * WCMD_directory
39  *
40  * List a file directory.
41  * FIXME: /S switch only works for the current directory
42  *
43  */
44
45 void WCMD_directory () {
46
47 char path[MAX_PATH], drive[8];
48 int status;
49 __int64 free_space;
50 DWORD spc, bps, fc, capacity;
51
52   line_count = 5;
53   page_mode = (strstr(quals, "/P") != NULL);
54   recurse = (strstr(quals, "/S") != NULL);
55   if (param1[0] == '\0') strcpy (param1, ".");
56   status = GetFullPathName (param1, sizeof(path), path, NULL);
57   if (!status) {
58     WCMD_print_error();
59     return;
60   }
61   lstrcpyn (drive, path, 3);
62   status = WCMD_volume (0, drive);
63   if (!status) {
64     return;
65   }
66   WCMD_list_directory (path, 0);
67   lstrcpyn (drive, path, 4);
68   GetDiskFreeSpace (drive, &spc, &bps, &fc, &capacity);
69   free_space = bps * spc * fc;
70   WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free_space));
71   if (recurse) {
72     WCMD_output ("Total files listed:\n%8d files%25s bytes\n%8d directories\n\n",
73          file_total, WCMD_filesize64 (byte_total), dir_total);
74   }
75 }
76
77 /*****************************************************************************
78  * WCMD_list_directory
79  *
80  * List a single file directory. This function (and those below it) can be called
81  * recursively when the /S switch is used.
82  *
83  * FIXME: Assumes individual files are less than 2**32 bytes.
84  * FIXME: Entries sorted by name only. Should we support DIRCMD??
85  * FIXME: Assumes 24-line display for the /P qualifier.
86  * FIXME: Other command qualifiers not supported.
87  * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level. 
88  */
89
90 void WCMD_list_directory (char *search_path, int level) {
91
92 char string[1024], datestring[32], timestring[32];
93 char mem_err[] = "Memory Allocation Error";
94 char *p;
95 DWORD count;
96 WIN32_FIND_DATA *fd;
97 FILETIME ft;
98 SYSTEMTIME st;
99 HANDLE hff;
100 int status, dir_count, file_count, entry_count, i;
101 __int64 byte_count;
102
103   dir_count = 0;
104   file_count = 0;
105   entry_count = 0;
106   byte_count = 0;
107
108 /*
109  *  If the path supplied does not include a wildcard, and the endpoint of the
110  *  path references a directory, we need to list the *contents* of that
111  *  directory not the directory file itself.
112  */
113
114   if ((strchr(search_path, '*') == NULL) && (strchr(search_path, '%') == NULL)) {
115     status = GetFileAttributes (search_path);
116     if ((status != -1) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
117       if (search_path[strlen(search_path)-1] == '\\') {
118         strcat (search_path, "*");
119       }
120       else {
121         strcat (search_path, "\\*");
122       }
123     }
124   }
125
126   fd = malloc (sizeof(WIN32_FIND_DATA));
127   hff = FindFirstFile (search_path, fd);
128   if (hff == INVALID_HANDLE_VALUE) {
129     SetLastError (ERROR_FILE_NOT_FOUND);
130     WCMD_print_error ();
131     free (fd);
132     return;
133   }
134   do {
135     entry_count++;
136     fd = realloc (fd, (entry_count+1)*sizeof(WIN32_FIND_DATA));
137     if (fd == NULL) {
138       FindClose (hff);
139       WCMD_output (mem_err);
140        return;
141     }
142   } while (FindNextFile(hff, (fd+entry_count)) != 0);
143   FindClose (hff);
144   qsort (fd, entry_count, sizeof(WIN32_FIND_DATA), WCMD_dir_sort);
145   if (level != 0) WCMD_output ("\n\n");
146   WCMD_output ("Directory of %s\n\n", search_path);
147   if (page_mode) {
148     line_count += 2;
149     if (line_count > 23) {
150       line_count = 0;
151       WCMD_output (anykey);
152       ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
153     }
154   }
155   for (i=0; i<entry_count; i++) {
156     FileTimeToLocalFileTime (&(fd+i)->ftLastWriteTime, &ft);
157     FileTimeToSystemTime (&ft, &st);
158     GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring,
159                 sizeof(datestring));
160     GetTimeFormat (0, TIME_NOSECONDS, &st,
161                 NULL, timestring, sizeof(timestring));
162     if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
163       dir_count++;
164       WCMD_output ("%8s  %8s   <DIR>        %s\n",
165           datestring, timestring, (fd+i)->cFileName);
166     }
167     else {
168       file_count++;
169       byte_count += (fd+i)->nFileSizeLow;
170       WCMD_output ("%8s  %8s    %10s  %s\n",
171           datestring, timestring,
172           WCMD_filesize32((fd+i)->nFileSizeLow), (fd+i)->cFileName);
173     }
174     if (page_mode) {
175       if (++line_count > 23) {
176         line_count = 0;
177         WCMD_output (anykey);
178         ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
179       }
180     }
181   }
182   if (file_count == 1) {
183     WCMD_output ("       1 file %25s bytes\n", WCMD_filesize64 (byte_count));
184   }
185   else {
186     WCMD_output ("%8d files %24s bytes\n", file_count, WCMD_filesize64 (byte_count));
187   }
188   if (page_mode) {
189     if (++line_count > 23) {
190       line_count = 0;
191       WCMD_output (anykey);
192       ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
193     }
194   }
195   byte_total = byte_total + byte_count;
196   file_total = file_total + file_count;
197   dir_total = dir_total + dir_count;
198   if (dir_count == 1) WCMD_output ("1 directory         ");
199   else WCMD_output ("%8d directories", dir_count);
200   if (page_mode) {
201     if (++line_count > 23) {
202       line_count = 0;
203       WCMD_output (anykey);
204       ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
205     }
206   }
207   for (i=0; i<entry_count; i++) {
208     if ((recurse) &&
209           ((fd+i)->cFileName[0] != '.') &&
210           ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
211 #if 0
212       GetFullPathName ((fd+i)->cFileName, sizeof(string), string, NULL);
213 #endif
214       p = strrchr (search_path, '\\');
215       lstrcpyn (string, search_path, (p-search_path+2));
216       lstrcat (string, (fd+i)->cFileName);
217       lstrcat (string, p);
218       WCMD_list_directory (string, 1);
219     }
220   }
221   free (fd);
222   return;
223 }
224
225 /*****************************************************************************
226  * WCMD_filesize64
227  *
228  * Convert a 64-bit number into a character string, with commas every three digits.
229  * Result is returned in a static string overwritten with each call.
230  * FIXME: There must be a better algorithm!
231  */
232
233 char * WCMD_filesize64 (__int64 n) {
234
235 __int64 q;
236 int r, i;
237 char *p;
238 static char buff[32];
239
240   p = buff;
241   i = -3;
242   do {
243     if ((++i)%3 == 1) *p++ = ',';
244     q = n / 10;
245     r = n - (q * 10);
246     *p++ = r + '0';
247     *p = '\0';
248     n = q;
249   } while (n != 0);
250   WCMD_strrev (buff);
251   return buff;
252 }
253
254 /*****************************************************************************
255  * WCMD_filesize32
256  *
257  * Convert a 32-bit number into a character string, with commas every three digits.
258  * Result is returned in a static string overwritten with each call.
259  * FIXME: There must be a better algorithm!
260  */
261
262 char * WCMD_filesize32 (int n) {
263
264 int r, i;
265 char *p, *q;
266 static char buff1[16], buff2[16];
267
268   wsprintf (buff1, "%i", n);
269   r = lstrlen (buff1);
270   WCMD_strrev (buff1);
271   p = buff1;
272   q = buff2;
273   for (i=0; i<r; i++) {
274   if ((i-2)%3 == 1) *q++ = ',';
275   *q++ = *p++;
276   }
277   *q = '\0';
278   WCMD_strrev (buff2);
279   return buff2;
280 }
281
282 /*****************************************************************************
283  * WCMD_strrev
284  *
285  * Reverse a character string in-place (strrev() is not available under unixen :-( ).
286  */
287
288 char * WCMD_strrev (char *buff) {
289
290 int r, i;
291 char b;
292
293   r = lstrlen (buff);
294   for (i=0; i<r/2; i++) {
295     b = buff[i];
296     buff[i] = buff[r-i-1];
297     buff[r-i-1] = b;
298   }
299   return (buff);
300 }
301
302
303 int WCMD_dir_sort (const void *a, const void *b) {
304
305   return (lstrcmpi(((WIN32_FIND_DATA *)a)->cFileName,
306         ((WIN32_FIND_DATA *)b)->cFileName));
307 }
308