/* $Id$ * * DIR.C - dir internal command. * * * History: * * 01/29/97 (Tim Norman) * started. * * 06/13/97 (Tim Norman) * Fixed code. * * 07/12/97 (Tim Norman) * Fixed bug that caused the root directory to be unlistable * * 07/12/97 (Marc Desrochers) * Changed to use maxx, maxy instead of findxy() * * 06/08/98 (Rob Lake) * Added compatibility for /w in dir * * 06/09/98 (Rob Lake) * Compatibility for dir/s started * Tested that program finds directories off root fine * * 06/10/98 (Rob Lake) * do_recurse saves the cwd and also stores it in Root * build_tree adds the cwd to the beginning of its' entries * Program runs fine, added print_tree -- works fine.. as EXE, * program won't work properly as COM. * * 06/11/98 (Rob Lake) * Found problem that caused COM not to work * * 06/12/98 (Rob Lake) * debugged... * added free mem routine * * 06/13/98 (Rob Lake) * debugged the free mem routine * debugged whole thing some more * Notes: * ReadDir stores Root name and _Read_Dir does the hard work * PrintDir prints Root and _Print_Dir does the hard work * KillDir kills Root _after_ _Kill_Dir does the hard work * Integrated program into DIR.C(this file) and made some same * changes throughout * * 06/14/98 (Rob Lake) * Cleaned up code a bit, added comments * * 06/16/98 (Rob Lake) * Added error checking to my previously added routines * * 06/17/98 (Rob Lake) * Rewrote recursive functions, again! Most other recursive * functions are now obsolete -- ReadDir, PrintDir, _Print_Dir, * KillDir and _Kill_Dir. do_recurse does what PrintDir did * and _Read_Dir did what it did before along with what _Print_Dir * did. Makes /s a lot faster! * Reports 2 more files/dirs that MS-DOS actually reports * when used in root directory(is this because dir defaults * to look for read only files?) * Added support for /b, /a and /l * Made error message similar to DOS error messages * Added help screen * * 06/20/98 (Rob Lake) * Added check for /-(switch) to turn off previously defined * switches. * Added ability to check for DIRCMD in environment and * process it * * 06/21/98 (Rob Lake) * Fixed up /B * Now can dir *.ext/X, no spaces! * * 06/29/98 (Rob Lake) * error message now found in command.h * * 07/08/1998 (John P. Price) * removed extra returns; closer to MSDOS * fixed wide display so that an extra return is not displayed * when there is five filenames in the last line. * * 07/12/98 (Rob Lake) * Changed error messages * * 27-Jul-1998 (John P Price ) * added config.h include * * * 04-Dec-1998 (Eric Kohl ) * Converted source code to Win32, except recursive dir ("dir /s"). * * 10-Dec-1998 (Eric Kohl ) * Fixed recursive dir ("dir /s"). * * 14-Dec-1998 (Eric Kohl ) * Converted to Win32 directory functions and * fixed some output bugs. There are still some more ;) * * 10-Jan-1999 (Eric Kohl ) * Added "/N" and "/4" options, "/O" is a dummy. * Added locale support. * * 20-Jan-1999 (Eric Kohl ) * Redirection safe! * * 01-Mar-1999 (Eric Kohl ) * Replaced all runtime io functions by their Win32 counterparts. * * 23-Feb-2001 (Carl Nettelblad ) * dir /s now works in deeper trees */ #include "config.h" #ifdef INCLUDE_CMD_DIR #include #include #include #include #include #include #include "cmd.h" typedef BOOL STDCALL (*PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); /* flag definitions */ enum { 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 */ }; /* Globally save the # of dirs, files and bytes, * probabaly later pass them to functions. Rob Lake */ static ULONG recurse_dir_cnt; static ULONG recurse_file_cnt; static ULARGE_INTEGER recurse_bytes; /* * help * * displays help screen for dir * Rob Lake */ static VOID Help (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" )); } /* * DirReadParam * * read the parameters from the command line */ static BOOL DirReadParam (LPTSTR line, LPTSTR *param, LPDWORD lpFlags) { INT slash = 0; if (!line) return TRUE; *param = NULL; /* scan the command line, processing switches */ while (*line) { /* process switch */ if (*line == _T('/') || slash) { 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 { error_invalid_switch ((TCHAR)_totupper (*line)); return FALSE; } line++; continue; } else { 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; } } /* process parameter */ if (!_istspace (*line)) { if (*param) { error_too_many_parameters (*param); return FALSE; } *param = line; /* skip to end of line or next whitespace or next / */ while (*line && !_istspace (*line) && *line != _T('/')) line++; /* if end of line, return */ if (!*line) return TRUE; /* if parameter, remember to process it later */ if (*line == _T('/')) slash = 1; *line++ = 0; continue; } line++; } if (slash) { error_invalid_switch ((TCHAR)_totupper (*line)); return FALSE; } return TRUE; } /* * ExtendFilespec * * extend the filespec, possibly adding wildcards */ static VOID ExtendFilespec (LPTSTR file) { INT len = 0; if (!file) return; /* if no file spec, change to "*.*" */ if (*file == _T('\0')) { _tcscpy (file, _T("*.*")); return; } /* if starts with . add * in front */ if (*file == _T('.')) { memmove (&file[1], &file[0], (_tcslen (file) + 1) * sizeof(TCHAR)); file[0] = _T('*'); } /* if no . add .* */ if (!_tcschr (file, _T('.'))) { _tcscat (file, _T(".*")); return; } /* if last character is '.' add '*' */ len = _tcslen (file); if (file[len - 1] == _T('.')) { _tcscat (file, _T("*")); return; } } /* * dir_parse_pathspec * * split the pathspec into drive, directory, and filespec */ static INT DirParsePathspec (LPTSTR szPathspec, LPTSTR szPath, LPTSTR szFilespec) { TCHAR szOrigPath[MAX_PATH]; LPTSTR start; LPTSTR tmp; INT i; BOOL bWildcards = FALSE; GetCurrentDirectory (MAX_PATH, szOrigPath); /* get the drive and change to it */ if (szPathspec[1] == _T(':')) { TCHAR szRootPath[] = _T("A:"); szRootPath[0] = szPathspec[0]; start = szPathspec + 2; SetCurrentDirectory (szRootPath); } else { start = szPathspec; } /* check for wildcards */ for (i = 0; szPathspec[i]; i++) { if (szPathspec[i] == _T('*') || szPathspec[i] == _T('?')) bWildcards = TRUE; } /* check if this spec is a directory */ if (!bWildcards) { if (SetCurrentDirectory (szPathspec)) { _tcscpy (szFilespec, _T("*.*")); if (!GetCurrentDirectory (MAX_PATH, szPath)) { szFilespec[0] = _T('\0'); SetCurrentDirectory (szOrigPath); error_out_of_memory(); return 1; } SetCurrentDirectory (szOrigPath); return 0; } } /* find the file spec */ tmp = _tcsrchr (start, _T('\\')); /* if no path is specified */ if (!tmp) { _tcscpy (szFilespec, start); ExtendFilespec (szFilespec); if (!GetCurrentDirectory (MAX_PATH, szPath)) { szFilespec[0] = _T('\0'); SetCurrentDirectory (szOrigPath); error_out_of_memory(); return 1; } SetCurrentDirectory (szOrigPath); return 0; } /* get the filename */ _tcscpy (szFilespec, tmp+1); ExtendFilespec (szFilespec); *tmp = _T('\0'); /* change to this directory and get its full name */ if (!SetCurrentDirectory (start)) { *tmp = _T('\\'); szFilespec[0] = _T('\0'); SetCurrentDirectory (szOrigPath); error_path_not_found (); return 1; } if (!GetCurrentDirectory (MAX_PATH, szPath)) { *tmp = _T('\\'); szFilespec[0] = _T('\0'); SetCurrentDirectory (szOrigPath); error_out_of_memory (); return 1; } *tmp = _T('\\'); SetCurrentDirectory (szOrigPath); return 0; } /* * incline * * increment our line if paginating, display message at end of screen */ static BOOL IncLine (LPINT pLine, DWORD dwFlags) { if (!(dwFlags & DIR_PAGE)) return FALSE; (*pLine)++; if (*pLine >= (int)maxy - 2) { *pLine = 0; return (PagePrompt () == PROMPT_BREAK); } return FALSE; } /* * PrintDirectoryHeader * * print the header for the dir command */ static BOOL PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) { TCHAR szRootName[MAX_PATH]; TCHAR szVolName[80]; DWORD dwSerialNr; LPTSTR p; if (dwFlags & DIR_BARE) return(TRUE); /* build usable root path */ if (szPath[1] == _T(':') && szPath[2] == _T('\\')) { /* normal path */ szRootName[0] = szPath[0]; szRootName[1] = _T(':'); szRootName[2] = _T('\\'); szRootName[3] = 0; } else if (szPath[0] == _T('\\') && szPath[1] == _T('\\')) { /* UNC path */ p = _tcschr(&szPath[2], _T('\\')); if (p == NULL) { error_invalid_drive(); return(FALSE); } p = _tcschr(p+1, _T('\\')); if (p == NULL) { _tcscpy(szRootName, szPath); _tcscat(szRootName, _T("\\")); } else { *p = 0; _tcscpy(szRootName, szPath); _tcscat(szRootName, _T("\\")); *p = _T('\\'); } } else { error_invalid_drive(); return(FALSE); } /* get the media ID of the drive */ if (!GetVolumeInformation(szRootName, szVolName, 80, &dwSerialNr, NULL, NULL, NULL, 0)) { error_invalid_drive(); return(FALSE); } /* print drive info */ ConOutPrintf(_T(" Volume in drive %c"), szRootName[0]); if (szVolName[0] != _T('\0')) ConOutPrintf(_T(" is %s\n"), szVolName); 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); } /* * convert * * insert commas into a number */ static INT ConvertULong (ULONG num, LPTSTR des, INT len) { TCHAR temp[32]; INT c = 0; INT n = 0; if (num == 0) { des[0] = _T('0'); des[1] = _T('\0'); n = 1; } else { temp[31] = 0; while (num > 0) { if (((c + 1) % (nNumberGroups + 1)) == 0) temp[30 - c++] = cThousandSeparator; temp[30 - c++] = (TCHAR)(num % 10) + _T('0'); num /= 10; } for (n = 0; n <= c; n++) des[n] = temp[31 - c + n]; } return n; } static INT ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) { TCHAR temp[32]; INT c = 0; INT n = 0; if (num.QuadPart == 0) { des[0] = _T('0'); des[1] = _T('\0'); n = 1; } else { temp[31] = 0; while (num.QuadPart > 0) { if (((c + 1) % (nNumberGroups + 1)) == 0) temp[30 - c++] = cThousandSeparator; temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0'); num.QuadPart /= 10; } for (n = 0; n <= c; n++) des[n] = temp[31 - c + n]; } return n; } static VOID PrintFileDateTime (LPSYSTEMTIME dt, DWORD dwFlags) { WORD wYear = (dwFlags & DIR_FOUR) ? 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); break; case 1: /* ddmmyy */ ConOutPrintf (_T("%.2d%c%.2d%c%d"), dt->wDay, cDateSeparator, dt->wMonth, cDateSeparator, wYear); break; case 2: /* yymmdd */ ConOutPrintf (_T("%d%c%.2d%c%.2d"), wYear, cDateSeparator, dt->wMonth, cDateSeparator, dt->wDay); break; } 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)), cTimeSeparator, dt->wMinute, (dt->wHour <= 11 ? 'a' : 'p')); break; case 1: /* 24 hour format */ ConOutPrintf (_T(" %2d%c%.2u"), dt->wHour, cTimeSeparator, dt->wMinute); break; } } static VOID GetUserDiskFreeSpace(LPCTSTR lpRoot, PULARGE_INTEGER lpFreeSpace) { PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx; HINSTANCE hInstance; DWORD dwSecPerCl; DWORD dwBytPerSec; DWORD dwFreeCl; DWORD dwTotCl; lpFreeSpace->QuadPart = 0; hInstance = LoadLibrary(_T("KERNEL32")); if (hInstance != NULL) { pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance, #ifdef _UNICODE _T("GetDiskFreeSpaceExW")); #else _T("GetDiskFreeSpaceExA")); #endif if (pGetFreeDiskSpaceEx != NULL) { if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, NULL, NULL) == TRUE) return; } FreeLibrary(hInstance); } GetDiskFreeSpace(lpRoot, &dwSecPerCl, &dwBytPerSec, &dwFreeCl, &dwTotCl); lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl; } /* * print_summary: prints dir summary * Added by Rob Lake 06/17/98 to compact code * Just copied Tim's Code and patched it a bit * */ static INT PrintSummary(LPTSTR szPath, ULONG ulFiles, ULONG ulDirs, ULARGE_INTEGER bytes, LPINT pLine, DWORD dwFlags) { TCHAR buffer[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; return 0; } /* * dir_list * * list the files in the directory */ static INT DirList (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) { 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; 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) { /* 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; } /* 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) { ConOutPrintf (_T(" Directory of %s\n"), szPath); if (IncLine (pLine, dwFlags)) return 1; ConOutPrintf (_T("\n")); if (IncLine (pLine, dwFlags)) return 1; } /* For counting columns of output */ count = 0; 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) { _tcslwr (file.cAlternateFileName); } if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0) { ULARGE_INTEGER uliSize; if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (file.cAlternateFileName[0] == _T('\0')) _stprintf (buffer, _T("[%s]"), file.cFileName); else _stprintf (buffer, _T("[%s]"), file.cAlternateFileName); dircount++; } else { if (file.cAlternateFileName[0] == _T('\0')) _stprintf (buffer, _T("%s"), file.cFileName); else _stprintf (buffer, _T("%s"), file.cAlternateFileName); filecount++; } ConOutPrintf (_T("%-15s"), buffer); count++; if (count == 5) { /* output 5 columns */ 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; } 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_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; } } while (FindNextFile (hFile, &file)); FindClose (hFile); /* Rob Lake, need to make clean output */ /* JPP 07/08/1998 added check for count != 0 */ if ((dwFlags & DIR_WIDE) && (count != 0)) { ConOutPrintf (_T("\n")); if (IncLine (pLine, dwFlags)) return 1; } if (filecount || dircount) { recurse_dir_cnt += dircount; recurse_file_cnt += filecount; recurse_bytes.QuadPart += bytecount.QuadPart; /* print_summary */ if (PrintSummary (szPath, filecount, dircount, bytecount, pLine, dwFlags)) return 1; } else { error_file_not_found (); return 1; } return 0; } /* * _Read_Dir: Actual function that does recursive listing */ static INT DirRead (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) { TCHAR szFullPath[MAX_PATH]; WIN32_FIND_DATA file; HANDLE hFile; _tcscpy (szFullPath, szPath); if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) _tcscat (szFullPath, _T("\\")); _tcscat (szFullPath, szFilespec); hFile = FindFirstFile (szFullPath, &file); if (hFile == INVALID_HANDLE_VALUE) return 1; do { /* don't list "." and ".." */ if (_tcscmp (file.cFileName, _T(".")) == 0 || _tcscmp (file.cFileName, _T("..")) == 0) continue; if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { _tcscpy (szFullPath, szPath); if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) _tcscat (szFullPath, _T("\\")); _tcscat (szFullPath, file.cFileName); if (DirList (szFullPath, szFilespec, pLine, dwFlags)) { FindClose (hFile); return 1; } if ((dwFlags & DIR_BARE) == 0) { ConOutPrintf (_T("\n")); if (IncLine (pLine, dwFlags) != 0) return 1; ConOutPrintf (_T("\n")); if (IncLine (pLine, dwFlags) != 0) return 1; } if (DirRead (szFullPath, szFilespec, pLine, dwFlags) == 1) { FindClose (hFile); return 1; } } } while (FindNextFile (hFile, &file)); if (!FindClose (hFile)) return 1; 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 * * internal dir command */ INT CommandDir (LPTSTR first, LPTSTR rest) { DWORD dwFlags = DIR_NEW | DIR_FOUR; TCHAR dircmd[256]; TCHAR szPath[MAX_PATH]; TCHAR szFilespec[MAX_PATH]; LPTSTR param; INT nLine = 0; recurse_dir_cnt = 0L; recurse_file_cnt = 0L; recurse_bytes.QuadPart = 0; /* read the parameters from the DIRCMD environment variable */ if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) { if (!DirReadParam (dircmd, ¶m, &dwFlags)) return 1; } /* read the parameters */ if (!DirReadParam (rest, ¶m, &dwFlags)) return 1; /* default to current directory */ if (!param) param = _T("."); /* parse the directory info */ if (DirParsePathspec (param, szPath, szFilespec)) return 1; if (dwFlags & DIR_RECURSE) { if (IncLine (&nLine, dwFlags)) return 0; if (DirRecurse (szPath, szFilespec, &nLine, dwFlags)) return 1; return 0; } /* print the header */ if (!PrintDirectoryHeader (szPath, &nLine, dwFlags)) return 1; if (DirList (szPath, szFilespec, &nLine, dwFlags)) return 1; return 0; } #endif /* EOF */