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