mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1795 lines
44 KiB
1795 lines
44 KiB
#include "cmd.h"
|
|
|
|
/*
|
|
|
|
Usage:
|
|
------
|
|
|
|
DIR <filespec> /n /d /w /p /b /s /l /o<sortorder> /a<attriblist>
|
|
|
|
DIR /?
|
|
|
|
|
|
<filespec> may include any or none of: drive; directory path;
|
|
wildcarded filename. If drive or directory path are
|
|
omitted, the current defaults are used. If the
|
|
file name or extension is omitted, wildcards are
|
|
assumed.
|
|
|
|
/n Normal display form FAT drives is name followed by
|
|
file information, for non-FAT drives it is file
|
|
information followed by name. This switch will use
|
|
the non-FAT format independent of the filesystem.
|
|
|
|
/w Wide listing format. Files are displayed in compressed
|
|
'name.ext' format. Subdirectory files are enclosed in
|
|
brackets, '[dirname]'.
|
|
|
|
/d Same as /w but display sort by columns instead of by
|
|
rows.
|
|
|
|
/p Paged, or prompted listing. A screenful is displayed
|
|
at a time. The name of the directory being listed appears
|
|
at the top of each page.
|
|
|
|
/b Bare listing format. Turns off /w or /p. Files are
|
|
listed in compressed 'name.ext' format, one per line,
|
|
without additional information. Good for making batch
|
|
files or for piping. When used with /s, complete
|
|
pathnames are listed.
|
|
|
|
/s Descend subdirectory tree. Performs command on current
|
|
or specified directory, then for each subdirectory below
|
|
that directory. Directory header and footer is displayed
|
|
for each directory where matching files are found, unless
|
|
used with /b. /b suppresses headers and footers.
|
|
|
|
Tree is explored depth first, alphabetically within the
|
|
same level.
|
|
|
|
Bugbug: hidden directories aren't searched.
|
|
|
|
/l Display file names, extensions and paths in lowercase. ;M010
|
|
|
|
/o Sort order. /o alone sorts by default order (dirs-first, name,
|
|
extension). A sort order may be specified after /o. Any of
|
|
the following characters may be used: nedsg (name, extension,
|
|
date/time, size, group-dirs-first). Placing a '-' before any
|
|
letter causes a downward sort on that field. E.g., /oe-d
|
|
means sort first by extension in alphabetical order, then
|
|
within each extension sort by date and time in reverse chronological
|
|
order.
|
|
|
|
/a Attribute selection. Without /a, hidden and system files
|
|
are suppressed from the listing. With /a alone, all files
|
|
are listed. An attribute list may follow /a, consisting of
|
|
any of the following characters: hsdar (hidden, system,
|
|
directory, archive, read-only). A '-' before any letter
|
|
means 'not' that attribute. E.g., /ar-d means files that
|
|
are marked read-only and are not directory files. Note
|
|
that hidden or system files may be included in the listing.
|
|
They are suppressed without /a but are treated like any other
|
|
attribute with /a.
|
|
|
|
/t Which time stamp to use.
|
|
/t:a - last access
|
|
/t:c - create
|
|
/t:w - last write
|
|
|
|
/, Show thousand separators in output display.
|
|
|
|
DIRCMD An environment variable named DIRCMD is parsed before the
|
|
DIR command line. Any command line options may be specified
|
|
in DIRCMD, and become defaults. /? will be ignored in DIRCMD.
|
|
A filespec may be specified in DIRCMD and will be used unless
|
|
a filespec is specified on the command line. Any switch
|
|
specified in DIRCMD may be overridden on the command line.
|
|
If the original DIR default action is desired for a particular
|
|
switch, the switch letter may be preceded by a '-' on the
|
|
command line. E.g.,
|
|
|
|
/-w use long listing format
|
|
/-p don't page the listing
|
|
/-b don't use bare format
|
|
/-s don't descend subdirectory tree
|
|
/-o display files in disk order
|
|
/-a suppress hidden and system files
|
|
|
|
|
|
*/
|
|
|
|
extern TCHAR SwitChar, PathChar;
|
|
extern TCHAR Fmt14[] ;
|
|
extern TCHAR CurDrvDir[] ;
|
|
extern ULONG DCount ;
|
|
extern DWORD DosErr ;
|
|
extern BOOL CtrlCSeen;
|
|
|
|
|
|
VOID FreeStr( PTCHAR );
|
|
|
|
BOOLEAN FindFirstNt( PTCHAR, PWIN32_FIND_DATA, PHANDLE );
|
|
BOOLEAN FindNextNt ( PWIN32_FIND_DATA, HANDLE );
|
|
|
|
HANDLE OpenConsole();
|
|
STATUS PrintPatterns( PDRP );
|
|
|
|
PTCHAR SetWildCards( PTCHAR, BOOLEAN );
|
|
BOOLEAN GetDrive( PTCHAR , PTCHAR );
|
|
BOOLEAN IsFATDrive( PTCHAR );
|
|
PTCHAR GetNewDir(PTCHAR, PFF);
|
|
|
|
STATUS DirWalkAndProcess( STATUS (* ) ( PSCREEN, PULONG, PLARGE_INTEGER, ULONG, ULONG, PFS ),
|
|
STATUS (* ) (PFS, PULONG),
|
|
PSCREEN,
|
|
PULONG,
|
|
PLARGE_INTEGER,
|
|
PFS,
|
|
PDRP,
|
|
BOOLEAN,
|
|
BOOLEAN (*) (STATUS, PTCHAR));
|
|
|
|
|
|
|
|
PTCHAR BuildSearchPath( PTCHAR );
|
|
VOID SortFileList( PFS, PSORTDESC, ULONG);
|
|
STATUS SetSortDesc( PTCHAR, PDRP );
|
|
STATUS SetAttribs( PTCHAR, PDRP );
|
|
//
|
|
// This global is set in SortFileList and is used by the sort routine called
|
|
// within qsort. This array contains pointers to compare functions. Our sort
|
|
// does not just sort against 1 criteria but all of the criteria in the sort
|
|
// description array built from the command line.
|
|
PSORTDESC prgsrtdsc;
|
|
|
|
//
|
|
// dwTimeType is also globally set in SortFileList and is used to control
|
|
// which time field is used for sorting.
|
|
//
|
|
|
|
ULONG dwTimeType;
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints out a catalog of a specified directory.
|
|
|
|
Arguments:
|
|
|
|
pszCmdLine - Command line (see comment above)
|
|
|
|
Return Value:
|
|
|
|
Return: SUCCESS - no completion.
|
|
FAILURE - failed to complete entire catalog.
|
|
|
|
--*/
|
|
|
|
//
|
|
// BUGBUG change this to a BOOLEAN and a PTCHAR on pszCmdLine
|
|
int
|
|
Dir (
|
|
TCHAR *pszCmdLine
|
|
) {
|
|
|
|
//
|
|
// drp - structure holding current set of parameters. It is initialized
|
|
// in ParseDirParms function. It is also modified later when
|
|
// parameters are examined to determine if some turn others on.
|
|
//
|
|
DRP drpCur = {0, 0, 0, 0,0, {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, NULL} ;
|
|
|
|
//
|
|
// szEnvVar - pointer to value of the DIRCMD environmental variable.
|
|
// This should be a form of the command line that is
|
|
// used to alter DIR default behavior.
|
|
TCHAR szEnvVar[MAX_PATH + 2];
|
|
|
|
//
|
|
// szCurDrv - Hold current drive letter
|
|
//
|
|
TCHAR szCurDrv[MAX_PATH + 2];
|
|
|
|
//
|
|
// OldDCount - Holds the level number of the heap. It is used to
|
|
// free entries off the stack that might not have been
|
|
// freed due to error processing (ctrl-c etc.)
|
|
ULONG OldDCount;
|
|
|
|
STATUS rc;
|
|
|
|
OldDCount = DCount;
|
|
|
|
//
|
|
// Setup defaults
|
|
//
|
|
//
|
|
// Display everything but system and hidden files
|
|
// rgfAttribs set the attribute bits to that are of interest and
|
|
// rgfAttribsOnOff says wither the attributs should be present
|
|
// or not (i.e. On or Off)
|
|
//
|
|
drpCur.rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
|
|
drpCur.rgfAttribsOnOff = 0;
|
|
drpCur.rgfSwitchs = THOUSANDSEPSWITCH;
|
|
|
|
//
|
|
// Number of patterns present. A pattern is a string that may have
|
|
// wild cards. It is used to match against files present in the directory
|
|
// 0 patterns will show all files (i.e. mapped to *.*)
|
|
//
|
|
drpCur.cpatdsc = 0;
|
|
|
|
DEBUG((ICGRP, DILVL, "DIR:\t arg = `%ws'", (ULONG)pszCmdLine)) ;
|
|
|
|
//
|
|
// default time is LAST_WRITE_TIME.
|
|
//
|
|
drpCur.dwTimeType = LAST_WRITE_TIME;
|
|
|
|
//
|
|
// DIRCMD holds a copy of default parameters to Dir
|
|
// parse these into drpCur (list of parameters to dir) and use as
|
|
// default into parsing parameters on command line.
|
|
//
|
|
if (GetEnvironmentVariable(TEXT("DIRCMD"), szEnvVar, MAX_PATH + 2)) {
|
|
|
|
DEBUG((ICGRP, DILVL, "DIR: DIRCMD `%ws'", (ULONG)szEnvVar)) ;
|
|
|
|
if (ParseDirParms(szEnvVar, &drpCur) == FAILURE) {
|
|
|
|
//
|
|
// Error in parsing environment variable
|
|
//
|
|
// DOS 5.0 continues with command even if the
|
|
// environmental variable is wrong
|
|
//
|
|
PutStdErr(MSG_ERROR_IN_DIRCMD, NOARGS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Override environment variable with command line options
|
|
//
|
|
if (ParseDirParms(pszCmdLine, &drpCur) == FAILURE) {
|
|
|
|
return( FAILURE );
|
|
}
|
|
|
|
|
|
//
|
|
// If bare format then turn off the other formats
|
|
// bare format will have no addition information on the line so
|
|
// make sure that options set from the DIRCMD variable etc. to
|
|
// not combine with the bare switch
|
|
//
|
|
if (drpCur.rgfSwitchs & BAREFORMATSWITCH) {
|
|
drpCur.rgfSwitchs &= ~WIDEFORMATSWITCH;
|
|
drpCur.rgfSwitchs &= ~SORTDOWNFORMATSWITCH;
|
|
drpCur.rgfSwitchs &= ~SHORTFORMATSWITCH;
|
|
drpCur.rgfSwitchs &= ~THOUSANDSEPSWITCH;
|
|
}
|
|
|
|
//
|
|
// If short form (short file names) turn off others
|
|
//
|
|
if (drpCur.rgfSwitchs & SHORTFORMATSWITCH) {
|
|
drpCur.rgfSwitchs &= ~WIDEFORMATSWITCH;
|
|
drpCur.rgfSwitchs &= ~SORTDOWNFORMATSWITCH;
|
|
drpCur.rgfSwitchs &= ~BAREFORMATSWITCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If no patterns on the command line use the default which
|
|
// would be the current directory
|
|
//
|
|
|
|
GetDir((PTCHAR)szCurDrv, GD_DEFAULT);
|
|
if (drpCur.cpatdsc == 0) {
|
|
drpCur.cpatdsc++;
|
|
drpCur.patdscFirst.pszPattern = szCurDrv;
|
|
drpCur.patdscFirst.fIsFat = TRUE;
|
|
drpCur.patdscFirst.pszDir = NULL;
|
|
drpCur.patdscFirst.ppatdscNext = NULL;
|
|
|
|
}
|
|
|
|
|
|
DEBUG((ICGRP, DILVL, "Dir: Parameters")) ;
|
|
DEBUG((ICGRP, DILVL, "\t rgfSwitchs %x", drpCur.rgfSwitchs)) ;
|
|
DEBUG((ICGRP, DILVL, "\t rgfAttribs %x", drpCur.rgfAttribs)) ;
|
|
DEBUG((ICGRP, DILVL, "\t rgfAttribsOnOff %x", drpCur.rgfAttribsOnOff)) ;
|
|
DEBUG((ICGRP, DILVL, "\t csrtdsc %d", drpCur.csrtdsc)) ;
|
|
DEBUG((ICGRP, DILVL, "\t pszPattern '%ws'", drpCur.pszPattern)) ;
|
|
|
|
//
|
|
// Print out this particular pattern. If the recursion switch
|
|
// is set then this will desend down the tree.
|
|
//
|
|
|
|
rc = PrintPatterns(&drpCur);
|
|
|
|
mystrcpy(CurDrvDir, szCurDrv);
|
|
|
|
|
|
//
|
|
// Free unneeded memory
|
|
//
|
|
FreeStack( OldDCount );
|
|
|
|
#ifdef _CRTHEAP_
|
|
//
|
|
// Force the crt to release heap we may have taken on recursion
|
|
//
|
|
if (drpCur.rgfSwitchs & RECURSESWITCH) {
|
|
_heapmin();
|
|
}
|
|
#endif
|
|
|
|
return( (int)rc );
|
|
|
|
}
|
|
STATUS
|
|
SetTimeType(
|
|
IN PTCHAR pszTok,
|
|
OUT PDRP pdrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses the 'time' string
|
|
|
|
Arguments:
|
|
|
|
pszTok -
|
|
|
|
Return Value:
|
|
|
|
pdrp - where to place the time type
|
|
|
|
Return: TRUE - recognized all parameters
|
|
FALSE - syntax error.
|
|
|
|
An error is printed if incountered.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG irgch;
|
|
|
|
|
|
//
|
|
// Move over optional ':'
|
|
//
|
|
if (*pszTok == COLON) {
|
|
pszTok++;
|
|
}
|
|
|
|
for( irgch = 0; pszTok[irgch]; irgch++ ) {
|
|
|
|
switch (_totupper(pszTok[irgch])) {
|
|
|
|
case TEXT('C'):
|
|
|
|
pdrp->dwTimeType = CREATE_TIME;
|
|
break;
|
|
|
|
case TEXT('A'):
|
|
|
|
pdrp->dwTimeType = LAST_ACCESS_TIME;
|
|
break;
|
|
|
|
case TEXT('W'):
|
|
|
|
pdrp->dwTimeType = LAST_WRITE_TIME;
|
|
break;
|
|
|
|
default:
|
|
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
argstr1(Fmt14,(unsigned long)((int)(&(pszTok[irgch])))));
|
|
return( FAILURE );
|
|
|
|
} // switch
|
|
} // for
|
|
|
|
return( SUCCESS );
|
|
|
|
}
|
|
|
|
|
|
STATUS
|
|
SetAttribs(
|
|
IN PTCHAR pszTok,
|
|
OUT PDRP pdrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses the 'attribute' string
|
|
|
|
Arguments:
|
|
|
|
pszTok - list of attributes
|
|
|
|
Return Value:
|
|
|
|
pdrp - where to place the attributes recognized.
|
|
this is the parameter structure.
|
|
|
|
Return: TRUE - recognized all parameters
|
|
FALSE - syntax error.
|
|
|
|
An error is printed if incountered.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG irgch;
|
|
BOOLEAN fOff;
|
|
|
|
ULONG rgfAttribs, rgfAttribsOnOff;
|
|
|
|
// rgfAttributes hold 1 bit per recognized attribute. If the bit is
|
|
// on then do something with this attribute. Either select the file
|
|
// with this attribute or select the file without this attribute.
|
|
//
|
|
// rgfAttribsOnOff controls wither to select for the attribute or
|
|
// select without the attribute.
|
|
|
|
//
|
|
// /a triggers selection of all files by default
|
|
// so override the default
|
|
//
|
|
pdrp->rgfAttribs = rgfAttribs = 0;
|
|
pdrp->rgfAttribsOnOff = rgfAttribsOnOff = 0;
|
|
|
|
//
|
|
// Move over optional ':'
|
|
//
|
|
if (*pszTok == COLON) {
|
|
pszTok++;
|
|
}
|
|
|
|
//
|
|
// rgfAttribs and rgfAttribsOnOff must be maintained in the
|
|
// same bit order.
|
|
//
|
|
for( irgch = 0, fOff = FALSE; pszTok[irgch]; irgch++ ) {
|
|
|
|
switch (_totupper(pszTok[irgch])) {
|
|
|
|
case TEXT('H'):
|
|
rgfAttribs |= FILE_ATTRIBUTE_HIDDEN;
|
|
if (fOff) {
|
|
rgfAttribsOnOff &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
fOff = FALSE;
|
|
} else {
|
|
rgfAttribsOnOff |= FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
break;
|
|
case TEXT('S'):
|
|
rgfAttribs |= FILE_ATTRIBUTE_SYSTEM;
|
|
if (fOff) {
|
|
rgfAttribsOnOff &= ~FILE_ATTRIBUTE_SYSTEM;
|
|
fOff = FALSE;
|
|
} else {
|
|
rgfAttribsOnOff |= FILE_ATTRIBUTE_SYSTEM;
|
|
}
|
|
break;
|
|
|
|
case TEXT('D'):
|
|
rgfAttribs |= FILE_ATTRIBUTE_DIRECTORY;
|
|
if (fOff) {
|
|
rgfAttribsOnOff &= ~FILE_ATTRIBUTE_DIRECTORY;
|
|
fOff = FALSE;
|
|
} else {
|
|
rgfAttribsOnOff |= FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
break;
|
|
case TEXT('A'):
|
|
rgfAttribs |= FILE_ATTRIBUTE_ARCHIVE;
|
|
if (fOff) {
|
|
rgfAttribsOnOff &= ~FILE_ATTRIBUTE_ARCHIVE;
|
|
fOff = FALSE;
|
|
} else {
|
|
rgfAttribsOnOff |= FILE_ATTRIBUTE_ARCHIVE;
|
|
}
|
|
break;
|
|
case TEXT('R'):
|
|
rgfAttribs |= FILE_ATTRIBUTE_READONLY;
|
|
if (fOff) {
|
|
rgfAttribsOnOff &= ~FILE_ATTRIBUTE_READONLY;
|
|
fOff = FALSE;
|
|
} else {
|
|
rgfAttribsOnOff |= FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
|
|
break;
|
|
case MINUS:
|
|
if (fOff) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
argstr1(Fmt14,(unsigned long)((int)(&(pszTok[irgch])))));
|
|
return( FAILURE );
|
|
}
|
|
|
|
fOff = TRUE;
|
|
break;
|
|
|
|
default:
|
|
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
argstr1(Fmt14,(unsigned long)((int)(&(pszTok[irgch])))));
|
|
return( FAILURE );
|
|
|
|
} // switch
|
|
} // for
|
|
|
|
pdrp->rgfAttribs = rgfAttribs;
|
|
pdrp->rgfAttribsOnOff = rgfAttribsOnOff;
|
|
|
|
return( SUCCESS );
|
|
|
|
}
|
|
|
|
STATUS
|
|
SetSortDesc(
|
|
IN PTCHAR pszTok,
|
|
OUT PDRP pdrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses the 'attribute' string
|
|
|
|
Arguments:
|
|
|
|
pszTok - list of sort orders
|
|
|
|
Return Value:
|
|
|
|
pdrp - where to place the sort orderings recognized.
|
|
this is the parameter structure.
|
|
|
|
Return: TRUE - recognized all parameters
|
|
FALSE - syntax error.
|
|
|
|
An error is printed if incountered.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG irgch, irgsrtdsc;
|
|
|
|
DEBUG((ICGRP, DILVL, "SetSortDesc for `%ws'", pszTok));
|
|
|
|
//
|
|
// Move over optional ':'
|
|
//
|
|
if (*pszTok == COLON) {
|
|
pszTok++;
|
|
}
|
|
|
|
//
|
|
// Sorting order is based upon the order of entries in rgsrtdsc.
|
|
// srtdsc contains a pointer to a compare function and a flag
|
|
// wither to sort up or down.
|
|
//
|
|
for( irgch = 0, irgsrtdsc = pdrp->csrtdsc ;
|
|
pszTok[irgch] && irgsrtdsc < MAXSORTDESC ;
|
|
irgch++, irgsrtdsc++) {
|
|
|
|
switch (_totupper(pszTok[irgch])) {
|
|
|
|
case TEXT('N'):
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpName;
|
|
break;
|
|
case TEXT('E'):
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpExt;
|
|
break;
|
|
case TEXT('D'):
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpTime;
|
|
break;
|
|
case TEXT('S'):
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpSize;
|
|
break;
|
|
case TEXT('G'):
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpType;
|
|
break;
|
|
case MINUS:
|
|
|
|
//
|
|
// Check that there are not 2 -- in a row
|
|
//
|
|
if (pszTok[irgch+1] == MINUS) {
|
|
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
argstr1(Fmt14,(unsigned long)((int)(&(pszTok[irgch])))));
|
|
return( FAILURE );
|
|
|
|
}
|
|
|
|
pdrp->rgsrtdsc[irgsrtdsc].Order = DESCENDING;
|
|
irgsrtdsc--;
|
|
break;
|
|
|
|
default:
|
|
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
argstr1(Fmt14,(unsigned long)((int)(&(pszTok[irgch])))));
|
|
return( FAILURE );
|
|
|
|
} // switch
|
|
|
|
} // for
|
|
|
|
//
|
|
// Was there any specific sort order (something besides /O
|
|
//
|
|
if (irgsrtdsc == 0) {
|
|
|
|
//
|
|
// Setup default sorting
|
|
//
|
|
pdrp->rgsrtdsc[0].fctCmp = CmpType;
|
|
pdrp->rgsrtdsc[1].fctCmp = CmpName;
|
|
irgsrtdsc = 2;
|
|
}
|
|
|
|
|
|
DEBUG((ICGRP, DILVL, "SetSortDesc count %d", irgsrtdsc));
|
|
pdrp->csrtdsc = irgsrtdsc;
|
|
pdrp->rgsrtdsc[irgsrtdsc].fctCmp = NULL;
|
|
return( SUCCESS );
|
|
}
|
|
|
|
|
|
STATUS
|
|
ParseDirParms (
|
|
IN PTCHAR pszCmdLine,
|
|
OUT PDRP pdrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse the command line translating the tokens into values
|
|
placed in the parameter structure. The values are or'd into
|
|
the parameter structure since this routine is called repeatedly
|
|
to build up values (once for the environment variable DIRCMD
|
|
and once for the actual command line).
|
|
|
|
Arguments:
|
|
|
|
pszCmdLine - pointer to command line user typed
|
|
|
|
|
|
Return Value:
|
|
|
|
pdrp - parameter data structure
|
|
|
|
Return: TRUE - if valid command line.
|
|
FALSE - if not.
|
|
|
|
--*/
|
|
{
|
|
|
|
PTCHAR pszTok;
|
|
|
|
TCHAR szT[10] ;
|
|
USHORT irgchTok;
|
|
BOOLEAN fToggle;
|
|
PPATDSC ppatdscCur;
|
|
|
|
DEBUG((ICGRP, DILVL, "DIR:ParseParms for `%ws'", pszCmdLine));
|
|
|
|
//
|
|
// Tokensize the command line (special delimeters are tokens)
|
|
//
|
|
szT[0] = SwitChar ;
|
|
szT[1] = NULLC ;
|
|
pszTok = TokStr(pszCmdLine, szT, TS_SDTOKENS) ;
|
|
|
|
ppatdscCur = &(pdrp->patdscFirst);
|
|
//
|
|
// If there was a pattern put in place from the environment.
|
|
// just add any new patterns on. So move to the end of the
|
|
// current list.
|
|
//
|
|
if (pdrp->cpatdsc) {
|
|
|
|
while (ppatdscCur->ppatdscNext) {
|
|
|
|
ppatdscCur = ppatdscCur->ppatdscNext;
|
|
|
|
}
|
|
}
|
|
|
|
pdrp->csrtdsc = 0;
|
|
//
|
|
// At this state pszTok will be a series of zero terminated strings.
|
|
// "/o foo" wil be /0o0foo0
|
|
//
|
|
for ( irgchTok = 0; *pszTok ; pszTok += mystrlen(pszTok)+1, irgchTok = 0) {
|
|
|
|
DEBUG((ICGRP, DILVL, "PRIVSW: pszTok = %ws", (ULONG)pszTok)) ;
|
|
|
|
//
|
|
// fToggle control wither to turn off a switch that was set
|
|
// in the DIRCMD environment variable.
|
|
//
|
|
fToggle = FALSE;
|
|
if (pszTok[irgchTok] == (TCHAR)SwitChar) {
|
|
|
|
if (pszTok[irgchTok + 2] == MINUS) {
|
|
|
|
//
|
|
// disable the previously enabled the switch
|
|
//
|
|
fToggle = TRUE;
|
|
irgchTok++;
|
|
}
|
|
|
|
switch (_totupper(pszTok[irgchTok + 2])) {
|
|
|
|
//
|
|
// New Format is the os/2 default HPFS format. The main
|
|
// difference is the filename is at the end of a long display
|
|
// instead of at the beginning
|
|
//
|
|
case TEXT('N'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs |= OLDFORMATSWITCH) : (pdrp->rgfSwitchs |= NEWFORMATSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('W'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= WIDEFORMATSWITCH) : (pdrp->rgfSwitchs |= WIDEFORMATSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('D'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= SORTDOWNFORMATSWITCH) : (pdrp->rgfSwitchs |= SORTDOWNFORMATSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('P'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= PAGEDOUTPUTSWITCH) : (pdrp->rgfSwitchs |= PAGEDOUTPUTSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('B'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= BAREFORMATSWITCH) : (pdrp->rgfSwitchs |= BAREFORMATSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('L'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= LOWERCASEFORMATSWITCH) : (pdrp->rgfSwitchs |= LOWERCASEFORMATSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('S'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= RECURSESWITCH) : (pdrp->rgfSwitchs |= RECURSESWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('C'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= THOUSANDSEPSWITCH) : (pdrp->rgfSwitchs |= THOUSANDSEPSWITCH);
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('X'):
|
|
|
|
//
|
|
// BUGBUG what should happen for toggle.
|
|
//
|
|
pdrp->rgfSwitchs |= SHORTFORMATSWITCH;
|
|
pdrp->rgfSwitchs |= NEWFORMATSWITCH;
|
|
if (pszTok[irgchTok + 3]) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case MINUS:
|
|
|
|
PutStdOut(MSG_HELP_DIR, NOARGS);
|
|
return( FAILURE );
|
|
break;
|
|
|
|
case TEXT('O'):
|
|
|
|
fToggle ? (pdrp->rgfSwitchs ^= SORTSWITCH) : (pdrp->rgfSwitchs |= SORTSWITCH);
|
|
if (fToggle) {
|
|
if ( _tcslen( &(pszTok[irgchTok + 2]) ) > 1) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
pdrp->csrtdsc = 0;
|
|
pdrp->rgsrtdsc[0].fctCmp = NULL;
|
|
break;
|
|
}
|
|
|
|
if (SetSortDesc( &(pszTok[irgchTok+3]), pdrp)) {
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('A'):
|
|
|
|
if (fToggle) {
|
|
if ( _tcslen( &(pszTok[irgchTok + 2]) ) > 1) {
|
|
PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
return( FAILURE );
|
|
}
|
|
pdrp->rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
|
|
pdrp->rgfAttribsOnOff = 0;
|
|
break;
|
|
}
|
|
|
|
if (SetAttribs(&(pszTok[irgchTok + 3]), pdrp) ) {
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
case TEXT('T'):
|
|
|
|
if (fToggle) {
|
|
|
|
//
|
|
// revert to default
|
|
//
|
|
pdrp->dwTimeType = LAST_WRITE_TIME;
|
|
break;
|
|
}
|
|
if (SetTimeType(&(pszTok[irgchTok + 3]), pdrp) ) {
|
|
return( FAILURE );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
szT[0] = SwitChar;
|
|
szT[1] = pszTok[2];
|
|
szT[2] = NULLC;
|
|
PutStdErr(MSG_INVALID_SWITCH,
|
|
ONEARG,
|
|
(ULONG)(&(pszTok[irgchTok + 2])) );
|
|
|
|
return( FAILURE );
|
|
|
|
} // switch
|
|
|
|
//
|
|
// TokStr parses /N as /0N0 so we need to move over the
|
|
// switchar in or to move past the actual switch value
|
|
// in for loop.
|
|
//
|
|
pszTok += 2;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If there already is a list the extend it else put info
|
|
// directly into structure.
|
|
//
|
|
if (pdrp->cpatdsc) {
|
|
|
|
ppatdscCur->ppatdscNext = (PPATDSC)gmkstr(sizeof(PATDSC));
|
|
ppatdscCur = ppatdscCur->ppatdscNext;
|
|
ppatdscCur->ppatdscNext = NULL;
|
|
|
|
}
|
|
|
|
pdrp->cpatdsc++;
|
|
ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(pszTok)*sizeof(TCHAR) + sizeof(TCHAR));
|
|
mystrcpy(ppatdscCur->pszPattern, stripit(pszTok) );
|
|
ppatdscCur->fIsFat = TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
} // for
|
|
|
|
return( SUCCESS );
|
|
}
|
|
|
|
//
|
|
// return a pointer to the a new pattern with wild cards inserted.
|
|
// If no change has occured the passed in pattern is returned.
|
|
//
|
|
// NULL is returned if error.
|
|
//
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if any modification of to the current.
|
|
NOTE that pszInPattern is freed!
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
PTCHAR
|
|
SetWildCards (
|
|
IN PTCHAR pszInPattern,
|
|
IN BOOLEAN fFatDrive
|
|
)
|
|
|
|
{
|
|
|
|
PTCHAR pszNewPattern = NULL;
|
|
PTCHAR pszT;
|
|
USHORT cb;
|
|
DWORD l;
|
|
|
|
DEBUG((ICGRP, DILVL, "DIR:SetWildCards"));
|
|
DEBUG((ICGRP, DILVL, "\t fFatDrive = %x",fFatDrive));
|
|
|
|
//
|
|
// failure to allocate will not return but go through an
|
|
// abort call in gmkstr
|
|
//
|
|
l = max(mystrlen(pszInPattern)+2, MAX_PATH+2) * sizeof(TCHAR);
|
|
pszNewPattern = (PTCHAR)gmkstr(l);
|
|
mystrcpy(pszNewPattern, pszInPattern);
|
|
|
|
//
|
|
// On FAT the default for .xxx is *.xxx while for HPFS .xxx is
|
|
// just a file name.
|
|
//
|
|
// If .xxx or \xxx\.xxx then tranform into *.xxx or \xxx\*.xxx
|
|
//
|
|
// Likewise for no extension the default would be foo.*
|
|
//
|
|
|
|
if (fFatDrive) {
|
|
|
|
pszT = mystrrchr(pszInPattern, PathChar);
|
|
|
|
//
|
|
// If there is no slash then check if pattern begining with
|
|
// a .xxx (making sure not to confuse it with just a . or .. at
|
|
// start of pattern)
|
|
// If there a slash then check for \xxx\.xxx again making sure
|
|
// it is not \xxx\.. or \xxx\.
|
|
//
|
|
if ((!pszT && *pszInPattern == DOT &&
|
|
*(pszInPattern + 1) != NULLC &&
|
|
*(pszInPattern + 1) != DOT ) ||
|
|
(pszT && *(pszT + 1) == DOT &&
|
|
*(pszT + 2) != NULLC &&
|
|
*(pszT + 2) != DOT ) ) {
|
|
|
|
if (pszT) {
|
|
cb = (USHORT)(pszT - pszInPattern + 1);
|
|
//
|
|
// BUGBUG this needs a dbcs version!
|
|
//
|
|
_tcsncpy(pszNewPattern, pszInPattern, cb);
|
|
*(pszNewPattern + cb) = NULLC;
|
|
} else {
|
|
*pszNewPattern = NULLC;
|
|
cb = 0;
|
|
}
|
|
mystrcat(pszNewPattern, TEXT("*"));
|
|
mystrcat(pszNewPattern, pszInPattern + cb);
|
|
// FreeStr( pszInPattern );
|
|
return( pszNewPattern );
|
|
|
|
}
|
|
}
|
|
|
|
return( pszNewPattern );
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
BOOLEAN
|
|
IsFATDrive (
|
|
IN PTCHAR pszPath
|
|
)
|
|
{
|
|
|
|
DWORD cbComponentMax;
|
|
TCHAR szFileSystemName[MAX_PATH + 2];
|
|
TCHAR szDrivePath[ MAX_PATH + 2 ];
|
|
TCHAR szDrive[MAX_PATH + 2];
|
|
|
|
DosErr = 0;
|
|
if (GetDrive(pszPath, (PTCHAR)szDrive)) {
|
|
|
|
DEBUG((ICGRP, DILVL, "DIR:IsFatDrive `%ws'", szDrive));
|
|
|
|
|
|
mystrcpy( szDrivePath, szDrive );
|
|
mystrcat( szDrivePath, TEXT("\\") );
|
|
|
|
//
|
|
// We return that the file system in question is a FAT file system
|
|
// if the component length is more than 12 bytes.
|
|
//
|
|
//
|
|
// BUGBUG This is NOT compatible with the OS/2 definition.
|
|
// There should be a better way then to get cbComponentMax
|
|
// To tell if it is a fat or not
|
|
//
|
|
if (GetVolumeInformation( szDrivePath,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&cbComponentMax,
|
|
NULL,
|
|
szFileSystemName,
|
|
MAX_PATH + 2
|
|
)
|
|
) {
|
|
if (!_tcsicmp(szFileSystemName, TEXT("FAT")) && cbComponentMax == 12) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
} else {
|
|
|
|
DosErr = GetLastError();
|
|
|
|
// if GetVolumeInformation failed because we're a substed drive
|
|
// or a down-level server, don't fail.
|
|
|
|
if (DosErr == ERROR_DIR_NOT_ROOT) {
|
|
DosErr = 0;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// If we could not get the drive then assume it is not FAT.
|
|
// If it is not accessable etc. then that will be caught
|
|
// later.
|
|
//
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
BOOLEAN
|
|
GetDrive(
|
|
IN PTCHAR pszPattern,
|
|
OUT TCHAR szDrive[]
|
|
)
|
|
{
|
|
TCHAR szCurDrv[MAX_PATH + 2];
|
|
PTCHAR pszT;
|
|
TCHAR ch = NULLC;
|
|
|
|
if (pszPattern == NULL) {
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
//
|
|
// assume we have the default case with no drive
|
|
// letter specified
|
|
//
|
|
GetDir((PTCHAR)szCurDrv,GD_DEFAULT);
|
|
szDrive[0] = szCurDrv[0];
|
|
|
|
|
|
//
|
|
// If we have a UNC name do not return a drive. No
|
|
// drive operation would be allowed
|
|
// For everything else a some drive operation would
|
|
// be valid
|
|
//
|
|
|
|
// handle UNC names with drive letter (allowed in DOS)
|
|
if ((pszPattern[1] == COLON) && (pszPattern[2] == BSLASH) &&
|
|
(pszPattern[3] == BSLASH)) {
|
|
mystrcpy(&pszPattern[0],&pszPattern[2]);
|
|
}
|
|
|
|
if ((pszPattern[0] == BSLASH) && (pszPattern[1] == BSLASH)) {
|
|
|
|
pszT = mystrchr(&(pszPattern[2]), BSLASH);
|
|
if (pszT == NULL) {
|
|
|
|
//
|
|
// badly formed unc name
|
|
//
|
|
return( FALSE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// look for '\\foo\bar\xxx'
|
|
//
|
|
pszT = mystrchr(pszT + 1, BSLASH);
|
|
//
|
|
// pszPattern contains more then just share point
|
|
//
|
|
if (pszT != NULL) {
|
|
|
|
ch = *pszT;
|
|
*pszT = NULLC;
|
|
}
|
|
mystrcpy(szDrive, pszPattern);
|
|
if (ch != NULLC) {
|
|
|
|
*pszT = ch;
|
|
|
|
}
|
|
return ( TRUE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Must be a drive letter
|
|
//
|
|
|
|
if ((pszPattern[0]) && (pszPattern[1] == COLON)) {
|
|
szDrive[0] = _totupper(*pszPattern);
|
|
}
|
|
|
|
szDrive[1] = COLON;
|
|
szDrive[2] = NULLC;
|
|
return( TRUE );
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
STATUS
|
|
PrintPatterns (
|
|
IN PDRP pdpr
|
|
)
|
|
{
|
|
|
|
TCHAR szDriveCur[MAX_PATH + 2];
|
|
TCHAR szDrivePrev[MAX_PATH + 2];
|
|
TCHAR szDriveNext[MAX_PATH + 2];
|
|
PPATDSC ppatdscCur;
|
|
PPATDSC ppatdscX;
|
|
PFS pfsFirst;
|
|
PFS pfsCur;
|
|
PFS pfsPrev;
|
|
BOOLEAN fPrintFreeSpace;
|
|
ULONG cffTotal;
|
|
LARGE_INTEGER cbFileTotal = {0,0};
|
|
ULONG i;
|
|
STATUS rc;
|
|
|
|
|
|
PSCREEN pscr;
|
|
|
|
//
|
|
// Creating the console output is done early since error message
|
|
// should go through the console. If PrintPattern is called
|
|
// many times in the future this will be required since the
|
|
// error message should need to be under pause control
|
|
//
|
|
|
|
if (OpenScreen( &pscr) == FAILURE) {
|
|
|
|
//
|
|
// can't print out
|
|
//
|
|
return( FAILURE );
|
|
}
|
|
|
|
//
|
|
// This will be NULL if for any reason we STDOUT is not a valid
|
|
// console handle, such as file redirection or redirection to a
|
|
// non-console device. In that case we turn off any paged output.
|
|
//
|
|
if (!(pscr->hndScreen)) {
|
|
|
|
pdpr->rgfSwitchs &= ~PAGEDOUTPUTSWITCH;
|
|
|
|
} else {
|
|
|
|
if (pdpr->rgfSwitchs & PAGEDOUTPUTSWITCH) {
|
|
|
|
//
|
|
// Default will be the size of the screen - 1
|
|
// subtract 1 to account for the current line
|
|
//
|
|
SetPause( pscr, pscr->crowMax - 1 );
|
|
}
|
|
}
|
|
|
|
//
|
|
// map the sorting down to wide format
|
|
//
|
|
if (pdpr->rgfSwitchs & SORTDOWNFORMATSWITCH) {
|
|
pdpr->rgfSwitchs |= WIDEFORMATSWITCH;
|
|
}
|
|
|
|
//
|
|
// determine FAT drive from original pattern.
|
|
// Used in several places to control name format etc.
|
|
//
|
|
DosErr = 0;
|
|
|
|
if (BuildFSFromPatterns(pdpr, TRUE, &pfsFirst) == FAILURE) {
|
|
|
|
return( FAILURE );
|
|
|
|
}
|
|
|
|
pfsPrev = NULL;
|
|
fPrintFreeSpace = FALSE;
|
|
|
|
for( pfsCur = pfsFirst; pfsCur; pfsCur = pfsCur->pfsNext) {
|
|
|
|
if (pfsCur->fIsFat) {
|
|
|
|
//
|
|
// This is used in the display code to control formating
|
|
// for all of the strange little rules DOS has.
|
|
//
|
|
pdpr->rgfSwitchs | FATFORMAT;
|
|
|
|
//
|
|
// Make sure if FAT only LAST_WRITE_TIME is allowed
|
|
// since this is all that is supported.
|
|
//
|
|
if (pdpr->dwTimeType != LAST_WRITE_TIME) {
|
|
|
|
PutStdErr(MSG_TIME_NOT_SUPPORTED, NOARGS);
|
|
return( FAILURE );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If it is not fat then print out in new format that
|
|
// puts names to the right to allow for extra long names
|
|
//
|
|
if (!(pdpr->rgfSwitchs & OLDFORMATSWITCH)) {
|
|
pdpr->rgfSwitchs |= NEWFORMATSWITCH;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// do not print out header information if in bare mode
|
|
//
|
|
if (!(pdpr->rgfSwitchs & BAREFORMATSWITCH)) {
|
|
|
|
//
|
|
// szDrive is use to detect change of drive and also
|
|
// used to print free space on drive
|
|
//
|
|
GetDrive(pfsCur->pszDir, szDriveCur);
|
|
|
|
//
|
|
// pfsPrev == NULL will mean first time through
|
|
//
|
|
if (pfsPrev) {
|
|
|
|
//
|
|
// Only print out volume information if it changes
|
|
// pszDir should have fully qualified paths by now.
|
|
//
|
|
GetDrive(pfsPrev->pszDir, szDrivePrev);
|
|
|
|
if (_tcsicmp(szDriveCur, szDrivePrev)) {
|
|
|
|
//
|
|
// This prints from szDriveCur
|
|
//
|
|
if (WriteEol(pscr) == SUCCESS) {
|
|
if (DisplayVolInfo( pscr, pfsCur->pszDir )!= SUCCESS) {
|
|
|
|
//
|
|
// do not bother to continue if we could not
|
|
// get volume information.
|
|
//
|
|
return( FAILURE );
|
|
|
|
}
|
|
} else {
|
|
|
|
return( FAILURE );
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// This prints from szDriveCur
|
|
//
|
|
if (DisplayVolInfo( pscr, pfsCur->pszDir ) != SUCCESS) {
|
|
|
|
//
|
|
// do not bother to continue if we could not
|
|
// get volume information.
|
|
//
|
|
return( FAILURE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Walk down the tree printing each directory or just return
|
|
// after specificied directory.
|
|
//
|
|
cffTotal = 0;
|
|
if (!(pdpr->rgfSwitchs & BAREFORMATSWITCH )) {
|
|
WriteEol(pscr);
|
|
}
|
|
rc = DirWalkAndProcess(DisplayFileList,
|
|
NULL,
|
|
pscr,
|
|
&cffTotal,
|
|
&cbFileTotal,
|
|
pfsCur,
|
|
pdpr,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ((rc != FAILURE) && (cffTotal != 0)) {
|
|
|
|
if (!(pdpr->rgfSwitchs & BAREFORMATSWITCH )) {
|
|
|
|
//
|
|
// if there an another set of patterns check if the
|
|
// drive will change with that set. If so then print out
|
|
// the free space on the current drive. This will miss
|
|
// the final 1 which is picked up right after this loop.
|
|
// and controled by the fPrintFreeSpace flag.
|
|
//
|
|
if (pfsCur->pfsNext) {
|
|
|
|
GetDrive(pfsCur->pfsNext->pszDir, szDriveNext);
|
|
if (_tcsicmp(szDriveNext, szDriveCur)) {
|
|
|
|
CHECKSTATUS(DisplayDiskFreeSpace(pscr, szDriveCur, pdpr->rgfSwitchs ));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fPrintFreeSpace = TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
} else if (!CtrlCSeen) {
|
|
|
|
//
|
|
// Stop the tail end DisplayDiskFreeSpace call at the end
|
|
//
|
|
fPrintFreeSpace = FALSE;
|
|
//
|
|
// If recursing then will not have printed
|
|
// if ((cffTotal == 0) && (pdpr->rgfSwitchs & RECURSESWITCH)) {
|
|
if ((cffTotal == 0)) {
|
|
|
|
PutStdErr(MSG_FILE_NOT_FOUND, NOARGS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FreeStr(pfsCur->pszDir);
|
|
for(i = 1, ppatdscCur = pfsCur->ppatdsc;
|
|
i <= pfsCur->cpatdsc;
|
|
i++, ppatdscCur = ppatdscX) {
|
|
|
|
ppatdscX = ppatdscCur->ppatdscNext;
|
|
FreeStr(ppatdscCur->pszPattern);
|
|
FreeStr(ppatdscCur->pszDir);
|
|
FreeStr((PTCHAR)ppatdscCur);
|
|
}
|
|
//
|
|
if (pfsPrev) {
|
|
|
|
FreeStr((PTCHAR)pfsPrev);
|
|
}
|
|
pfsPrev = pfsCur;
|
|
}
|
|
|
|
if ((pdpr->rgfSwitchs & RECURSESWITCH) &&
|
|
!(pdpr->rgfSwitchs & BAREFORMATSWITCH ) &&
|
|
(cffTotal)) {
|
|
|
|
if (WriteEol(pscr) == SUCCESS) {
|
|
if (DisplayTotals(pscr, cffTotal, &cbFileTotal, pdpr->rgfSwitchs) != SUCCESS) {
|
|
return( FAILURE );
|
|
}
|
|
} else {
|
|
|
|
return( FAILURE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// For case of single volume, the above loop would not have
|
|
// printed free space.
|
|
//
|
|
|
|
if (fPrintFreeSpace) {
|
|
|
|
CHECKSTATUS(DisplayDiskFreeSpace(pscr, szDriveCur, pdpr->rgfSwitchs));
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
CmpName(
|
|
const void *elem1,
|
|
const void *elem2
|
|
)
|
|
{
|
|
int result;
|
|
|
|
result = lstrcmpi( ((PFF)(* (PPFF)elem1))->data.cFileName, ((PFF)(* (PPFF)elem2))->data.cFileName);
|
|
return result;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
CmpExt(
|
|
const void *pszElem1,
|
|
const void *pszElem2
|
|
)
|
|
{
|
|
PTCHAR pszElem1T, pszElem2T;
|
|
int rc;
|
|
|
|
|
|
//
|
|
// Move pointer to name to make it all easier to read
|
|
//
|
|
pszElem1 = &(((PFF)(* (PPFF)pszElem1))->data.cFileName);
|
|
pszElem2 = &(((PFF)(* (PPFF)pszElem2))->data.cFileName);
|
|
|
|
//
|
|
// Locate the extensions if any
|
|
//
|
|
if (((pszElem1T = mystrrchr( pszElem1, DOT)) == NULL ) ||
|
|
|
|
(!_tcscmp(TEXT(".."),pszElem1) || !_tcscmp(TEXT("."),pszElem1)) ) {
|
|
|
|
//
|
|
// If no extension then point to end of string
|
|
//
|
|
pszElem1T = ((PTCHAR)pszElem1) + mystrlen(pszElem1 );
|
|
}
|
|
|
|
if (((pszElem2T = mystrrchr( pszElem2, DOT)) == NULL ) ||
|
|
(!_tcscmp(TEXT(".."),pszElem2) || !_tcscmp(TEXT("."),pszElem2)) ) {
|
|
|
|
//
|
|
// If no extension then point to end of string
|
|
//
|
|
pszElem2T = ((PTCHAR)pszElem2) + mystrlen(pszElem2 );
|
|
}
|
|
rc = lstrcmpi( pszElem1T, pszElem2T );
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
CmpTime(
|
|
const void *pszElem1,
|
|
const void *pszElem2
|
|
)
|
|
{
|
|
|
|
LPFILETIME pft1, pft2;
|
|
|
|
|
|
switch (dwTimeType) {
|
|
|
|
case LAST_ACCESS_TIME:
|
|
|
|
pft1 = & ((* (PPFF)pszElem1)->data.ftLastAccessTime);
|
|
pft2 = & ((* (PPFF)pszElem2)->data.ftLastAccessTime);
|
|
break;
|
|
|
|
case LAST_WRITE_TIME:
|
|
|
|
pft1 = & ((* (PPFF)pszElem1)->data.ftLastWriteTime);
|
|
pft2 = & ((* (PPFF)pszElem2)->data.ftLastWriteTime);
|
|
break;
|
|
|
|
case CREATE_TIME:
|
|
|
|
pft1 = & ((* (PPFF)pszElem1)->data.ftCreationTime);
|
|
pft2 = & ((* (PPFF)pszElem2)->data.ftCreationTime);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
return(CompareFileTime( pft1, pft2 ) );
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
CmpSize(
|
|
const void * pszElem1,
|
|
const void * pszElem2
|
|
)
|
|
{
|
|
ULARGE_INTEGER ul1, ul2;
|
|
|
|
ul1.HighPart = (* (PPFF)pszElem1)->data.nFileSizeHigh;
|
|
ul2.HighPart = (* (PPFF)pszElem2)->data.nFileSizeHigh;
|
|
ul1.LowPart = (* (PPFF)pszElem1)->data.nFileSizeLow;
|
|
ul2.LowPart = (* (PPFF)pszElem2)->data.nFileSizeLow;
|
|
|
|
if (ul1.QuadPart < ul2.QuadPart)
|
|
return -1;
|
|
else
|
|
if (ul1.QuadPart > ul2.QuadPart)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
CmpType(
|
|
const void *pszElem1,
|
|
const void *pszElem2
|
|
)
|
|
{
|
|
|
|
//
|
|
// This dependents upon FILE_ATTRIBUTE_DIRECTORY not being the high bit.
|
|
//
|
|
return( (( (* (PPFF)pszElem2)->data.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) -
|
|
(( (* (PPFF)pszElem1)->data.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) );
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
int
|
|
_cdecl
|
|
SortCompare(
|
|
IN const void * elem1,
|
|
IN const void * elem2
|
|
)
|
|
{
|
|
|
|
ULONG irgsrt;
|
|
int rc;
|
|
|
|
//
|
|
// prgsrtdsc is set in SortFileList
|
|
//
|
|
for (irgsrt = 0; prgsrtdsc[irgsrt].fctCmp; irgsrt++) {
|
|
|
|
if (prgsrtdsc[irgsrt].Order == DESCENDING) {
|
|
|
|
if (rc = prgsrtdsc[irgsrt].fctCmp(elem2, elem1)) {
|
|
return( rc );
|
|
}
|
|
|
|
} else {
|
|
|
|
if (rc = prgsrtdsc[irgsrt].fctCmp(elem1, elem2)) {
|
|
return( rc );
|
|
}
|
|
|
|
}
|
|
}
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return:
|
|
|
|
--*/
|
|
VOID
|
|
SortFileList(
|
|
IN PFS pfsFiles,
|
|
IN PSORTDESC prgsrtdscLocal,
|
|
IN ULONG dwTimeTypeLocal
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
// Set these globally to handle fixed parameters list for qsort
|
|
//
|
|
dwTimeType = dwTimeTypeLocal;
|
|
prgsrtdsc = prgsrtdscLocal;
|
|
|
|
//
|
|
// Make sure there is something to sort
|
|
//
|
|
if (pfsFiles->cff) {
|
|
if (prgsrtdsc[0].fctCmp) {
|
|
qsort(pfsFiles->prgpff,
|
|
pfsFiles->cff,
|
|
sizeof(PTCHAR),
|
|
SortCompare);
|
|
}
|
|
}
|
|
|
|
|
|
}
|