xref: /reactos/base/shell/cmd/dir.c (revision 4019caae)
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 %cM"),
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 */
1697