From 1adf22c4f44c9df7594d27777657dde89816bca0 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Tue, 8 Mar 2005 15:08:01 +0000 Subject: [PATCH] 27-Feb-2005 (Konstantinos Paliouras ) Implemented all the switches that were missing, and made the ros dir very similar to windows dir. Major part of the code is rewritten. /p is removed, to be rewriten in the main cmd code. svn path=/trunk/; revision=13877 --- reactos/subsys/system/cmd/dir.c | 1775 ++++++++++++++++++--------- reactos/subsys/system/cmd/precomp.h | 1 + 2 files changed, 1192 insertions(+), 584 deletions(-) diff --git a/reactos/subsys/system/cmd/dir.c b/reactos/subsys/system/cmd/dir.c index 31e8b4e8c1e..5a2d915deb5 100644 --- a/reactos/subsys/system/cmd/dir.c +++ b/reactos/subsys/system/cmd/dir.c @@ -119,6 +119,13 @@ * * 30-Apr-2004 (Filip Navara ) * Fix /w to print long names. + * + * 27-Feb-2005 (Konstantinos Paliouras ) + * Implemented all the switches that were missing, and made + * the ros dir very similar to windows dir. Major part of + * the code is rewritten. /p is removed, to be rewriten in + * the main cmd code. + * */ #include "precomp.h" @@ -126,20 +133,61 @@ #ifdef INCLUDE_CMD_DIR -/* flag definitions */ -enum + +/* Time Field enumeration */ +enum ETimeField { - DIR_RECURSE = 0x0001, - DIR_PAGE = 0x0002, - DIR_WIDE = 0x0004, /* Rob Lake */ - DIR_BARE = 0x0008, /* Rob Lake */ - DIR_ALL = 0x0010, /* Rob Lake */ - DIR_LWR = 0x0020, /* Rob Lake */ - DIR_SORT = 0x0040, /* /O sort */ - DIR_NEW = 0x0080, /* /N new style */ - DIR_FOUR = 0x0100 /* /4 four digit year */ + TF_CREATIONDATE = 0, + TF_MODIFIEDDATE = 1, + TF_LASTACCESSEDDATE = 2 }; +/* Ordered by enumeration */ +enum EOrderBy +{ + ORDER_NAME = 0, + ORDER_SIZE = 1, + ORDER_DIRECTORY = 2, + ORDER_EXTENSION = 3, + ORDER_TIME = 4 +}; + +/* The struct for holding the switches */ +typedef struct TDirSwitchesFlags +{ + BOOL bBareFormat; /* Bare Format */ + BOOL bTSeperator; /* Thousands seperator */ + BOOL bWideList; /* Wide list format */ + BOOL bWideListColSort; /* Wide list format but sorted by column */ + BOOL bLowerCase; /* Uses lower case */ + BOOL bNewLongList; /* New long list */ + BOOL bPause; /* Pause per page */ + BOOL bUser; /* Displays the owner of file */ + BOOL bRecursive; /* Displays files in specified directory and all sub */ + BOOL bShortName; /* Displays the sort name of files if exist */ + BOOL b4Digit; /* Four digit year */ + struct + { + DWORD dwAttribVal; /* The desired state of attribute */ + DWORD dwAttribMask; /* Which attributes to check */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stAttribs; /* Displays files with this attributes only */ + struct + { + enum EOrderBy eCriteria[3]; /* Criterias used to order by */ + BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */ + short sCriteriaCount; /* The quantity of criterias */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stOrderBy; /* Ordered by criterias */ + struct + { + enum ETimeField eTimeField; /* The time field that will be used for */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stTimeField; /* The time field to display or use for sorting */ +}DIRSWITCHFLAGS, * LPDIRSWITCHFLAGS; typedef BOOL STDCALL (*PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); @@ -158,177 +206,382 @@ static ULARGE_INTEGER recurse_bytes; * displays help screen for dir * Rob Lake */ -static VOID Help (VOID) +static VOID + DirHelp (VOID) { - ConOutPuts(_T("Displays a list of files and subdirectories in a directory.\n" - "\n" - "DIR [drive:][path][filename] [/A] [/B] [/L] [/N] [/S] [/P] [/W] [/4]\n" - "\n" - " [drive:][path][filename]\n" - " Specifies drive, directory, and/or files to list.\n" - "\n" - " /A Displays files with HIDDEN SYSTEM attributes\n" - " default is ARCHIVE and READ ONLY\n" - " /B Uses bare format (no heading information or summary).\n" - " /L Uses lowercase.\n" - " /N New long list format where filenames are on the far right.\n" - " /S Displays files in specified directory and all subdirectories\n" - " /P Pauses after each screen full\n" - " /W Prints in wide format\n" - " /4 Display four digit years.\n" - "\n" - "Switches may be present in the DIRCMD environment variable. Use\n" - "of the - (hyphen) can turn off defined swtiches. Ex. /-W would\n" - "turn off printing in wide format.\n" + ConOutPuts(_T( + "DIR [drive:][path][filename] [/A[[:]attributes]] [/B] [/C] [/D] [/L] [/N]\n" + " [/O[[:]sortorder]] [/P] [/Q] [/S] [/T[[:]timefield]] [/W] [/X] [/4]\n" + "\n" + " [drive:][path][filename]\n" + " Specifies drive, directory, and/or files to list.\n" + "\n" + " /A Displays files with specified attributes.\n" + " attributes D Directories R Read-only files\n" + " H Hidden files A Files ready for archiving\n" + " S System files - Prefix meaning not\n" + " /B Uses bare format (no heading information or summary).\n" + " /C Display the thousand separator in file sizes. This is the\n" + " default. Use /-C to disable display of separator.\n" + " /D Same as wide but files are list sorted by column.\n" + " /L Uses lowercase.\n" + " /N New long list format where filenames are on the far right.\n" + " /O List by files in sorted order.\n" + " sortorder N By name (alphabetic) S By size (smallest first)\n" + " E By extension (alphabetic) D By date/time (oldest first)\n" + " G Group directories first - Prefix to reverse order\n" + " /P Pauses after each screenful of information.\n" + " /Q Display the owner of the file.\n" + " /S Displays files in specified directory and all subdirectories.\n" + " /T Controls which time field displayed or used for sorting\n" + " timefield C Creation\n" + " A Last Access\n" + " W Last Written\n" + " /W Uses wide list format.\n" + " /X This displays the short names generated for non-8dot3 file\n" + " names. The format is that of /N with the short name inserted\n" + " before the long name. If no short name is present, blanks are\n" + " displayed in its place.\n" + " /4 Displays four-digit years\n" + " \n" + "Switches may be preset in the DIRCMD environment variable. Override\n" + "preset switches by prefixing any switch with - (hyphen)--for example, /-W.\n" )); } + /* - * DirReadParam + * DirReadParameters * - * read the parameters from the command line + * Parse the parameters and switches of the command line and exports them */ -static BOOL -DirReadParam (LPTSTR line, LPTSTR *param, LPDWORD lpFlags) +static BOOL +DirReadParam (LPTSTR line, /* [IN] The line with the parameters & switches */ + LPTSTR *param, /* [OUT] The parameters after parsing */ + LPDIRSWITCHFLAGS lpFlags /* [IN/OUT] The flags after calculating switches */ + ) { - INT slash = 0; +TCHAR cCurSwitch; /* The current switch */ +TCHAR cCurChar; /* Current examing character */ +TCHAR cCurUChar; /* Current upper examing character */ +BOOL bNegative; /* Negative switch */ +BOOL bPNegative; /* Negative switch parameter */ +BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */ +LPTSTR ptrLast; /* A pointer to the last character of param */ - if (!line) - return TRUE; + /* Initialize variables; */ + cCurSwitch = _T(' '); + bNegative = FALSE; + bPNegative = FALSE; + bIntoQuotes = FALSE; + /* No parameters yet */ *param = NULL; + ptrLast = NULL; + + /* We suppose that switch parameters + were given to avoid setting them to default + if the switch was not given */ + lpFlags->stAttribs.bParSetted = TRUE; + lpFlags->stOrderBy.bParSetted = TRUE; + lpFlags->stTimeField.bParSetted = TRUE; - /* scan the command line, processing switches */ + /* Main Loop (see README_DIR.txt) */ + /* scan the command line char per char, and we process its char */ while (*line) { - /* process switch */ - if (*line == _T('/') || slash) + /* we save current character as it is and its upper case */ + cCurChar = *line; + cCurUChar = _toupper(*line); + + /* 1st section (see README_DIR.txt) */ + /* When a switch is expecting */ + if (cCurSwitch == _T('/')) { - if (!slash) - line++; - slash = 0; - if (*line == _T('-')) - { - line++; - if (_totupper (*line) == _T('S')) - *lpFlags &= ~DIR_RECURSE; - else if (_totupper (*line) == _T('P')) - *lpFlags &= ~DIR_PAGE; - else if (_totupper (*line) == _T('W')) - *lpFlags &= ~DIR_WIDE; - else if (_totupper (*line) == _T('B')) - *lpFlags &= ~DIR_BARE; - else if (_totupper (*line) == _T('A')) - *lpFlags &= ~DIR_ALL; - else if (_totupper (*line) == _T('L')) - *lpFlags &= ~DIR_LWR; - else if (_totupper (*line) == _T('N')) - *lpFlags &= ~DIR_NEW; - else if (_totupper (*line) == _T('O')) - *lpFlags &= ~DIR_SORT; - else if (_totupper (*line) == _T('4')) - *lpFlags &= ~DIR_FOUR; - else + if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O'))) + { + cCurSwitch = cCurUChar; + switch (cCurUChar) { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; + case _T('A'): + lpFlags->stAttribs.bUnSet = bNegative; + lpFlags->stAttribs.bParSetted = FALSE; + break; + case _T('T'): + lpFlags->stTimeField.bUnSet = bNegative; + lpFlags->stTimeField.bParSetted = FALSE; + break; + case _T('O'): + lpFlags->stOrderBy.bUnSet = bNegative; + lpFlags->stOrderBy.bParSetted = FALSE; + break; } - line++; - continue; } - else + else if (cCurUChar == _T('L')) + lpFlags->bLowerCase = ! bNegative; + else if (cCurUChar == _T('B')) + lpFlags->bBareFormat = ! bNegative; + else if (cCurUChar == _T('C')) + lpFlags->bTSeperator = ! bNegative; + else if (cCurUChar == _T('W')) + lpFlags->bWideList = ! bNegative; + else if (cCurUChar == _T('D')) + lpFlags->bWideListColSort = ! bNegative; + else if (cCurUChar == _T('N')) + lpFlags->bNewLongList = ! bNegative; + else if (cCurUChar == _T('P')) + lpFlags->bPause = ! bNegative; + else if (cCurUChar == _T('Q')) + lpFlags->bUser = ! bNegative; + else if (cCurUChar == _T('S')) + lpFlags->bRecursive = ! bNegative; + else if (cCurUChar == _T('X')) + lpFlags->bShortName = ! bNegative; + else if (cCurChar == _T('4')) + lpFlags->b4Digit = ! bNegative; + else if (cCurChar == _T('?')) { - if (_totupper (*line) == _T('S')) - *lpFlags |= DIR_RECURSE; - else if (_totupper (*line) == _T('P')) - *lpFlags |= DIR_PAGE; - else if (_totupper (*line) == _T('W')) - *lpFlags |= DIR_WIDE; - else if (_totupper (*line) == _T('B')) - *lpFlags |= DIR_BARE; - else if (_totupper (*line) == _T('A')) - *lpFlags |= DIR_ALL; - else if (_totupper (*line) == _T('L')) - *lpFlags |= DIR_LWR; - else if (_totupper (*line) == _T('N')) - *lpFlags |= DIR_NEW; - else if (_totupper (*line) == _T('O')) - *lpFlags |= DIR_SORT; - else if (_totupper (*line) == _T('4')) - *lpFlags |= DIR_FOUR; - else if (*line == _T('?')) - { - Help(); - return FALSE; - } - else - { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; - } - line++; - continue; + DirHelp(); + return FALSE; } - } - - /* process parameter */ - if (!_istspace (*line)) - { - if (*param) + else if (cCurChar == _T('-')) + bNegative = TRUE; + else { - error_too_many_parameters (line); + error_invalid_switch ((TCHAR)_totupper (*line)); return FALSE; } - *param = line; + /* We check if we calculated the negative value and realese the flag */ + if ((cCurChar != _T('-')) && bNegative) + bNegative = FALSE; - /* skip to end of line or next whitespace or next / */ - if (*line != _T('\"')) - { - while (*line && !_istspace (*line) && *line != _T('/')) - line++; + /* if not a,o,t or - option then next parameter is not a switch */ + if ((cCurSwitch == _T('/')) && (!bNegative)) + cCurSwitch = _T(' '); - /* if end of line, return */ - if (!*line) - return TRUE; - } - else - { - /* skip over the initial quote */ - (*param)++; - line++; - - while (*line && *line != _T('"')) - line++; - - if (*line == _T('"')) - *line = 0; - else - return TRUE; - } - - /* if parameter, remember to process it later */ - if (*line == _T('/')) - slash = 1; - - *line++ = 0; - continue; } + /* 2nd section (see README_DIR.txt) */ + /* We are expecting parameter or the unknown */ + else if ((cCurSwitch == _T(' ')) || (cCurSwitch == _T('P'))) + { + if (cCurChar == _T('/')) + cCurSwitch = _T('/'); + /* Process a spacer */ + else if (cCurChar == _T(' ')) + { + if (!bIntoQuotes) + { + cCurSwitch = _T(' '); + if ((*param) && !(ptrLast)) + ptrLast = line; + } + + } + /* Process a quote */ + else if (cCurChar == _T('\"')) + { + bIntoQuotes = !bIntoQuotes; + if (!bIntoQuotes) ptrLast = line; + } + /* Process a character for parameter */ + else + { + if ((cCurSwitch == _T(' ')) && (*param)) + { + error_too_many_parameters(line); + return FALSE; + } + cCurSwitch = _T('P'); + if (!(*param)) + *param = line; + } + + } + /* 3rd section (see README_DIR.txt) */ + /* We are waiting for switch parameters */ + else + { + /* Check if there are no more switch parameters */ + if ((cCurChar == _T('/')) || ( cCurChar == _T(' '))) + { + /* Wrong desicion path, reprocess current character */ + cCurSwitch = cCurChar; + continue; + } + /* Process parameter switch */ + switch(cCurSwitch) + { + case _T('A'): /* Switch parameters for /A (attributes filter) */ + /* Ok a switch parameter was given */ + lpFlags->stAttribs.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* =V= dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurChar == _T('-')) + bPNegative = TRUE; + else if(cCurUChar == _T('D')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY; + } + else if(cCurUChar == _T('R')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY; + } + else if(cCurUChar == _T('H')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN; + } + else if(cCurUChar == _T('A')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE; + } + else if(cCurUChar == _T('S')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM; + } + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + break; + case _T('T'): /* Switch parameters for /T (time field) */ + + /* Ok a switch parameter was given */ + lpFlags->stTimeField.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* =V= dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurUChar == _T('C')) + lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ; + else if(cCurUChar == _T('A')) + lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ; + else if(cCurUChar == _T('W')) + lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ; + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + break; + case _T('O'): /* Switch parameters for /O (order) */ + /* Ok a switch parameter was given */ + lpFlags->stOrderBy.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* <== dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurChar == _T('-')) + bPNegative = TRUE; + else if(cCurUChar == _T('N')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME; + } + else if(cCurUChar == _T('S')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE; + } + else if(cCurUChar == _T('G')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY; + } + else if(cCurUChar == _T('E')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION; + } + else if(cCurUChar == _T('D')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME; + } + + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + + + } + /* We check if we calculated the negative value and realese the flag */ + if ((cCurChar != _T('-')) && bPNegative) + bPNegative = FALSE; + } + line++; } + /* Terminate the parameters */ + if (ptrLast) *ptrLast = 0; - if (slash) + /* Calculate the switches with no switch paramater */ + if (!(lpFlags->stAttribs.bParSetted)) { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; + lpFlags->stAttribs.dwAttribVal = 0L; + lpFlags->stAttribs.dwAttribMask = lpFlags->stAttribs.dwAttribVal; } + if (!(lpFlags->stOrderBy.bParSetted)) + { + lpFlags->stOrderBy.sCriteriaCount = 1; + lpFlags->stOrderBy.eCriteria[0] = ORDER_NAME; + lpFlags->stOrderBy.bCriteriaRev[0] = FALSE; + } + if (!(lpFlags->stOrderBy.bParSetted)) + lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE ; + /* Calculate the unsetted switches (the "-" prefixed)*/ + if (lpFlags->stAttribs.bUnSet) + { + lpFlags->stAttribs.bUnSet = FALSE; + lpFlags->stAttribs.dwAttribVal = 0L; + lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; + } + if (lpFlags->stOrderBy.bUnSet) + { + lpFlags->stOrderBy.bUnSet = FALSE; + lpFlags->stOrderBy.sCriteriaCount = 0; + } + if (lpFlags->stTimeField.bUnSet ) + { + lpFlags->stTimeField.bUnSet = FALSE; + lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE; + } return TRUE; } - /* * ExtendFilespec * @@ -507,9 +760,9 @@ DirParsePathspec (LPTSTR szPathspec, LPTSTR szPath, LPTSTR szFilespec) * incline * * increment our line if paginating, display message at end of screen - */ + *//*Maybe needed in future static BOOL -IncLine (LPINT pLine, DWORD dwFlags) +IncLine (LPINT pLine, LPDIRSWITCHFLAGS lpFlags) { BOOL error; CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo; @@ -518,16 +771,21 @@ IncLine (LPINT pLine, DWORD dwFlags) WindowHeight= lpConsoleScreenBufferInfo.srWindow.Bottom - lpConsoleScreenBufferInfo.srWindow.Top; - if (!WindowHeight) //That prevents bad behave if WindowHeight couln't calc + //That prevents bad behave if WindowHeight couln't calc + if (!WindowHeight) { WindowHeight= 1000000; } - if (!(dwFlags & DIR_PAGE)) + if (!(lpFlags->bPause)) return FALSE; (*pLine)++; - if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight) //Because I don't know if WindowsHeight work under all cases, perhaps then maxy is the right value + // Because I don't know if WindowsHeight + work under all cases, perhaps then maxy + is the right value + + if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight) { *pLine = 0; return (PagePrompt () == PROMPT_BREAK); @@ -535,7 +793,7 @@ IncLine (LPINT pLine, DWORD dwFlags) return FALSE; } - +*/ /* * PrintDirectoryHeader @@ -543,14 +801,14 @@ IncLine (LPINT pLine, DWORD dwFlags) * print the header for the dir command */ static BOOL -PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) +PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, LPDIRSWITCHFLAGS lpFlags) { TCHAR szRootName[MAX_PATH]; TCHAR szVolName[80]; DWORD dwSerialNr; LPTSTR p; - if (dwFlags & DIR_BARE) + if (lpFlags->bBareFormat) return(TRUE); /* build usable root path */ @@ -607,15 +865,10 @@ PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) else ConOutPrintf(_T(" has no label\n")); - if (IncLine(pLine, dwFlags)) - return(FALSE); - /* print the volume serial number if the return was successful */ ConOutPrintf(_T(" Volume Serial Number is %04X-%04X\n"), HIWORD(dwSerialNr), LOWORD(dwSerialNr)); - if (IncLine(pLine, dwFlags)) - return(FALSE); return(TRUE); } @@ -625,7 +878,7 @@ PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) * convert * * insert commas into a number - */ + *//* Maybe needed in future static INT ConvertULong (ULONG num, LPTSTR des, INT len) { @@ -656,10 +909,10 @@ ConvertULong (ULONG num, LPTSTR des, INT len) return n; } - +*/ static INT -ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) +ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator) { TCHAR temp[32]; INT c = 0; @@ -676,7 +929,7 @@ ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) temp[31] = 0; while (num.QuadPart > 0) { - if (((c + 1) % (nNumberGroups + 1)) == 0) + if ((((c + 1) % (nNumberGroups + 1)) == 0) && (bPutSeperator)) temp[30 - c++] = cThousandSeparator; temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0'); num.QuadPart /= 10; @@ -691,44 +944,80 @@ ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) static VOID -PrintFileDateTime (LPSYSTEMTIME dt, DWORD dwFlags) +DirPrintFileDateTime (TCHAR * lpDate, + TCHAR * lpTime, + LPWIN32_FIND_DATA lpFile, + LPDIRSWITCHFLAGS lpFlags) { - WORD wYear = (dwFlags & DIR_FOUR) ? dt->wYear : dt->wYear%100; +FILETIME ft; +SYSTEMTIME dt; +TCHAR szDate[30]; +TCHAR szTime[30]; +WORD wYear; + /* Select the right time field */ + switch (lpFlags->stTimeField.eTimeField) + { + case TF_CREATIONDATE: + if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + case TF_LASTACCESSEDDATE : + if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + case TF_MODIFIEDDATE: + if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + } + + /* Format date */ + wYear = (lpFlags->b4Digit) ? dt.wYear : dt.wYear%100; switch (nDateFormat) { case 0: /* mmddyy */ default: - ConOutPrintf (_T("%.2d%c%.2d%c%d"), - dt->wMonth, cDateSeparator, dt->wDay, cDateSeparator, wYear); + _stprintf (szDate,_T("%02d%c%02d%c%0*d"), + dt.wMonth, cDateSeparator, + dt.wDay, cDateSeparator, + lpFlags->b4Digit?4:2, wYear); break; case 1: /* ddmmyy */ - ConOutPrintf (_T("%.2d%c%.2d%c%d"), - dt->wDay, cDateSeparator, dt->wMonth, cDateSeparator, wYear); + _stprintf (szDate, _T("%02d%c%02d%c%0*d"), + dt.wDay, cDateSeparator, dt.wMonth, + cDateSeparator,lpFlags->b4Digit?4:2, wYear); break; case 2: /* yymmdd */ - ConOutPrintf (_T("%d%c%.2d%c%.2d"), - wYear, cDateSeparator, dt->wMonth, cDateSeparator, dt->wDay); + _stprintf (szDate, _T("%0*d%c%02d%c%02d"), + lpFlags->b4Digit?4:2, wYear, cDateSeparator, + dt.wMonth, cDateSeparator, dt.wDay); break; } - + /* Format Time */ switch (nTimeFormat) { case 0: /* 12 hour format */ default: - ConOutPrintf (_T(" %2d%c%.2u%c"), - (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)), + _stprintf (szTime,_T(" %02d%c%02u%c"), + (dt.wHour == 0 ? 12 : (dt.wHour <= 12 ? dt.wHour : dt.wHour - 12)), cTimeSeparator, - dt->wMinute, (dt->wHour <= 11 ? 'a' : 'p')); + dt.wMinute, (dt.wHour <= 11 ? 'a' : 'p')); break; case 1: /* 24 hour format */ - ConOutPrintf (_T(" %2d%c%.2u"), - dt->wHour, cTimeSeparator, dt->wMinute); + _stprintf (szTime, _T(" %02d%c%02u"), + dt.wHour, cTimeSeparator, dt.wMinute); break; } + /* Copy results */ + _tcscpy(lpDate, szDate); + _tcscpy(lpTime, szTime); } @@ -783,456 +1072,733 @@ static INT PrintSummary(LPTSTR szPath, ULONG ulFiles, ULONG ulDirs, - ULARGE_INTEGER bytes, + ULARGE_INTEGER u64Bytes, LPINT pLine, - DWORD dwFlags) + LPDIRSWITCHFLAGS lpFlags) { - TCHAR buffer[64]; - ULARGE_INTEGER uliFree; - TCHAR szRoot[] = _T("A:\\"); +TCHAR szBuffer[64]; +ULARGE_INTEGER uliFree; +TCHAR szRoot[] = _T("A:\\"); - if (dwFlags & DIR_BARE) - return(0); - /* Print number of files and bytes */ - ConvertULong (ulFiles, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %6s File%c"), - buffer, ulFiles == 1 ? _T(' ') : _T('s')); - - ConvertULargeInteger (bytes, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %15s byte%c\n"), - buffer, bytes.QuadPart == 1 ? _T(' ') : _T('s')); - - if (IncLine (pLine, dwFlags)) - return 1; - - /* Print number of dirs and bytes free */ - ConvertULong (ulDirs, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %6s Dir%c"), - buffer, ulDirs == 1 ? _T(' ') : _T('s')); - - if (!(dwFlags & DIR_RECURSE)) - { - szRoot[0] = szPath[0]; - GetUserDiskFreeSpace(szRoot, &uliFree); - ConvertULargeInteger (uliFree, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %15s bytes free\n"), buffer); - if (IncLine (pLine, dwFlags)) - return 1; - } - else - { - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - ConOutPrintf (_T("\n")); + /* Here we check if we didn't find anything */ + if (!(ulFiles + ulDirs)) + { + error_file_not_found(); + return 1; } - if (IncLine (pLine, dwFlags)) - return 1; + /* In bare format we don't print results */ + if (lpFlags->bBareFormat) + return 0; + + /* Print recursive specific results */ + if (lpFlags->bRecursive) + { + ConvertULargeInteger (u64Bytes, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator); + ConOutPrintf (_T("\n Total Files Listed:\n")); + ConOutPrintf(_T("%16i File(s)% 14s bytes\n"),ulFiles, szBuffer); } + /* Print total directories and freespace */ + szRoot[0] = szPath[0]; + GetUserDiskFreeSpace(szRoot, &uliFree); + ConvertULargeInteger (uliFree, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator); + ConOutPrintf (_T("%16i Dir(s)% 15s bytes\n"),ulDirs, szBuffer); + return 0; } +/* + * getExt + * + * Get the extension of a filename + */ +TCHAR* getExt(const TCHAR* file) +{ + + TCHAR* tmp = _tcsrchr(file,'.'); + return tmp?tmp+1:""; +} /* - * dir_list + * getName * - * list the files in the directory + * Get the name of the file without extension */ -static INT -DirList (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) +TCHAR * getName(const TCHAR* file, TCHAR * dest) { - TCHAR szFullPath[MAX_PATH]; - WIN32_FIND_DATA file; - ULARGE_INTEGER bytecount; - FILETIME ft; - SYSTEMTIME dt; - HANDLE hFile; - TCHAR buffer[32]; - ULONG filecount = 0; - ULONG dircount = 0; - INT count = 0; - SHORT screenwidth; - INT longestfname = 0; - - bytecount.QuadPart = 0; - - _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\')) - _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, szFilespec); - - hFile = FindFirstFile (szFullPath, &file); - if (hFile == INVALID_HANDLE_VALUE) +int iLen; +TCHAR* end; + + /* Check for "." and ".." folders */ + if ((_tcscmp(file, _T(".")) == 0) + || (_tcscmp(file, _T("..")) == 0)) { - /* Don't want to print anything if scanning recursively - * for a file. RL - */ - if ((dwFlags & DIR_RECURSE) == 0) - { - FindClose (hFile); - error_file_not_found (); - if (IncLine (pLine, dwFlags)) - return 0; - return 1; - } - FindClose (hFile); - return 0; + _tcscpy(dest,file); + return dest; } - /* Get the size of longest filename for wide listing. FN */ - if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0) + end = _tcsrchr(file,'.'); + if (!end) + iLen = _tcslen(file); + else + iLen = (end - file); + + + _tcsncpy(dest, file, iLen); + *(dest + iLen) = _T('\0'); + + return dest; +} +/* + * DirPrintNewList + * + * The function that prints in new style + */ +static int +DirPrintNewList(LPWIN32_FIND_DATA ptrFiles[], /* [IN]Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szSize[30]; /* The size of file */ +TCHAR szShortName[15]; /* The sort name */ +TCHAR szDate[20], szTime[20]; /* Date and time strings */ +int iSizeFormat; /* The format of size field */ +ULARGE_INTEGER u64FileSize; /* The file size */ + + for(i = 0;i < dwCount;i++) { - do + + /* Calculate size */ + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + /* Directory */ { - if (_tcslen(file.cFileName) > longestfname) - { - longestfname = _tcslen(file.cFileName); - /* Directories get extra brackets around them. */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - longestfname += 2; - } + iSizeFormat = -14; + _tcscpy(szSize, _T("")); } - while (FindNextFile (hFile, &file)); - FindClose (hFile); + else + /* File */ + { + iSizeFormat = 14; + u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh; + u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow; + ConvertULargeInteger(u64FileSize, szSize, 20,lpFlags->bTSeperator); + } + /* Calculate short name */ + szShortName[0] = _T('\0'); + if (lpFlags->bShortName) + _stprintf(szShortName," %-12s", ptrFiles[i]->cAlternateFileName); + + /* Format date and time */ + DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags); - hFile = FindFirstFile (szFullPath, &file); - - /* Count the highest number of columns */ - GetScreenSize(&screenwidth, 0); - - /* For counting columns of output */ - count = 0; - - /* Increase by the number of spaces behind file name */ - longestfname += 3; + /* Print the line */ + ConOutPrintf("%10s %-8s %*s%s %s\n", + szDate, + szTime, + iSizeFormat, + szSize, + szShortName, + ptrFiles[i]->cFileName ); + } + return 0; +} +/* + * DirPrintWideList + * + * The function that prints in wide list + */ +static int +DirPrintWideList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i,j; /* An indexer for "for"s */ +short iScreenWidth; /* The screen width */ +short iColumns; /* The columns (used by wide list) */ +short iLines; /* The lines (used by the wide list /d) */ +int iBiggestName; /* The biggest name, used for wide list */ +TCHAR szTempFname[MAX_PATH]; /* Temporary string */ - /* moved down here because if we are recursively searching and - * don't find any files, we don't want just to print - * Directory of C:\SOMEDIR - * with nothing else - * Rob Lake 06/13/98 - */ - if ((dwFlags & DIR_BARE) == 0) + /* Calculate biggest name */ + iBiggestName = 1; + for(i = 0;i < dwCount;i++) { - ConOutPrintf (_T(" Directory of %s\n"), szPath); - if (IncLine (pLine, dwFlags)) - return 1; - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - do - { - /* next file, if user doesn't want all files */ - if (!(dwFlags & DIR_ALL) && - ((file.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || - (file.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))) - continue; - - if (dwFlags & DIR_LWR) + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - _tcslwr (file.cAlternateFileName); - } - - if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0) - { - ULARGE_INTEGER uliSize; - - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - _stprintf (buffer, _T("[%s]"), file.cFileName); - dircount++; - } - else - { - _stprintf (buffer, _T("%s"), file.cFileName); - filecount++; - } - - ConOutPrintf (_T("%*s"), - longestfname, buffer); - count++; - /* output as much columns as fits on the screen */ - if (count >= (screenwidth / longestfname)) - { - /* print the new line only if we aren't on the - * last column, in this case it wraps anyway */ - if (count * longestfname != screenwidth) - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - count = 0; - } - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - bytecount.QuadPart += uliSize.QuadPart; - } - else if (dwFlags & DIR_BARE) - { - ULARGE_INTEGER uliSize; - - if (_tcscmp (file.cFileName, _T(".")) == 0 || - _tcscmp (file.cFileName, _T("..")) == 0) - continue; - - if (dwFlags & DIR_RECURSE) - { - TCHAR dir[MAX_PATH]; - - _tcscpy (dir, szPath); - _tcscat (dir, _T("\\")); - if (dwFlags & DIR_LWR) - _tcslwr (dir); - ConOutPrintf (dir); - } - - ConOutPrintf (_T("%-13s\n"), file.cFileName); - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - dircount++; - else - filecount++; - if (IncLine (pLine, dwFlags)) - return 1; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - bytecount.QuadPart += uliSize.QuadPart; + /* Directories need 2 additinal characters for brackets */ + if ((_tcslen(ptrFiles[i]->cFileName) + 2) > iBiggestName) + iBiggestName = _tcslen(ptrFiles[i]->cFileName) + 2; } else { - if (dwFlags & DIR_NEW) - { - /* print file date and time */ - if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft)) - { - FileTimeToSystemTime (&ft, &dt); - PrintFileDateTime (&dt, dwFlags); - } - - /* print file size */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - { - ConOutPrintf (_T(" ")); - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - dircount++; - } - else if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - ConOutPrintf (_T(" ")); - dircount++; - } - else - { - ULARGE_INTEGER uliSize; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - - ConvertULargeInteger (uliSize, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %20s"), buffer); - - bytecount.QuadPart += uliSize.QuadPart; - filecount++; - } - - /* print long filename */ - ConOutPrintf (_T(" %s\n"), file.cFileName); - } - else - { - if (file.cFileName[0] == _T('.')) - ConOutPrintf (_T("%-13s "), file.cFileName); - else if (file.cAlternateFileName[0] == _T('\0')) - { - TCHAR szShortName[13]; - LPTSTR ext; - - _tcsncpy (szShortName, file.cFileName, 13); - ext = _tcschr (szShortName, _T('.')); - if (!ext) - ext = _T(""); - else - *ext++ = _T('\0'); - ConOutPrintf (_T("%-8s %-3s "), szShortName, ext); - } - else - { - LPTSTR ext; - - ext = _tcschr (file.cAlternateFileName, _T('.')); - if (!ext) - ext = _T(""); - else - *ext++ = _T('\0'); - ConOutPrintf (_T("%-8s %-3s "), file.cAlternateFileName, ext); - } - - /* print file size */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - ConOutPrintf (_T("%-14s"), _T("")); - dircount++; - } - else - { - ULARGE_INTEGER uliSize; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - ConvertULargeInteger (uliSize, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %10s "), buffer); - bytecount.QuadPart += uliSize.QuadPart; - filecount++; - } - - /* print file date and time */ - if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft)) - { - FileTimeToSystemTime (&ft, &dt); - PrintFileDateTime (&dt, dwFlags); - } - - /* print long filename */ - ConOutPrintf (_T(" %s\n"), file.cFileName); - } - - if (IncLine (pLine, dwFlags)) - return 1; + if (_tcslen(ptrFiles[i]->cFileName) > iBiggestName) + iBiggestName = _tcslen(ptrFiles[i]->cFileName); } } - while (FindNextFile (hFile, &file)); - FindClose (hFile); + + /* Count the highest number of columns */ + GetScreenSize(&iScreenWidth, 0); + iColumns = iScreenWidth / iBiggestName; - /* Rob Lake, need to make clean output */ - /* JPP 07/08/1998 added check for count != 0 */ - if ((dwFlags & DIR_WIDE) && (count != 0)) + /* Check if there is enough space for spaces between names */ + if (((iBiggestName * iColumns) + iColumns) >= iScreenWidth) + iColumns --; + /* A last check at iColumns to avoid division by zero */ + if (!(iColumns)) iColumns = 1; + + /* Print Column sorted */ + if (lpFlags->bWideListColSort) { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } + /* Calculate the lines that will be printed */ + iLines = ceil((float)dwCount/(float)iColumns); - if (filecount || dircount) - { - recurse_dir_cnt += dircount; - recurse_file_cnt += filecount; - recurse_bytes.QuadPart += bytecount.QuadPart; + for (i = 0;i < iLines;i++) + { + for (j = 0;j < iColumns;j++) + { + DWORD temp = (j*iLines)+i; + if (temp >= dwCount) break; + if (ptrFiles[temp]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + _stprintf(szTempFname, "[%s]", ptrFiles[temp]->cFileName); + else + _stprintf(szTempFname, "%s", ptrFiles[temp]->cFileName); - /* print_summary */ - if (PrintSummary (szPath, filecount, dircount, bytecount, pLine, dwFlags)) - return 1; + ConOutPrintf("%-*s",iBiggestName + 1 , szTempFname ); + } + ConOutPrintf("\n"); + } } + /* Print Line sorted */ else { - error_file_not_found (); - return 1; - } + for (i = 0;i < dwCount;i++) + { + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + _stprintf(szTempFname, "[%s]", ptrFiles[i]->cFileName); + else + _stprintf(szTempFname, "%s", ptrFiles[i]->cFileName); + ConOutPrintf("%-*s", iBiggestName + 1, szTempFname ); + /* We print a new line at the end of each column + except for the case that it is the last item.*/ + if (!((i+1)%iColumns) && (i < (dwCount-1))) + ConOutPrintf("\n"); + } + /* Add a new line after the last item */ + ConOutPrintf("\n"); + } + return 0; +} +/* + * DirPrintOldList + * + * The function that prints in old style + */ +static int +DirPrintOldList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szName[10]; /* The name of file */ +TCHAR szExt[5]; /* The extension of file */ +TCHAR szDate[30],szTime[30]; /* Used to format time and date */ +TCHAR szSize[30]; /* The size of file */ +int iSizeFormat; /* The format of size field */ +ULARGE_INTEGER u64FileSize; /* The file size */ + + for(i = 0;i < dwCount;i++) + { + /* Broke 8.3 format */ + if (*ptrFiles[i]->cAlternateFileName ) + { + /* If the file is long named then we read the alter name */ + getName( ptrFiles[i]->cAlternateFileName, szName); + _tcscpy(szExt, getExt( ptrFiles[i]->cAlternateFileName)); + } + else + { + /* If the file is not long name we read its original name */ + getName( ptrFiles[i]->cFileName, szName); + _tcscpy(szExt, getExt( ptrFiles[i]->cFileName)); + } + + /* Calculate size */ + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* Directory, no size it's a directory*/ + iSizeFormat = -17; + _tcscpy(szSize, _T("")); + } + else + { + /* File */ + iSizeFormat = 17; + u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh; + u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow; + ConvertULargeInteger(u64FileSize, szSize, 20, lpFlags->bTSeperator); + } + + /* Format date and time */ + DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags); + + /* Print the line */ + ConOutPrintf("%-8s %-3s %*s %s %s\n", + szName, /* The file's 8.3 name */ + szExt, /* The file's 8.3 extension */ + iSizeFormat, /* print format for size column */ + szSize, /* The size of file or "" for dirs */ + szDate, /* The date of file/dir */ + szTime); /* The time of file/dir */ + } + return 0; +} + +/* + * DirPrintBareList + * + * The function that prints in bare format + */ +static int +DirPrintBareList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szFullName[MAX_PATH]; /* The fullpath name of file */ + + for(i = 0;i < dwCount;i++) + { + if ((_tcscmp(ptrFiles[i]->cFileName, _T(".")) == 0) || + (_tcscmp(ptrFiles[i]->cFileName, _T("..")) == 0)) + { + /* at bare format we don't print "." and ".." folder */ + continue; + } + /* at recursive mode we print full path of file */ + if (lpFlags->bRecursive) + { + _tcscpy(szFullName, szCurPath); + _tcscat(szFullName, ptrFiles[i]->cFileName); + ConOutPrintf("%s\n", szFullName); + } + /* if we are not in recursive mode we print the file names */ + else + ConOutPrintf("%s\n",ptrFiles[i]->cFileName); + } + return 0; +} + +/* + * DirPrintFiles + * + * The functions that prints the files list + */ +static int +DirPrintFiles(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */ + + /* Print directory header */ + _tcscpy(szTemp, szCurPath); + /* We cut the trailing \ of the full path */ + szTemp[_tcslen(szTemp)-1] = _T('\0'); + /* Condition to print header: + We are not printing in bare format + and if we are in recursive mode... we must have results */ + if (!(lpFlags->bBareFormat ) && !((lpFlags->bRecursive) && (dwCount <= 0))) + ConOutPrintf("\n Directory of %s\n\n", szTemp); + + /* Bare format */ + if (lpFlags->bBareFormat) + { + DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* New list style / Short names */ + else if(lpFlags->bShortName) + { + DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* Wide list */ + else if(lpFlags->bWideListColSort || lpFlags->bWideList) + { + DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* New list style*/ + else if (lpFlags->bNewLongList ) + { + DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* If nothing is selected old list is the default */ + else + { + DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags); + } return 0; } + /* - * _Read_Dir: Actual function that does recursive listing + * CompareFiles + * + * Compares 2 files based on the order criteria */ -static INT -DirRead (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) +static int + CompareFiles( + LPWIN32_FIND_DATA lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */ + LPWIN32_FIND_DATA lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */ { - TCHAR szFullPath[MAX_PATH]; - WIN32_FIND_DATA file; - HANDLE hFile; +/* u64 variables used to translate some broken 32bit info */ +ULARGE_INTEGER u64File1, u64File2; +int i; /* An indexer for "for"s */ +long iComp; /* The comparison result */ + /* Calculate critiries by order given from user */ + for (i = 0;i < lpFlags->stOrderBy.sCriteriaCount;i++) + { + + /* Calculate criteria */ + switch(lpFlags->stOrderBy.eCriteria[i]) + { + case ORDER_SIZE: /* Order by size /o:s */ + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->nFileSizeLow; + u64File1.HighPart = lpFile1->nFileSizeHigh; + u64File2.LowPart = lpFile2->nFileSizeLow; + u64File2.HighPart = lpFile2->nFileSizeHigh; + + /* In case that differnce is too big for a long */ + if (u64File1.QuadPart < u64File2.QuadPart) + iComp = -1; + else if (u64File1.QuadPart > u64File2.QuadPart) + iComp = 1; + else + iComp = 0; + break; + + break; + case ORDER_DIRECTORY: /* Order by directory attribute /o:g */ + iComp = ((lpFile2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)- + (lpFile1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + break; + case ORDER_EXTENSION: /* Order by extension name /o:e */ + iComp = _stricmp(getExt(lpFile1->cFileName),getExt(lpFile2->cFileName)); + break; + case ORDER_NAME: /* Order by filename /o:n */ + iComp = _stricmp(lpFile1->cFileName, lpFile2->cFileName); + break; + case ORDER_TIME: /* Order by file's time /o:t */ + + /* We compare files based on the time field selected by /t */ + switch(lpFlags->stTimeField.eTimeField) + { + case TF_CREATIONDATE: + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftCreationTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftCreationTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftCreationTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftCreationTime.dwHighDateTime ; + break; + case TF_LASTACCESSEDDATE : + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftLastAccessTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftLastAccessTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftLastAccessTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftLastAccessTime.dwHighDateTime ; + break; + case TF_MODIFIEDDATE: + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftLastWriteTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftLastWriteTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftLastWriteTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftLastWriteTime.dwHighDateTime ; + break; + } + + /* In case that differnce is too big for a long */ + if (u64File1.QuadPart < u64File2.QuadPart) + iComp = -1; + else if (u64File1.QuadPart > u64File2.QuadPart) + iComp = 1; + else + iComp = 0; + break; + } + /* Reverse if desired */ + if (lpFlags->stOrderBy.bCriteriaRev[i]) + iComp *= -1; + + /* If that criteria was enough for distinguishing + the files/dirs,there is no need to calculate the others*/ + if (iComp != 0) break; + } + + /* Translate the value of iComp to boolean */ + if (iComp > 0) + return TRUE; + else + return FALSE; +} + +/* + * QsortFiles + * + * Sort files by the order criterias using quicksort method + */ +static void +QsortFiles( + LPWIN32_FIND_DATA ptrArray[], /* [IN/OUT] The array with file info pointers */ + int i, /* [IN] The index of first item in array */ + int j, /* [IN] The index to last item in array */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */ +{ +LPWIN32_FIND_DATA lpTemp; /* A temporary pointer */ + /* used for exchangin pointers */ +int First, Last, Temp; +BOOL Way; + + if (i < j) + { + First = i, Last = j, Way = TRUE; + while (i != j) + { + if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags)) + { + /* Swap the pointers of the array */ + lpTemp = ptrArray[i]; + ptrArray[i]= ptrArray[j]; + ptrArray[j] = lpTemp; + /* Swap the indexers for inverting sorting */ + Temp = i, i=j,j =Temp; + Way = !Way; + } + j += (!Way - Way); + + } + QsortFiles(ptrArray,First, i-1, lpFlags); + QsortFiles(ptrArray,i+1,Last, lpFlags); + } +} + + + +/* + * DirList + * + * The functions that does everything except for printing results + */ +static INT DirList (LPTSTR szPath, /* [IN] The path that dir starts */ + LPTSTR szFilespec, /* [IN] The type of file that we are looking for */ + LPINT pLine, /* FIXME: Maybe used for paginating */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */ +{ + +/* Internal linked list */ +struct TDirFindListNode + { + WIN32_FIND_DATA stFindInfo; + struct TDirFindListNode * ptrNext; +}; + +HANDLE hSearch; /* The handle of the search */ +HANDLE hRecSearch; /* The handle for searching recursivly */ +WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */ +LPWIN32_FIND_DATA * ptrFileArray; /* An array of pointers with all the files */ +struct TDirFindListNode * ptrStartNode; /* The pointer to the first node */ +struct TDirFindListNode * ptrNextNode; /* A pointer used for relatives refernces */ +TCHAR szFullPath[MAX_PATH]; /* The full path that we are listing with trailing \ */ +TCHAR szFullFileSpec[MAX_PATH]; /* The full path with file specs that we ll request\ */ +TCHAR szBytes[20]; /* A string for converting ULARGE integer */ +DWORD dwCount; /* A counter of files found in directory */ +DWORD dwCountFiles; /* Counter for files */ +DWORD dwCountDirs; /* Counter for directories */ +ULARGE_INTEGER u64CountBytes; /* Counter for bytes */ +ULARGE_INTEGER u64Temp; /* A temporary counter */ + + /* Initialize Variables */ + ptrStartNode = NULL; + ptrNextNode = NULL; + dwCount = 0; + dwCountFiles = 0; + dwCountDirs = 0; + u64CountBytes.QuadPart = 0; + + /* Create szFullPath and szFullFileSpec */ _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) + if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\')) _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, _T("*")); + _tcscpy (szFullFileSpec, szFullPath); + _tcscat (szFullFileSpec, szFilespec); - hFile = FindFirstFile (szFullPath, &file); - if (hFile == INVALID_HANDLE_VALUE) - return 1; + /* Prepare the linked list, first node is allocated */ + if ((ptrStartNode = malloc(sizeof(struct TDirFindListNode))) == NULL) + { +#ifdef _DEBUG + ConErrPrintf("DEBUG: Cannot allocate memmory for ptrStartNode!\n"); +#endif + return 1; /* Error cannot allocate memmory for 1st object */ + } + ptrNextNode = ptrStartNode; + /* Collect the results for the current folder */ + hSearch = FindFirstFile (szFullFileSpec, &wfdFileInfo); do { - /* don't list "." and ".." */ - if (_tcscmp (file.cFileName, _T(".")) == 0 || - _tcscmp (file.cFileName, _T("..")) == 0) - continue; - - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if (hSearch != INVALID_HANDLE_VALUE) { - _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) - _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, file.cFileName); + /* Here we filter all the specified attributes */ + if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask ) + == (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal )) + { + ptrNextNode->ptrNext = malloc(sizeof(struct TDirFindListNode)); + /* If malloc fails we go to next file in hope it works, + without braking the linked list! */ + if (ptrNextNode->ptrNext) + { + /* Copy the info of search at linked list */ + memcpy((void *)&ptrNextNode->ptrNext->stFindInfo, + (void *)&wfdFileInfo,sizeof(WIN32_FIND_DATA)); + + /* If lower case is selected do it here */ + if (lpFlags->bLowerCase) + { + _tcslwr(ptrNextNode->ptrNext->stFindInfo.cAlternateFileName); + _tcslwr(ptrNextNode->ptrNext->stFindInfo.cFileName); + } + + /* Continue at next node at linked list */ + ptrNextNode = ptrNextNode->ptrNext; + dwCount ++; + + /* Grab statistics */ + if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* Directory */ + dwCountDirs++; + } + else + { + /* File */ + dwCountFiles++; + u64Temp.HighPart = wfdFileInfo.nFileSizeHigh; + u64Temp.LowPart = wfdFileInfo.nFileSizeLow; + u64CountBytes.QuadPart += u64Temp.QuadPart; + } + } + } - if (DirList (szFullPath, szFilespec, pLine, dwFlags)) - { - FindClose (hFile); - return 1; - } - if (DirRead (szFullPath, szFilespec, pLine, dwFlags) == 1) - { - FindClose (hFile); - return 1; - } - } - } - while (FindNextFile (hFile, &file)); + } + }while(FindNextFile(hSearch,&wfdFileInfo)); - if (!FindClose (hFile)) - return 1; + /* Terminate list */ + ptrNextNode->ptrNext = NULL; + + /* Calculate and allocate space need for making an array of pointers */ + ptrFileArray = malloc(sizeof(LPWIN32_FIND_DATA)*dwCount); + if (!(ptrFileArray)) + { +#ifdef _DEBUG + ConErrPrintf("DEBUG: Cannot allocate memmory for ptrFileArray!\n"); +#endif + goto _DirList_clear_n_exit; + } + + /* Create an array of pointers from the linked list + this will be used to sort and print data, rather than the list */ + ptrNextNode = ptrStartNode; + dwCount = 0; + while(ptrNextNode->ptrNext) + { + *(ptrFileArray + dwCount) = &ptrNextNode->ptrNext->stFindInfo; + ptrNextNode = ptrNextNode->ptrNext; + dwCount++; + } + + /* Sort Data if requested*/ + if (lpFlags->stOrderBy.sCriteriaCount > 0) + QsortFiles(ptrFileArray, 0, dwCount-1,lpFlags); + + /* Print Data */ + DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags); + + /* Print Directory Summary */ + /* Condition to print summary is: + If we are not in bare format and if we have results! */ + if (!(lpFlags->bBareFormat) && (dwCount > 0)) + { + ConvertULargeInteger(u64CountBytes, szBytes, 20, lpFlags->bTSeperator); + ConOutPrintf(_T("%16i File(s) %14s bytes\n"), + dwCountFiles, szBytes); + } + + /* Add statistics to recursive statistics*/ + recurse_dir_cnt += dwCountDirs; + recurse_file_cnt += dwCountFiles; + recurse_bytes.QuadPart += u64CountBytes.QuadPart; + + /* Do the recursive job if requested + the recursive is be done on ALL(indepent of their attribs) + directoried of the current one.*/ + if (lpFlags->bRecursive) + { + /* The new search is involving any *.* file */ + _tcscpy(szFullFileSpec, szFullPath); + _tcscat(szFullFileSpec, _T("*.*")); + hRecSearch = FindFirstFile (szFullFileSpec, &wfdFileInfo); + do + { + if (hRecSearch != INVALID_HANDLE_VALUE) + { + /* We search for directories other than "." and ".." */ + if ((_stricmp(wfdFileInfo.cFileName, _T(".")) != 0) + && (_stricmp(wfdFileInfo.cFileName, _T("..")) != 0 ) + && (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + /* Concat the path and the directory to do recursive */ + _tcscpy(szFullFileSpec, szFullPath); + _tcscat(szFullFileSpec, wfdFileInfo.cFileName); + /* We do the same for tha folder */ + DirList(szFullFileSpec, szFilespec, pLine,lpFlags); + } + } + }while(FindNextFile(hRecSearch,&wfdFileInfo)); + } + +_DirList_clear_n_exit: +/* Deallocate memmory */ + /* Free linked list */ + while (ptrStartNode) + { + ptrNextNode = ptrStartNode->ptrNext; + free(ptrStartNode); + ptrStartNode = ptrNextNode; + dwCount --; + } + /* Free array */ + free(ptrFileArray); + + /* Close Handles */ + if (hSearch != INVALID_HANDLE_VALUE) + FindClose(hSearch); + if (hRecSearch != INVALID_HANDLE_VALUE) + FindClose(hRecSearch); return 0; } -/* - * do_recurse: Sets up for recursive directory listing - */ -static INT -DirRecurse (LPTSTR szPath, LPTSTR szSpec, LPINT pLine, DWORD dwFlags) -{ - if (!PrintDirectoryHeader (szPath, pLine, dwFlags)) - return 1; - - if (DirList (szPath, szSpec, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - if (DirRead (szPath, szSpec, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - ConOutPrintf (_T("Total files listed:\n")); - - dwFlags &= ~DIR_RECURSE; - - if (PrintSummary (szPath, recurse_file_cnt, - recurse_dir_cnt, recurse_bytes, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - return 0; -} - /* * dir @@ -1241,27 +1807,45 @@ DirRecurse (LPTSTR szPath, LPTSTR szSpec, LPINT pLine, DWORD dwFlags) */ INT CommandDir (LPTSTR first, LPTSTR rest) { - DWORD dwFlags = DIR_NEW | DIR_FOUR; - TCHAR dircmd[256]; + TCHAR dircmd[256]; /* A variable to store the DIRCMD enviroment variable */ TCHAR szPath[MAX_PATH]; TCHAR szFilespec[MAX_PATH]; LPTSTR param; INT nLine = 0; + DIRSWITCHFLAGS stFlags; - + /* Initialize variables */ recurse_dir_cnt = 0L; recurse_file_cnt = 0L; recurse_bytes.QuadPart = 0; + /* Initialize Switch Flags < Default switches are setted here!> */ + stFlags.b4Digit = TRUE; + stFlags.bBareFormat = FALSE; + stFlags.bLowerCase = FALSE; + stFlags.bNewLongList = TRUE; + stFlags.bPause = FALSE; + stFlags.bRecursive = FALSE; + stFlags.bShortName = FALSE; + stFlags.bTSeperator = TRUE; + stFlags.bUser = FALSE; + stFlags.bWideList = FALSE; + stFlags.bWideListColSort = FALSE; + stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE; + stFlags.stTimeField.bUnSet = FALSE; + stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; + stFlags.stAttribs.dwAttribVal = 0L; + stFlags.stAttribs.bUnSet = FALSE; + stFlags.stOrderBy.sCriteriaCount = 0; + stFlags.stOrderBy.bUnSet = FALSE; + /* read the parameters from the DIRCMD environment variable */ - if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) - { - if (!DirReadParam (dircmd, ¶m, &dwFlags)) - return 1; - } + if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) + if (!DirReadParam(dircmd, ¶m, &stFlags)) + return 1; /* read the parameters */ - if (!DirReadParam (rest, ¶m, &dwFlags)) + if (!DirReadParam(rest, ¶m, &stFlags)) return 1; /* default to current directory */ @@ -1272,21 +1856,44 @@ INT CommandDir (LPTSTR first, LPTSTR rest) if (DirParsePathspec (param, szPath, szFilespec)) return 1; - if (dwFlags & DIR_RECURSE) - { - if (IncLine (&nLine, dwFlags)) - return 0; - if (DirRecurse (szPath, szFilespec, &nLine, dwFlags)) +/* + Uncomment this to show the final state of switch flags*/ +/* + ConOutPrintf("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal ); + ConOutPrintf("(B) Bare format : %i\n", stFlags.bBareFormat ); + ConOutPrintf("(C) Thousand : %i\n", stFlags.bTSeperator ); + ConOutPrintf("(W) Wide list : %i\n", stFlags.bWideList ); + ConOutPrintf("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort ); + ConOutPrintf("(L) Lowercase : %i\n", stFlags.bLowerCase ); + ConOutPrintf("(N) New : %i\n", stFlags.bNewLongList ); + ConOutPrintf("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount ); + int i; + for (i =0;i #include #include +#include #include "cmd.h" #include "config.h"