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