[THEMES]
[reactos.git] / reactos / base / shell / cmd / dir.c
1 /*
2 * DIR.C - dir internal command.
3 *
4 *
5 * History:
6 *
7 * 01/29/97 (Tim Norman)
8 * started.
9 *
10 * 06/13/97 (Tim Norman)
11 * Fixed code.
12 *
13 * 07/12/97 (Tim Norman)
14 * Fixed bug that caused the root directory to be unlistable
15 *
16 * 07/12/97 (Marc Desrochers)
17 * Changed to use maxx, maxy instead of findxy()
18 *
19 * 06/08/98 (Rob Lake)
20 * Added compatibility for /w in dir
21 *
22 * 06/09/98 (Rob Lake)
23 * Compatibility for dir/s started
24 * Tested that program finds directories off root fine
25 *
26 * 06/10/98 (Rob Lake)
27 * do_recurse saves the cwd and also stores it in Root
28 * build_tree adds the cwd to the beginning of its' entries
29 * Program runs fine, added print_tree -- works fine.. as EXE,
30 * program won't work properly as COM.
31 *
32 * 06/11/98 (Rob Lake)
33 * Found problem that caused COM not to work
34 *
35 * 06/12/98 (Rob Lake)
36 * debugged...
37 * added free mem routine
38 *
39 * 06/13/98 (Rob Lake)
40 * debugged the free mem routine
41 * debugged whole thing some more
42 * Notes:
43 * ReadDir stores Root name and _Read_Dir does the hard work
44 * PrintDir prints Root and _Print_Dir does the hard work
45 * KillDir kills Root _after_ _Kill_Dir does the hard work
46 * Integrated program into DIR.C(this file) and made some same
47 * changes throughout
48 *
49 * 06/14/98 (Rob Lake)
50 * Cleaned up code a bit, added comments
51 *
52 * 06/16/98 (Rob Lake)
53 * Added error checking to my previously added routines
54 *
55 * 06/17/98 (Rob Lake)
56 * Rewrote recursive functions, again! Most other recursive
57 * functions are now obsolete -- ReadDir, PrintDir, _Print_Dir,
58 * KillDir and _Kill_Dir. do_recurse does what PrintDir did
59 * and _Read_Dir did what it did before along with what _Print_Dir
60 * did. Makes /s a lot faster!
61 * Reports 2 more files/dirs that MS-DOS actually reports
62 * when used in root directory(is this because dir defaults
63 * to look for read only files?)
64 * Added support for /b, /a and /l
65 * Made error message similar to DOS error messages
66 * Added help screen
67 *
68 * 06/20/98 (Rob Lake)
69 * Added check for /-(switch) to turn off previously defined
70 * switches.
71 * Added ability to check for DIRCMD in environment and
72 * process it
73 *
74 * 06/21/98 (Rob Lake)
75 * Fixed up /B
76 * Now can dir *.ext/X, no spaces!
77 *
78 * 06/29/98 (Rob Lake)
79 * error message now found in command.h
80 *
81 * 07/08/1998 (John P. Price)
82 * removed extra returns; closer to MSDOS
83 * fixed wide display so that an extra return is not displayed
84 * when there is five filenames in the last line.
85 *
86 * 07/12/98 (Rob Lake)
87 * Changed error messages
88 *
89 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
90 * added config.h include
91 *
92 *
93 * 04-Dec-1998 (Eric Kohl)
94 * Converted source code to Win32, except recursive dir ("dir /s").
95 *
96 * 10-Dec-1998 (Eric Kohl)
97 * Fixed recursive dir ("dir /s").
98 *
99 * 14-Dec-1998 (Eric Kohl)
100 * Converted to Win32 directory functions and
101 * fixed some output bugs. There are still some more ;)
102 *
103 * 10-Jan-1999 (Eric Kohl)
104 * Added "/N" and "/4" options, "/O" is a dummy.
105 * Added locale support.
106 *
107 * 20-Jan-1999 (Eric Kohl)
108 * Redirection safe!
109 *
110 * 01-Mar-1999 (Eric Kohl)
111 * Replaced all runtime io functions by their Win32 counterparts.
112 *
113 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
114 * dir /s now works in deeper trees
115 *
116 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
117 * Fix for /p, so it is working under Windows in GUI-mode, too.
118 *
119 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
120 * Fix /w to print long names.
121 *
122 * 27-Feb-2005 (Konstantinos Paliouras <squarious@gmail.com>)
123 * Implemented all the switches that were missing, and made
124 * the ros dir very similar to windows dir. Major part of
125 * the code is rewritten. /p is removed, to be rewriten in
126 * the main cmd code.
127 *
128 * 1-Jul-2004 (Brandon Turner <turnerb7@msu.edu>)
129 * Added /p back in using ConOutPrintfPaging
130 *
131 * 3-feb-2007 (Paolo Devoti devotip at gmail)
132 * Removed variables formerly in use to handle pagination
133 * Pagination belongs to ConOutPrintfPaging
134 * Removed already commented out code of old pagination
135 */
136
137 #include "precomp.h"
138
139 #ifdef INCLUDE_CMD_DIR
140
141
142
143 /* Time Field enumeration */
144 enum ETimeField
145 {
146 TF_CREATIONDATE = 0,
147 TF_MODIFIEDDATE = 1,
148 TF_LASTACCESSEDDATE = 2
149 };
150
151 /* Ordered by enumeration */
152 enum EOrderBy
153 {
154 ORDER_NAME = 0,
155 ORDER_SIZE = 1,
156 ORDER_DIRECTORY = 2,
157 ORDER_EXTENSION = 3,
158 ORDER_TIME = 4
159 };
160
161 /* The struct for holding the switches */
162 typedef struct _DirSwitchesFlags
163 {
164 BOOL bBareFormat; /* Bare Format */
165 BOOL bTSeperator; /* Thousands seperator */
166 BOOL bWideList; /* Wide list format */
167 BOOL bWideListColSort; /* Wide list format but sorted by column */
168 BOOL bLowerCase; /* Uses lower case */
169 BOOL bNewLongList; /* New long list */
170 BOOL bPause; /* Pause per page */
171 BOOL bUser; /* Displays the owner of file */
172 BOOL bRecursive; /* Displays files in specified directory and all sub */
173 BOOL bShortName; /* Displays the sort name of files if exist */
174 BOOL b4Digit; /* Four digit year */
175 struct
176 {
177 DWORD dwAttribVal; /* The desired state of attribute */
178 DWORD dwAttribMask; /* Which attributes to check */
179 } stAttribs; /* Displays files with this attributes only */
180 struct
181 {
182 enum EOrderBy eCriteria[3]; /* Criterias used to order by */
183 BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */
184 short sCriteriaCount; /* The quantity of criterias */
185 } stOrderBy; /* Ordered by criterias */
186 struct
187 {
188 enum ETimeField eTimeField; /* The time field that will be used for */
189 } stTimeField; /* The time field to display or use for sorting */
190 } DIRSWITCHFLAGS, *LPDIRSWITCHFLAGS;
191
192 typedef struct _DIRFINDLISTNODE
193 {
194 WIN32_FIND_DATA stFindInfo;
195 struct _DIRFINDLISTNODE *ptrNext;
196 } DIRFINDLISTNODE, *PDIRFINDLISTNODE;
197
198 typedef BOOL
199 (WINAPI *PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
200
201 /* Globally save the # of dirs, files and bytes,
202 * probabaly later pass them to functions. Rob Lake */
203 static ULONG recurse_dir_cnt;
204 static ULONG recurse_file_cnt;
205 static ULONGLONG recurse_bytes;
206
207 /*
208 * help
209 *
210 * displays help screen for dir
211 * Rob Lake
212 */
213 static VOID
214 DirHelp(VOID)
215 {
216 ConOutResPaging(TRUE, STRING_DIR_HELP1);
217 }
218
219 /*
220 * DirReadParameters
221 *
222 * Parse the parameters and switches of the command line and exports them
223 */
224 static BOOL
225 DirReadParam(LPTSTR Line, /* [IN] The line with the parameters & switches */
226 LPTSTR** params, /* [OUT] The parameters after parsing */
227 LPINT entries, /* [OUT] The number of parameters after parsing */
228 LPDIRSWITCHFLAGS lpFlags) /* [IN/OUT] The flags after calculating switches */
229 {
230 TCHAR cCurSwitch; /* The current switch */
231 TCHAR cCurChar; /* Current examing character */
232 TCHAR cCurUChar; /* Current upper examing character */
233 BOOL bNegative; /* Negative switch */
234 BOOL bPNegative; /* Negative switch parameter */
235 BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */
236 LPTSTR ptrStart; /* A pointer to the first character of a parameter */
237 LPTSTR ptrEnd; /* A pointer to the last character of a parameter */
238 BOOL bOrderByNoPar; /* A flag to indicate /O with no switch parameter */
239 LPTSTR temp;
240
241 /* Initialize parameter array */
242 *params = NULL;
243 *entries = 0;
244
245 /* Initialize variables; */
246 cCurSwitch = _T(' ');
247 bNegative = FALSE;
248 bPNegative = FALSE;
249
250 /* We suppose that switch parameters
251 were given to avoid setting them to default
252 if the switch was not given */
253 bOrderByNoPar = FALSE;
254
255 /* Main Loop (see README_DIR.txt) */
256 /* scan the command line char per char, and we process its char */
257 while (*Line)
258 {
259 /* we save current character as it is and its upper case */
260 cCurChar = *Line;
261 cCurUChar = _totupper(*Line);
262
263 /* 1st section (see README_DIR.txt) */
264 /* When a switch is expecting */
265 if (cCurSwitch == _T('/'))
266 {
267 while (*Line == _T(' '))
268 Line++;
269
270 bNegative = (*Line == _T('-'));
271 Line += bNegative;
272
273 cCurChar = *Line;
274 cCurUChar = _totupper(*Line);
275
276 if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O')))
277 {
278 /* If positive, prepare for parameters... if negative, reset to defaults */
279 switch (cCurUChar)
280 {
281 case _T('A'):
282 lpFlags->stAttribs.dwAttribVal = 0L;
283 lpFlags->stAttribs.dwAttribMask = 0L;
284 if (bNegative)
285 lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
286 break;
287 case _T('T'):
288 if (bNegative)
289 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE;
290 break;
291 case _T('O'):
292 bOrderByNoPar = !bNegative;
293 lpFlags->stOrderBy.sCriteriaCount = 0;
294 break;
295 }
296
297 if (!bNegative)
298 {
299 /* Positive switch, so it can take parameters. */
300 cCurSwitch = cCurUChar;
301 Line++;
302 /* Skip optional leading colon */
303 if (*Line == _T(':'))
304 Line++;
305 continue;
306 }
307 }
308 else if (cCurUChar == _T('L'))
309 lpFlags->bLowerCase = ! bNegative;
310 else if (cCurUChar == _T('B'))
311 lpFlags->bBareFormat = ! bNegative;
312 else if (cCurUChar == _T('C'))
313 lpFlags->bTSeperator = ! bNegative;
314 else if (cCurUChar == _T('W'))
315 lpFlags->bWideList = ! bNegative;
316 else if (cCurUChar == _T('D'))
317 lpFlags->bWideListColSort = ! bNegative;
318 else if (cCurUChar == _T('N'))
319 lpFlags->bNewLongList = ! bNegative;
320 else if (cCurUChar == _T('P'))
321 lpFlags->bPause = ! bNegative;
322 else if (cCurUChar == _T('Q'))
323 lpFlags->bUser = ! bNegative;
324 else if (cCurUChar == _T('S'))
325 lpFlags->bRecursive = ! bNegative;
326 else if (cCurUChar == _T('X'))
327 lpFlags->bShortName = ! bNegative;
328 else if (cCurChar == _T('4'))
329 lpFlags->b4Digit = ! bNegative;
330 else if (cCurChar == _T('?'))
331 {
332 DirHelp();
333 return FALSE;
334 }
335 else
336 {
337 error_invalid_switch ((TCHAR)_totupper (*Line));
338 return FALSE;
339 }
340
341 /* Make sure there's no extra characters at the end of the switch */
342 if (Line[1] && Line[1] != _T('/') && Line[1] != _T(' '))
343 {
344 error_parameter_format(Line[1]);
345 return FALSE;
346 }
347
348 cCurSwitch = _T(' ');
349 }
350 else if (cCurSwitch == _T(' '))
351 {
352 /* 2nd section (see README_DIR.txt) */
353 /* We are expecting parameter or the unknown */
354
355 if (cCurChar == _T('/'))
356 cCurSwitch = _T('/');
357 else if (cCurChar == _T(' '))
358 /* do nothing */;
359 else
360 {
361 /* This is a file/directory name parameter. Find its end */
362 ptrStart = Line;
363 bIntoQuotes = FALSE;
364 while (*Line)
365 {
366 if (!bIntoQuotes && (*Line == _T('/') || *Line == _T(' ')))
367 break;
368 bIntoQuotes ^= (*Line == _T('"'));
369 Line++;
370 }
371 ptrEnd = Line;
372
373 /* Copy it to the entries list */
374 temp = cmd_alloc((ptrEnd - ptrStart + 1) * sizeof (TCHAR));
375 if (!temp)
376 return FALSE;
377 memcpy(temp, ptrStart, (ptrEnd - ptrStart) * sizeof (TCHAR));
378 temp[ptrEnd - ptrStart] = _T('\0');
379 StripQuotes(temp);
380 if (!add_entry(entries, params, temp))
381 {
382 cmd_free(temp);
383 freep(*params);
384 return FALSE;
385 }
386
387 cmd_free(temp);
388 continue;
389 }
390 }
391 else
392 {
393 /* 3rd section (see README_DIR.txt) */
394 /* We are waiting for switch parameters */
395
396 /* Check if there are no more switch parameters */
397 if ((cCurChar == _T('/')) || ( cCurChar == _T(' ')))
398 {
399 /* Wrong desicion path, reprocess current character */
400 cCurSwitch = _T(' ');
401 continue;
402 }
403 /* Process parameter switch */
404 switch(cCurSwitch)
405 {
406 case _T('A'): /* Switch parameters for /A (attributes filter) */
407 if (cCurChar == _T('-'))
408 bPNegative = TRUE;
409 else if (cCurUChar == _T('D'))
410 {
411 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY;
412 if (bPNegative)
413 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY;
414 else
415 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY;
416 }
417 else if (cCurUChar == _T('R'))
418 {
419 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY;
420 if (bPNegative)
421 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY;
422 else
423 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY;
424 }
425 else if (cCurUChar == _T('H'))
426 {
427 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN;
428 if (bPNegative)
429 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN;
430 else
431 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN;
432 }
433 else if (cCurUChar == _T('A'))
434 {
435 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE;
436 if (bPNegative)
437 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE;
438 else
439 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE;
440 }
441 else if (cCurUChar == _T('S'))
442 {
443 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM;
444 if (bPNegative)
445 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM;
446 else
447 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM;
448 }
449 else
450 {
451 error_parameter_format((TCHAR)_totupper (*Line));
452 return FALSE;
453 }
454 break;
455 case _T('T'): /* Switch parameters for /T (time field) */
456 if (cCurUChar == _T('C'))
457 lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ;
458 else if (cCurUChar == _T('A'))
459 lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ;
460 else if (cCurUChar == _T('W'))
461 lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ;
462 else
463 {
464 error_parameter_format((TCHAR)_totupper (*Line));
465 return FALSE;
466 }
467 break;
468 case _T('O'): /* Switch parameters for /O (order) */
469 /* Ok a switch parameter was given */
470 bOrderByNoPar = FALSE;
471
472 if (cCurChar == _T('-'))
473 bPNegative = TRUE;
474 else if (cCurUChar == _T('N'))
475 {
476 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
477 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
478 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME;
479 }
480 else if (cCurUChar == _T('S'))
481 {
482 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
483 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
484 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE;
485 }
486 else if (cCurUChar == _T('G'))
487 {
488 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
489 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
490 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY;
491 }
492 else if (cCurUChar == _T('E'))
493 {
494 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
495 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
496 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION;
497 }
498 else if (cCurUChar == _T('D'))
499 {
500 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
501 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
502 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME;
503 }
504
505 else
506 {
507 error_parameter_format((TCHAR)_totupper (*Line));
508 return FALSE;
509 }
510
511
512 }
513 /* We check if we calculated the negative value and realese the flag */
514 if ((cCurChar != _T('-')) && bPNegative)
515 bPNegative = FALSE;
516 }
517
518 Line++;
519 }
520
521 /* /O with no switch parameters acts like /O:GN */
522 if (bOrderByNoPar)
523 {
524 lpFlags->stOrderBy.sCriteriaCount = 2;
525 lpFlags->stOrderBy.eCriteria[0] = ORDER_DIRECTORY;
526 lpFlags->stOrderBy.bCriteriaRev[0] = FALSE;
527 lpFlags->stOrderBy.eCriteria[1] = ORDER_NAME;
528 lpFlags->stOrderBy.bCriteriaRev[1] = FALSE;
529 }
530
531 return TRUE;
532 }
533
534 /* Print either with or without paging, depending on /P switch */
535 static INT
536 DirPrintf(LPDIRSWITCHFLAGS lpFlags, LPTSTR szFormat, ...)
537 {
538 INT iReturn = 0;
539 va_list arg_ptr;
540 va_start(arg_ptr, szFormat);
541 if (lpFlags->bPause)
542 iReturn = ConPrintfPaging(FALSE, szFormat, arg_ptr, STD_OUTPUT_HANDLE);
543 else
544 ConPrintf(szFormat, arg_ptr, STD_OUTPUT_HANDLE);
545 va_end(arg_ptr);
546 return iReturn;
547 }
548
549
550 /*
551 * PrintDirectoryHeader
552 *
553 * print the header for the dir command
554 */
555 static BOOL
556 PrintDirectoryHeader(LPTSTR szPath, LPDIRSWITCHFLAGS lpFlags)
557 {
558 TCHAR szMsg[RC_STRING_MAX_SIZE];
559 TCHAR szFullDir[MAX_PATH];
560 TCHAR szRootName[MAX_PATH];
561 TCHAR szVolName[80];
562 LPTSTR pszFilePart;
563 DWORD dwSerialNr;
564
565 if (lpFlags->bBareFormat)
566 return TRUE;
567
568 if (GetFullPathName(szPath, sizeof(szFullDir) / sizeof(TCHAR), szFullDir, &pszFilePart) == 0)
569 {
570 ErrorMessage(GetLastError(), _T("Failed to build full directory path"));
571 return FALSE;
572 }
573
574 if (pszFilePart != NULL)
575 *pszFilePart = _T('\0');
576
577 /* get the media ID of the drive */
578 if (!GetVolumePathName(szFullDir, szRootName, sizeof(szRootName) / sizeof(TCHAR)) ||
579 !GetVolumeInformation(szRootName, szVolName, 80, &dwSerialNr,
580 NULL, NULL, NULL, 0))
581 {
582 return TRUE;
583 }
584
585 /* print drive info */
586 if (szVolName[0] != _T('\0'))
587 {
588 LoadString(CMD_ModuleHandle, STRING_DIR_HELP2, szMsg, RC_STRING_MAX_SIZE);
589 DirPrintf(lpFlags, szMsg, szRootName[0], szVolName);
590 }
591 else
592 {
593 LoadString(CMD_ModuleHandle, STRING_DIR_HELP3, szMsg, RC_STRING_MAX_SIZE);
594 DirPrintf(lpFlags, szMsg, szRootName[0]);
595 }
596
597 /* print the volume serial number if the return was successful */
598 LoadString(CMD_ModuleHandle, STRING_DIR_HELP4, (LPTSTR) szMsg, RC_STRING_MAX_SIZE);
599 DirPrintf(lpFlags, szMsg, HIWORD(dwSerialNr), LOWORD(dwSerialNr));
600
601 return TRUE;
602 }
603
604
605 static VOID
606 DirPrintFileDateTime(TCHAR *lpDate,
607 TCHAR *lpTime,
608 LPWIN32_FIND_DATA lpFile,
609 LPDIRSWITCHFLAGS lpFlags)
610 {
611 FILETIME ft;
612 SYSTEMTIME dt;
613
614 /* Select the right time field */
615 switch (lpFlags->stTimeField.eTimeField)
616 {
617 case TF_CREATIONDATE:
618 if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft))
619 return;
620 FileTimeToSystemTime(&ft, &dt);
621 break;
622
623 case TF_LASTACCESSEDDATE :
624 if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft))
625 return;
626 FileTimeToSystemTime(&ft, &dt);
627 break;
628
629 case TF_MODIFIEDDATE:
630 if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft))
631 return;
632 FileTimeToSystemTime(&ft, &dt);
633 break;
634 }
635
636 FormatDate(lpDate, &dt, lpFlags->b4Digit);
637 FormatTime(lpTime, &dt);
638 }
639
640 INT
641 FormatDate(TCHAR *lpDate, LPSYSTEMTIME dt, BOOL b4Digit)
642 {
643 /* Format date */
644 WORD wYear = b4Digit ? dt->wYear : dt->wYear%100;
645 switch (nDateFormat)
646 {
647 case 0: /* mmddyy */
648 default:
649 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"),
650 dt->wMonth, cDateSeparator,
651 dt->wDay, cDateSeparator,
652 b4Digit?4:2, wYear);
653 break;
654
655 case 1: /* ddmmyy */
656 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"),
657 dt->wDay, cDateSeparator, dt->wMonth,
658 cDateSeparator, b4Digit?4:2, wYear);
659 break;
660
661 case 2: /* yymmdd */
662 return _stprintf(lpDate, _T("%0*d%c%02d%c%02d"),
663 b4Digit?4:2, wYear, cDateSeparator,
664 dt->wMonth, cDateSeparator, dt->wDay);
665 break;
666 }
667 }
668
669 INT
670 FormatTime(TCHAR *lpTime, LPSYSTEMTIME dt)
671 {
672 /* Format Time */
673 switch (nTimeFormat)
674 {
675 case 0: /* 12 hour format */
676 default:
677 return _stprintf(lpTime,_T("%02d%c%02u %cM"),
678 (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)),
679 cTimeSeparator,
680 dt->wMinute, (dt->wHour <= 11 ? _T('A') : _T('P')));
681 break;
682
683 case 1: /* 24 hour format */
684 return _stprintf(lpTime, _T("%02d%c%02u"),
685 dt->wHour, cTimeSeparator, dt->wMinute);
686 break;
687 }
688 }
689
690
691 static VOID
692 GetUserDiskFreeSpace(LPCTSTR lpRoot,
693 PULARGE_INTEGER lpFreeSpace)
694 {
695 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx;
696 HINSTANCE hInstance;
697 DWORD dwSecPerCl;
698 DWORD dwBytPerSec;
699 DWORD dwFreeCl;
700 DWORD dwTotCl;
701 ULARGE_INTEGER TotalNumberOfBytes, TotalNumberOfFreeBytes;
702
703 lpFreeSpace->QuadPart = 0;
704
705 hInstance = GetModuleHandle(_T("KERNEL32"));
706 if (hInstance != NULL)
707 {
708 pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance,
709 #ifdef _UNICODE
710 "GetDiskFreeSpaceExW");
711 #else
712 "GetDiskFreeSpaceExA");
713 #endif
714 if (pGetFreeDiskSpaceEx != NULL)
715 {
716 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == TRUE)
717 return;
718 }
719 }
720
721 GetDiskFreeSpace(lpRoot,
722 &dwSecPerCl,
723 &dwBytPerSec,
724 &dwFreeCl,
725 &dwTotCl);
726
727 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl;
728 }
729
730
731 /*
732 * print_summary: prints dir summary
733 * Added by Rob Lake 06/17/98 to compact code
734 * Just copied Tim's Code and patched it a bit
735 */
736 static INT
737 PrintSummary(LPTSTR szPath,
738 ULONG ulFiles,
739 ULONG ulDirs,
740 ULONGLONG u64Bytes,
741 LPDIRSWITCHFLAGS lpFlags,
742 BOOL TotalSummary)
743 {
744 TCHAR szMsg[RC_STRING_MAX_SIZE];
745 TCHAR szBuffer[64];
746 ULARGE_INTEGER uliFree;
747
748 /* Here we check if we didn't find anything */
749 if (!(ulFiles + ulDirs))
750 {
751 if (!lpFlags->bRecursive || (TotalSummary && lpFlags->bRecursive))
752 error_file_not_found();
753 return 1;
754 }
755
756 /* In bare format we don't print results */
757 if (lpFlags->bBareFormat)
758 return 0;
759
760 /* Print recursive specific results */
761
762 /* Take this code offline to fix /S does not print duoble info */
763 if (TotalSummary && lpFlags->bRecursive)
764 {
765 ConvertULargeInteger(u64Bytes, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator);
766 LoadString(CMD_ModuleHandle, STRING_DIR_HELP5, szMsg, RC_STRING_MAX_SIZE);
767 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer);
768 }
769 else
770 {
771 /* Print File Summary */
772 /* Condition to print summary is:
773 If we are not in bare format and if we have results! */
774 ConvertULargeInteger(u64Bytes, szBuffer, 20, lpFlags->bTSeperator);
775 LoadString(CMD_ModuleHandle, STRING_DIR_HELP8, szMsg, RC_STRING_MAX_SIZE);
776 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer);
777 }
778
779 /* Print total directories and freespace */
780 if (!lpFlags->bRecursive || TotalSummary)
781 {
782 GetUserDiskFreeSpace(szPath, &uliFree);
783 ConvertULargeInteger(uliFree.QuadPart, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator);
784 LoadString(CMD_ModuleHandle, STRING_DIR_HELP6, (LPTSTR) szMsg, RC_STRING_MAX_SIZE);
785 DirPrintf(lpFlags, szMsg, ulDirs, szBuffer);
786 }
787
788 return 0;
789 }
790
791 /*
792 * getExt
793 *
794 * Get the extension of a filename
795 */
796 TCHAR* getExt(const TCHAR* file)
797 {
798 static TCHAR *NoExt = _T("");
799 TCHAR* lastdot = _tcsrchr(file, _T('.'));
800 return (lastdot != NULL ? lastdot + 1 : NoExt);
801 }
802
803 /*
804 * getName
805 *
806 * Get the name of the file without extension
807 */
808 static LPTSTR
809 getName(const TCHAR* file, TCHAR * dest)
810 {
811 INT_PTR iLen;
812 LPTSTR end;
813
814 /* Check for "." and ".." folders */
815 if ((_tcscmp(file, _T(".")) == 0) ||
816 (_tcscmp(file, _T("..")) == 0))
817 {
818 _tcscpy(dest,file);
819 return dest;
820 }
821
822 end = _tcsrchr(file, _T('.'));
823 if (!end)
824 iLen = _tcslen(file);
825 else
826 iLen = (end - file);
827
828 _tcsncpy(dest, file, iLen);
829 *(dest + iLen) = _T('\0');
830
831 return dest;
832 }
833
834
835 /*
836 * DirPrintNewList
837 *
838 * The function that prints in new style
839 */
840 static VOID
841 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles[], /* [IN]Files' Info */
842 DWORD dwCount, /* [IN] The quantity of files */
843 TCHAR *szCurPath, /* [IN] Full path of current directory */
844 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
845 {
846 DWORD i;
847 TCHAR szSize[30];
848 TCHAR szShortName[15];
849 TCHAR szDate[20];
850 TCHAR szTime[20];
851 INT iSizeFormat;
852 ULARGE_INTEGER u64FileSize;
853
854 for (i = 0; i < dwCount && !bCtrlBreak; i++)
855 {
856 /* Calculate size */
857 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
858 {
859 /* Junction */
860 iSizeFormat = -14;
861 _tcscpy(szSize, _T("<JUNCTION>"));
862 }
863 else if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
864 {
865 /* Directory */
866 iSizeFormat = -14;
867 _tcscpy(szSize, _T("<DIR>"));
868 }
869 else
870 {
871 /* File */
872 iSizeFormat = 14;
873 u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh;
874 u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow;
875 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeperator);
876 }
877
878 /* Calculate short name */
879 szShortName[0] = _T('\0');
880 if (lpFlags->bShortName)
881 _stprintf(szShortName, _T(" %-12s"), ptrFiles[i]->cAlternateFileName);
882
883 /* Format date and time */
884 DirPrintFileDateTime(szDate, szTime, ptrFiles[i], lpFlags);
885
886 /* Print the line */
887 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s\n"),
888 szDate,
889 szTime,
890 iSizeFormat,
891 szSize,
892 szShortName,
893 ptrFiles[i]->cFileName);
894 }
895 }
896
897
898 /*
899 * DirPrintWideList
900 *
901 * The function that prints in wide list
902 */
903 static VOID
904 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
905 DWORD dwCount, /* [IN] The quantity of files */
906 TCHAR *szCurPath, /* [IN] Full path of current directory */
907 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
908 {
909 SHORT iScreenWidth;
910 USHORT iColumns;
911 USHORT iLines;
912 UINT_PTR iLongestName;
913 TCHAR szTempFname[MAX_PATH];
914 DWORD i;
915 DWORD j;
916 DWORD temp;
917
918 /* Calculate longest name */
919 iLongestName = 1;
920 for (i = 0; i < dwCount; i++)
921 {
922 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
923 {
924 /* Directories need 2 additinal characters for brackets */
925 if ((_tcslen(ptrFiles[i]->cFileName) + 2) > iLongestName)
926 iLongestName = _tcslen(ptrFiles[i]->cFileName) + 2;
927 }
928 else
929 {
930 if (_tcslen(ptrFiles[i]->cFileName) > iLongestName)
931 iLongestName = _tcslen(ptrFiles[i]->cFileName);
932 }
933 }
934
935 /* Count the highest number of columns */
936 GetScreenSize(&iScreenWidth, 0);
937 iColumns = (USHORT)(iScreenWidth / iLongestName);
938
939 /* Check if there is enough space for spaces between names */
940 if (((iLongestName * iColumns) + iColumns) >= (UINT)iScreenWidth)
941 iColumns --;
942
943 /* A last check at iColumns to avoid division by zero */
944 if (!iColumns) iColumns = 1;
945
946 /* Calculate the lines that will be printed */
947 iLines = (USHORT)((dwCount + iColumns - 1) / iColumns);
948
949 for (i = 0; i < iLines && !bCtrlBreak; i++)
950 {
951 for (j = 0; j < iColumns; j++)
952 {
953 if (lpFlags->bWideListColSort)
954 {
955 /* Print Column sorted */
956 temp = (j * iLines) + i;
957 }
958 else
959 {
960 /* Print Line sorted */
961 temp = (i * iColumns) + j;
962 }
963
964 if (temp >= dwCount) break;
965
966 if (ptrFiles[temp]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
967 _stprintf(szTempFname, _T("[%s]"), ptrFiles[temp]->cFileName);
968 else
969 _stprintf(szTempFname, _T("%s"), ptrFiles[temp]->cFileName);
970
971 DirPrintf(lpFlags, _T("%-*s"), iLongestName + 1, szTempFname);
972 }
973
974 /* Add a new line after the last item in the column */
975 DirPrintf(lpFlags, _T("\n"));
976 }
977 }
978
979
980 /*
981 * DirPrintOldList
982 *
983 * The function that prints in old style
984 */
985 static VOID
986 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
987 DWORD dwCount, /* [IN] The quantity of files */
988 TCHAR * szCurPath, /* [IN] Full path of current directory */
989 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
990 {
991 DWORD i; /* An indexer for "for"s */
992 TCHAR szName[10]; /* The name of file */
993 TCHAR szExt[5]; /* The extension of file */
994 TCHAR szDate[30],szTime[30]; /* Used to format time and date */
995 TCHAR szSize[30]; /* The size of file */
996 int iSizeFormat; /* The format of size field */
997 ULARGE_INTEGER u64FileSize; /* The file size */
998
999 for (i = 0; i < dwCount && !bCtrlBreak; i++)
1000 {
1001 /* Broke 8.3 format */
1002 if (*ptrFiles[i]->cAlternateFileName )
1003 {
1004 /* If the file is long named then we read the alter name */
1005 getName( ptrFiles[i]->cAlternateFileName, szName);
1006 _tcscpy(szExt, getExt( ptrFiles[i]->cAlternateFileName));
1007 }
1008 else
1009 {
1010 /* If the file is not long name we read its original name */
1011 getName( ptrFiles[i]->cFileName, szName);
1012 _tcscpy(szExt, getExt( ptrFiles[i]->cFileName));
1013 }
1014
1015 /* Calculate size */
1016 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1017 {
1018 /* Directory, no size it's a directory*/
1019 iSizeFormat = -17;
1020 _tcscpy(szSize, _T("<DIR>"));
1021 }
1022 else
1023 {
1024 /* File */
1025 iSizeFormat = 17;
1026 u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh;
1027 u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow;
1028 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeperator);
1029 }
1030
1031 /* Format date and time */
1032 DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags);
1033
1034 /* Print the line */
1035 DirPrintf(lpFlags, _T("%-8s %-3s %*s %s %s\n"),
1036 szName, /* The file's 8.3 name */
1037 szExt, /* The file's 8.3 extension */
1038 iSizeFormat, /* print format for size column */
1039 szSize, /* The size of file or "<DIR>" for dirs */
1040 szDate, /* The date of file/dir */
1041 szTime); /* The time of file/dir */
1042 }
1043 }
1044
1045 /*
1046 * DirPrintBareList
1047 *
1048 * The function that prints in bare format
1049 */
1050 static VOID
1051 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1052 DWORD dwCount, /* [IN] The number of files */
1053 LPTSTR lpCurPath, /* [IN] Full path of current directory */
1054 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1055 {
1056 DWORD i;
1057
1058 for (i = 0; i < dwCount && !bCtrlBreak; i++)
1059 {
1060 if ((_tcscmp(ptrFiles[i]->cFileName, _T(".")) == 0) ||
1061 (_tcscmp(ptrFiles[i]->cFileName, _T("..")) == 0))
1062 {
1063 /* at bare format we don't print "." and ".." folder */
1064 continue;
1065 }
1066 if (lpFlags->bRecursive)
1067 {
1068 /* at recursive mode we print full path of file */
1069 DirPrintf(lpFlags, _T("%s\\%s\n"), lpCurPath, ptrFiles[i]->cFileName);
1070 }
1071 else
1072 {
1073 /* if we are not in recursive mode we print the file names */
1074 DirPrintf(lpFlags, _T("%s\n"), ptrFiles[i]->cFileName);
1075 }
1076 }
1077 }
1078
1079
1080 /*
1081 * DirPrintFiles
1082 *
1083 * The functions that prints the files list
1084 */
1085 static VOID
1086 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1087 DWORD dwCount, /* [IN] The quantity of files */
1088 TCHAR *szCurPath, /* [IN] Full path of current directory */
1089 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1090 {
1091 TCHAR szMsg[RC_STRING_MAX_SIZE];
1092 TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */
1093
1094 /* Print trailing backslash for root directory of drive */
1095 _tcscpy(szTemp, szCurPath);
1096 if (_tcslen(szTemp) == 2 && szTemp[1] == _T(':'))
1097 _tcscat(szTemp, _T("\\"));
1098
1099 /* Condition to print header:
1100 We are not printing in bare format
1101 and if we are in recursive mode... we must have results */
1102 if (!(lpFlags->bBareFormat ) && !((lpFlags->bRecursive) && (dwCount <= 0)))
1103 {
1104 LoadString(CMD_ModuleHandle, STRING_DIR_HELP7, szMsg, RC_STRING_MAX_SIZE);
1105 if (DirPrintf(lpFlags, szMsg, szTemp))
1106 return;
1107 }
1108
1109 if (lpFlags->bBareFormat)
1110 {
1111 /* Bare format */
1112 DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags);
1113 }
1114 else if (lpFlags->bShortName)
1115 {
1116 /* New list style / Short names */
1117 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1118 }
1119 else if (lpFlags->bWideListColSort || lpFlags->bWideList)
1120 {
1121 /* Wide list */
1122 DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags);
1123 }
1124 else if (lpFlags->bNewLongList )
1125 {
1126 /* New list style*/
1127 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1128 }
1129 else
1130 {
1131 /* If nothing is selected old list is the default */
1132 DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags);
1133 }
1134 }
1135
1136 /*
1137 * CompareFiles
1138 *
1139 * Compares 2 files based on the order criteria
1140 */
1141 static BOOL
1142 CompareFiles(LPWIN32_FIND_DATA lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1143 LPWIN32_FIND_DATA lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1144 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */
1145 {
1146 ULARGE_INTEGER u64File1;
1147 ULARGE_INTEGER u64File2;
1148 int i;
1149 long iComp = 0; /* The comparison result */
1150
1151 /* Calculate critiries by order given from user */
1152 for (i = 0;i < lpFlags->stOrderBy.sCriteriaCount;i++)
1153 {
1154
1155 /* Calculate criteria */
1156 switch(lpFlags->stOrderBy.eCriteria[i])
1157 {
1158 case ORDER_SIZE: /* Order by size /o:s */
1159 /* concat the 32bit integers to a 64bit */
1160 u64File1.LowPart = lpFile1->nFileSizeLow;
1161 u64File1.HighPart = lpFile1->nFileSizeHigh;
1162 u64File2.LowPart = lpFile2->nFileSizeLow;
1163 u64File2.HighPart = lpFile2->nFileSizeHigh;
1164
1165 /* In case that differnce is too big for a long */
1166 if (u64File1.QuadPart < u64File2.QuadPart)
1167 iComp = -1;
1168 else if (u64File1.QuadPart > u64File2.QuadPart)
1169 iComp = 1;
1170 else
1171 iComp = 0;
1172 break;
1173
1174 case ORDER_DIRECTORY: /* Order by directory attribute /o:g */
1175 iComp = ((lpFile2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)-
1176 (lpFile1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1177 break;
1178
1179 case ORDER_EXTENSION: /* Order by extension name /o:e */
1180 iComp = _tcsicmp(getExt(lpFile1->cFileName),getExt(lpFile2->cFileName));
1181 break;
1182
1183 case ORDER_NAME: /* Order by filename /o:n */
1184 iComp = _tcsicmp(lpFile1->cFileName, lpFile2->cFileName);
1185 break;
1186
1187 case ORDER_TIME: /* Order by file's time /o:t */
1188 /* We compare files based on the time field selected by /t */
1189 switch(lpFlags->stTimeField.eTimeField)
1190 {
1191 case TF_CREATIONDATE:
1192 /* concat the 32bit integers to a 64bit */
1193 u64File1.LowPart = lpFile1->ftCreationTime.dwLowDateTime;
1194 u64File1.HighPart = lpFile1->ftCreationTime.dwHighDateTime ;
1195 u64File2.LowPart = lpFile2->ftCreationTime.dwLowDateTime;
1196 u64File2.HighPart = lpFile2->ftCreationTime.dwHighDateTime ;
1197 break;
1198 case TF_LASTACCESSEDDATE :
1199 /* concat the 32bit integers to a 64bit */
1200 u64File1.LowPart = lpFile1->ftLastAccessTime.dwLowDateTime;
1201 u64File1.HighPart = lpFile1->ftLastAccessTime.dwHighDateTime ;
1202 u64File2.LowPart = lpFile2->ftLastAccessTime.dwLowDateTime;
1203 u64File2.HighPart = lpFile2->ftLastAccessTime.dwHighDateTime ;
1204 break;
1205 case TF_MODIFIEDDATE:
1206 /* concat the 32bit integers to a 64bit */
1207 u64File1.LowPart = lpFile1->ftLastWriteTime.dwLowDateTime;
1208 u64File1.HighPart = lpFile1->ftLastWriteTime.dwHighDateTime ;
1209 u64File2.LowPart = lpFile2->ftLastWriteTime.dwLowDateTime;
1210 u64File2.HighPart = lpFile2->ftLastWriteTime.dwHighDateTime ;
1211 break;
1212 }
1213
1214 /* In case that differnce is too big for a long */
1215 if (u64File1.QuadPart < u64File2.QuadPart)
1216 iComp = -1;
1217 else if (u64File1.QuadPart > u64File2.QuadPart)
1218 iComp = 1;
1219 else
1220 iComp = 0;
1221 break;
1222 }
1223
1224 /* Reverse if desired */
1225 if (lpFlags->stOrderBy.bCriteriaRev[i])
1226 iComp *= -1;
1227
1228 /* If that criteria was enough for distinguishing
1229 the files/dirs,there is no need to calculate the others*/
1230 if (iComp != 0) break;
1231 }
1232
1233 /* Translate the value of iComp to boolean */
1234 return iComp > 0;
1235 }
1236
1237 /*
1238 * QsortFiles
1239 *
1240 * Sort files by the order criterias using quicksort method
1241 */
1242 static VOID
1243 QsortFiles(LPWIN32_FIND_DATA ptrArray[], /* [IN/OUT] The array with file info pointers */
1244 int i, /* [IN] The index of first item in array */
1245 int j, /* [IN] The index to last item in array */
1246 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */
1247 {
1248 LPWIN32_FIND_DATA lpTemp; /* A temporary pointer */
1249 BOOL Way;
1250
1251 if (i < j)
1252 {
1253 int First = i, Last = j, Temp;
1254 Way = TRUE;
1255 while (i != j)
1256 {
1257 if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags))
1258 {
1259 /* Swap the pointers of the array */
1260 lpTemp = ptrArray[i];
1261 ptrArray[i]= ptrArray[j];
1262 ptrArray[j] = lpTemp;
1263
1264 /* Swap the indexes for inverting sorting */
1265 Temp = i;
1266 i = j;
1267 j =Temp;
1268
1269 Way = !Way;
1270 }
1271
1272 j += (!Way - Way);
1273 }
1274
1275 QsortFiles(ptrArray,First, i-1, lpFlags);
1276 QsortFiles(ptrArray,i+1,Last, lpFlags);
1277 }
1278 }
1279
1280 /*
1281 * DirList
1282 *
1283 * The functions that does everything except for printing results
1284 */
1285 static INT
1286 DirList(LPTSTR szPath, /* [IN] The path that dir starts */
1287 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */
1288 {
1289 BOOL fPoint; /* If szPath is a file with extension fPoint will be True*/
1290 HANDLE hSearch; /* The handle of the search */
1291 HANDLE hRecSearch; /* The handle for searching recursivly */
1292 WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */
1293 LPWIN32_FIND_DATA * ptrFileArray; /* An array of pointers with all the files */
1294 PDIRFINDLISTNODE ptrStartNode; /* The pointer to the first node */
1295 PDIRFINDLISTNODE ptrNextNode; /* A pointer used for relatives refernces */
1296 TCHAR szFullPath[MAX_PATH]; /* The full path that we are listing with trailing \ */
1297 TCHAR szSubPath[MAX_PATH];
1298 LPTSTR pszFilePart;
1299 DWORD dwCount; /* A counter of files found in directory */
1300 DWORD dwCountFiles; /* Counter for files */
1301 DWORD dwCountDirs; /* Counter for directories */
1302 ULONGLONG u64CountBytes; /* Counter for bytes */
1303 ULARGE_INTEGER u64Temp; /* A temporary counter */
1304
1305 /* Initialize Variables */
1306 ptrStartNode = NULL;
1307 ptrNextNode = NULL;
1308 dwCount = 0;
1309 dwCountFiles = 0;
1310 dwCountDirs = 0;
1311 u64CountBytes = 0;
1312 fPoint= FALSE;
1313
1314 /* Create szFullPath */
1315 if (GetFullPathName(szPath, sizeof(szFullPath) / sizeof(TCHAR), szFullPath, &pszFilePart) == 0)
1316 {
1317 _tcscpy (szFullPath, szPath);
1318 pszFilePart = NULL;
1319 }
1320
1321 /* If no wildcard or file was specified and this is a directory, then
1322 display all files in it */
1323 if (pszFilePart == NULL || IsExistingDirectory(szFullPath))
1324 {
1325 pszFilePart = &szFullPath[_tcslen(szFullPath)];
1326 if (pszFilePart[-1] != _T('\\'))
1327 *pszFilePart++ = _T('\\');
1328 _tcscpy(pszFilePart, _T("*"));
1329 }
1330
1331 /* Prepare the linked list, first node is allocated */
1332 ptrStartNode = cmd_alloc(sizeof(DIRFINDLISTNODE));
1333 if (ptrStartNode == NULL)
1334 {
1335 WARN("DEBUG: Cannot allocate memory for ptrStartNode!\n");
1336 return 1; /* Error cannot allocate memory for 1st object */
1337 }
1338 ptrNextNode = ptrStartNode;
1339
1340 /*Checking ir szPath is a File with/wout extension*/
1341 if (szPath[_tcslen(szPath) - 1] == _T('.'))
1342 fPoint= TRUE;
1343
1344 /* Collect the results for the current folder */
1345 hSearch = FindFirstFile(szFullPath, &wfdFileInfo);
1346 if (hSearch != INVALID_HANDLE_VALUE)
1347 {
1348 do
1349 {
1350 /*If retrieved FileName has extension,and szPath doesnt have extension then JUMP the retrieved FileName*/
1351 if (_tcschr(wfdFileInfo.cFileName,_T('.'))&&(fPoint==TRUE))
1352 {
1353 continue;
1354 /* Here we filter all the specified attributes */
1355 }
1356 else if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask )
1357 == (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal ))
1358 {
1359 ptrNextNode->ptrNext = cmd_alloc(sizeof(DIRFINDLISTNODE));
1360 if (ptrNextNode->ptrNext == NULL)
1361 {
1362 WARN("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n");
1363 while (ptrStartNode)
1364 {
1365 ptrNextNode = ptrStartNode->ptrNext;
1366 cmd_free(ptrStartNode);
1367 ptrStartNode = ptrNextNode;
1368 dwCount--;
1369 }
1370 FindClose(hSearch);
1371 return 1;
1372 }
1373
1374 /* If cmd_alloc fails we go to next file in hope it works,
1375 without braking the linked list! */
1376 if (ptrNextNode->ptrNext)
1377 {
1378 /* Copy the info of search at linked list */
1379 memcpy(&ptrNextNode->ptrNext->stFindInfo,
1380 &wfdFileInfo,
1381 sizeof(WIN32_FIND_DATA));
1382
1383 /* If lower case is selected do it here */
1384 if (lpFlags->bLowerCase)
1385 {
1386 _tcslwr(ptrNextNode->ptrNext->stFindInfo.cAlternateFileName);
1387 _tcslwr(ptrNextNode->ptrNext->stFindInfo.cFileName);
1388 }
1389
1390 /* Continue at next node at linked list */
1391 ptrNextNode = ptrNextNode->ptrNext;
1392 dwCount ++;
1393
1394 /* Grab statistics */
1395 if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1396 {
1397 /* Directory */
1398 dwCountDirs++;
1399 }
1400 else
1401 {
1402 /* File */
1403 dwCountFiles++;
1404 u64Temp.HighPart = wfdFileInfo.nFileSizeHigh;
1405 u64Temp.LowPart = wfdFileInfo.nFileSizeLow;
1406 u64CountBytes += u64Temp.QuadPart;
1407 }
1408 }
1409 }
1410 } while (FindNextFile(hSearch, &wfdFileInfo));
1411 FindClose(hSearch);
1412 }
1413
1414 /* Terminate list */
1415 ptrNextNode->ptrNext = NULL;
1416
1417 /* Calculate and allocate space need for making an array of pointers */
1418 ptrFileArray = cmd_alloc(sizeof(LPWIN32_FIND_DATA) * dwCount);
1419 if (ptrFileArray == NULL)
1420 {
1421 WARN("DEBUG: Cannot allocate memory for ptrFileArray!\n");
1422 while (ptrStartNode)
1423 {
1424 ptrNextNode = ptrStartNode->ptrNext;
1425 cmd_free(ptrStartNode);
1426 ptrStartNode = ptrNextNode;
1427 dwCount --;
1428 }
1429 return 1;
1430 }
1431
1432 /*
1433 * Create an array of pointers from the linked list
1434 * this will be used to sort and print data, rather than the list
1435 */
1436 ptrNextNode = ptrStartNode;
1437 dwCount = 0;
1438 while (ptrNextNode->ptrNext)
1439 {
1440 *(ptrFileArray + dwCount) = &ptrNextNode->ptrNext->stFindInfo;
1441 ptrNextNode = ptrNextNode->ptrNext;
1442 dwCount++;
1443 }
1444
1445 /* Sort Data if requested*/
1446 if (lpFlags->stOrderBy.sCriteriaCount > 0)
1447 QsortFiles(ptrFileArray, 0, dwCount-1, lpFlags);
1448
1449 /* Print Data */
1450 pszFilePart[-1] = _T('\0'); /* truncate to directory name only */
1451 DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags);
1452 pszFilePart[-1] = _T('\\');
1453
1454 if (lpFlags->bRecursive)
1455 {
1456 PrintSummary(szFullPath,
1457 dwCountFiles,
1458 dwCountDirs,
1459 u64CountBytes,
1460 lpFlags,
1461 FALSE);
1462 }
1463
1464 /* Free array */
1465 cmd_free(ptrFileArray);
1466 /* Free linked list */
1467 while (ptrStartNode)
1468 {
1469 ptrNextNode = ptrStartNode->ptrNext;
1470 cmd_free(ptrStartNode);
1471 ptrStartNode = ptrNextNode;
1472 dwCount --;
1473 }
1474
1475 if (CheckCtrlBreak(BREAK_INPUT))
1476 return 1;
1477
1478
1479 /* Add statistics to recursive statistics*/
1480 recurse_dir_cnt += dwCountDirs;
1481 recurse_file_cnt += dwCountFiles;
1482 recurse_bytes += u64CountBytes;
1483
1484 /* Do the recursive job if requested
1485 the recursive is be done on ALL(indepent of their attribs)
1486 directoried of the current one.*/
1487 if (lpFlags->bRecursive)
1488 {
1489 /* The new search is involving any *.* file */
1490 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR));
1491 _tcscpy(&szSubPath[pszFilePart - szFullPath], _T("*.*"));
1492
1493 hRecSearch = FindFirstFile (szSubPath, &wfdFileInfo);
1494 if (hRecSearch != INVALID_HANDLE_VALUE)
1495 {
1496 do
1497 {
1498 /* We search for directories other than "." and ".." */
1499 if ((_tcsicmp(wfdFileInfo.cFileName, _T(".")) != 0) &&
1500 (_tcsicmp(wfdFileInfo.cFileName, _T("..")) != 0 ) &&
1501 (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1502 {
1503 /* Concat the path and the directory to do recursive */
1504 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR));
1505 _tcscpy(&szSubPath[pszFilePart - szFullPath], wfdFileInfo.cFileName);
1506 _tcscat(szSubPath, _T("\\"));
1507 _tcscat(szSubPath, pszFilePart);
1508
1509 /* We do the same for the folder */
1510 if (DirList(szSubPath, lpFlags) != 0)
1511 {
1512 FindClose(hRecSearch);
1513 return 1;
1514 }
1515 }
1516 } while(FindNextFile(hRecSearch, &wfdFileInfo));
1517 }
1518 FindClose(hRecSearch);
1519 }
1520
1521 return 0;
1522 }
1523
1524 /*
1525 * dir
1526 *
1527 * internal dir command
1528 */
1529 INT
1530 CommandDir(LPTSTR rest)
1531 {
1532 TCHAR dircmd[256]; /* A variable to store the DIRCMD enviroment variable */
1533 TCHAR path[MAX_PATH];
1534 TCHAR prev_volume[MAX_PATH];
1535 LPTSTR* params = NULL;
1536 LPTSTR pszFilePart;
1537 INT entries = 0;
1538 UINT loop = 0;
1539 DIRSWITCHFLAGS stFlags;
1540 INT ret = 1;
1541 BOOL ChangedVolume;
1542
1543 /* Initialize Switch Flags < Default switches are setted here!> */
1544 stFlags.b4Digit = TRUE;
1545 stFlags.bBareFormat = FALSE;
1546 stFlags.bLowerCase = FALSE;
1547 stFlags.bNewLongList = TRUE;
1548 stFlags.bPause = FALSE;
1549 stFlags.bRecursive = FALSE;
1550 stFlags.bShortName = FALSE;
1551 stFlags.bTSeperator = TRUE;
1552 stFlags.bUser = FALSE;
1553 stFlags.bWideList = FALSE;
1554 stFlags.bWideListColSort = FALSE;
1555 stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE;
1556 stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1557 stFlags.stAttribs.dwAttribVal = 0L;
1558 stFlags.stOrderBy.sCriteriaCount = 0;
1559
1560 nErrorLevel = 0;
1561
1562 /* read the parameters from the DIRCMD environment variable */
1563 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256))
1564 {
1565 if (!DirReadParam(dircmd, &params, &entries, &stFlags))
1566 {
1567 nErrorLevel = 1;
1568 goto cleanup;
1569 }
1570 }
1571
1572 /* read the parameters */
1573 if (!DirReadParam(rest, &params, &entries, &stFlags) || CheckCtrlBreak(BREAK_INPUT))
1574 {
1575 nErrorLevel = 1;
1576 goto cleanup;
1577 }
1578
1579 /* default to current directory */
1580 if (entries == 0)
1581 {
1582 if (!add_entry(&entries, &params, _T("*")))
1583 {
1584 nErrorLevel = 1;
1585 goto cleanup;
1586 }
1587 }
1588
1589 prev_volume[0] = _T('\0');
1590
1591 /* Reset paging state */
1592 if (stFlags.bPause)
1593 ConOutPrintfPaging(TRUE, _T(""));
1594
1595 for(loop = 0; loop < (UINT)entries; loop++)
1596 {
1597 if (CheckCtrlBreak(BREAK_INPUT))
1598 {
1599 nErrorLevel = 1;
1600 goto cleanup;
1601 }
1602
1603 recurse_dir_cnt = 0L;
1604 recurse_file_cnt = 0L;
1605 recurse_bytes = 0;
1606
1607 /* <Debug :>
1608 Uncomment this to show the final state of switch flags*/
1609 {
1610 int i;
1611 TRACE("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal );
1612 TRACE("(B) Bare format : %i\n", stFlags.bBareFormat );
1613 TRACE("(C) Thousand : %i\n", stFlags.bTSeperator );
1614 TRACE("(W) Wide list : %i\n", stFlags.bWideList );
1615 TRACE("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort );
1616 TRACE("(L) Lowercase : %i\n", stFlags.bLowerCase );
1617 TRACE("(N) New : %i\n", stFlags.bNewLongList );
1618 TRACE("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount );
1619 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++)
1620 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i] );
1621 TRACE("(P) Pause : %i\n", stFlags.bPause );
1622 TRACE("(Q) Owner : %i\n", stFlags.bUser );
1623 TRACE("(S) Recursive : %i\n", stFlags.bRecursive );
1624 TRACE("(T) Time field : %i\n", stFlags.stTimeField.eTimeField );
1625 TRACE("(X) Short names : %i\n", stFlags.bShortName );
1626 TRACE("Parameter : %s\n", debugstr_aw(params[loop]) );
1627 }
1628
1629 /* Print the drive header if the volume changed */
1630 ChangedVolume = TRUE;
1631
1632 if (!stFlags.bBareFormat &&
1633 GetVolumePathName(params[loop], path, sizeof(path) / sizeof(TCHAR)))
1634 {
1635 if (!_tcscmp(path, prev_volume))
1636 ChangedVolume = FALSE;
1637 else
1638 _tcscpy(prev_volume, path);
1639 }
1640 else if (GetFullPathName(params[loop], sizeof(path) / sizeof(TCHAR), path, &pszFilePart) != 0)
1641 {
1642 if (pszFilePart != NULL)
1643 *pszFilePart = _T('\0');
1644 }
1645 else
1646 {
1647 _tcscpy(path, params[loop]);
1648 }
1649
1650 if (ChangedVolume && !stFlags.bBareFormat)
1651 {
1652 if (!PrintDirectoryHeader (params[loop], &stFlags))
1653 {
1654 nErrorLevel = 1;
1655 goto cleanup;
1656 }
1657 }
1658
1659 /* do the actual dir */
1660 if (DirList (params[loop], &stFlags))
1661 {
1662 nErrorLevel = 1;
1663 goto cleanup;
1664 }
1665
1666 /* print the footer */
1667 PrintSummary(path,
1668 recurse_file_cnt,
1669 recurse_dir_cnt,
1670 recurse_bytes,
1671 &stFlags,
1672 TRUE);
1673 }
1674
1675 ret = 0;
1676
1677 cleanup:
1678 freep(params);
1679
1680 return ret;
1681 }
1682
1683 #endif
1684
1685 /* EOF */