mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 07:08:19 +00:00
387 lines
10 KiB
C
387 lines
10 KiB
C
/*
|
|
* PROJECT: ReactOS
|
|
* LICENSE: GNU GPLv2 only as published by the Free Software Foundation
|
|
* PURPOSE: Implements tree.com command similar to Windows
|
|
* PROGRAMMERS: Asif Bahrainwala (asif_bahrainwala@hotmail.com)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
|
|
#include <conutils.h>
|
|
|
|
#include "resource.h"
|
|
|
|
#define STR_MAX 2048
|
|
|
|
static VOID GetDirectoryStructure(PWSTR strPath, UINT width, PCWSTR prevLine);
|
|
|
|
/* If this flag is set to true, files will also be listed within the folder structure */
|
|
BOOL bShowFiles = FALSE;
|
|
|
|
/* If this flag is true, ASCII characters will be used instead of UNICODE ones */
|
|
BOOL bUseAscii = FALSE;
|
|
|
|
/**
|
|
* @name: HasSubFolder
|
|
*
|
|
* @param strPath
|
|
* Must specify folder name
|
|
*
|
|
* @return
|
|
* true if folder has sub-folders, else will return false
|
|
*/
|
|
static BOOL HasSubFolder(PCWSTR strPath1)
|
|
{
|
|
BOOL ret = FALSE;
|
|
WIN32_FIND_DATAW FindFileData;
|
|
HANDLE hFind = NULL;
|
|
static WCHAR strPath[STR_MAX] = L"";
|
|
ZeroMemory(strPath, sizeof(strPath));
|
|
|
|
wcscat(strPath, strPath1);
|
|
wcscat(strPath, L"\\*.");
|
|
|
|
hFind = FindFirstFileW(strPath, &FindFileData);
|
|
do
|
|
{
|
|
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
if (wcscmp(FindFileData.cFileName, L".") == 0 ||
|
|
wcscmp(FindFileData.cFileName, L"..") == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ret = TRUE; // Sub-folder found
|
|
break;
|
|
}
|
|
}
|
|
while (FindNextFileW(hFind, &FindFileData));
|
|
|
|
FindClose(hFind);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @name: DrawTree
|
|
*
|
|
* @param strPath
|
|
* Must specify folder name
|
|
*
|
|
* @param arrFolder
|
|
* must be a list of folder names to be drawn in tree format
|
|
*
|
|
* @param width
|
|
* specifies drawing distance for correct formatting of tree structure being drawn on console screen
|
|
* used internally for adding spaces
|
|
*
|
|
* @param prevLine
|
|
* used internally for formatting reasons
|
|
*
|
|
* @return
|
|
* void
|
|
*/
|
|
static VOID DrawTree(PCWSTR strPath,
|
|
const WIN32_FIND_DATAW* arrFolder,
|
|
const size_t szArr,
|
|
UINT width,
|
|
PCWSTR prevLine,
|
|
BOOL drawfolder)
|
|
{
|
|
BOOL bHasSubFolder = HasSubFolder(strPath);
|
|
UINT i = 0;
|
|
|
|
/* This will format the spaces required for correct formatting */
|
|
for (i = 0; i < szArr; ++i)
|
|
{
|
|
PWSTR consoleOut = (PWSTR)malloc(STR_MAX * sizeof(WCHAR));
|
|
UINT j = 0;
|
|
static WCHAR str[STR_MAX];
|
|
|
|
/* As we do not seem to have the _s functions properly set up, use the non-secure version for now */
|
|
//wcscpy_s(consoleOut, STR_MAX, L"");
|
|
//wcscpy_s(str, STR_MAX, L"");
|
|
wcscpy(consoleOut, L"");
|
|
wcscpy(str, L"");
|
|
|
|
for (j = 0; j < width - 1; ++j)
|
|
{
|
|
/* If the previous line has '├' or '│' then the current line will
|
|
add '│' to continue the connecting line */
|
|
if (prevLine[j] == L'\x251C' || prevLine[j] == L'\x2502' ||
|
|
prevLine[j] == L'+' || prevLine[j] == L'|')
|
|
{
|
|
if (!bUseAscii)
|
|
{
|
|
wcscat(consoleOut, L"\x2502");
|
|
}
|
|
else
|
|
{
|
|
wcscat(consoleOut, L"|");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wcscat(consoleOut, L" ");
|
|
}
|
|
}
|
|
|
|
if (szArr - 1 != i)
|
|
{
|
|
if (drawfolder)
|
|
{
|
|
/* Add '├───Folder name' (\xC3\xC4\xC4\xC4 or \x251C\x2500\x2500\x2500) */
|
|
if (bUseAscii)
|
|
swprintf(str, L"+---%s", arrFolder[i].cFileName);
|
|
else
|
|
swprintf(str, L"\x251C\x2500\x2500\x2500%s", arrFolder[i].cFileName);
|
|
}
|
|
else
|
|
{
|
|
if (bHasSubFolder)
|
|
{
|
|
/* Add '│ FileName' (\xB3 or \x2502) */
|
|
// This line is added to connect the below-folder sub-structure
|
|
if (bUseAscii)
|
|
swprintf(str, L"| %s", arrFolder[i].cFileName);
|
|
else
|
|
swprintf(str, L"\x2502 %s", arrFolder[i].cFileName);
|
|
}
|
|
else
|
|
{
|
|
/* Add ' FileName' */
|
|
swprintf(str, L" %s", arrFolder[i].cFileName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (drawfolder)
|
|
{
|
|
/* '└───Folder name' (\xC0\xC4\xC4\xC4 or \x2514\x2500\x2500\x2500) */
|
|
if (bUseAscii)
|
|
swprintf(str, L"\\---%s", arrFolder[i].cFileName);
|
|
else
|
|
swprintf(str, L"\x2514\x2500\x2500\x2500%s", arrFolder[i].cFileName);
|
|
}
|
|
else
|
|
{
|
|
if (bHasSubFolder)
|
|
{
|
|
/* '│ FileName' (\xB3 or \x2502) */
|
|
if (bUseAscii)
|
|
swprintf(str, L"| %s", arrFolder[i].cFileName);
|
|
else
|
|
swprintf(str, L"\x2502 %s", arrFolder[i].cFileName);
|
|
}
|
|
else
|
|
{
|
|
/* ' FileName' */
|
|
swprintf(str, L" %s", arrFolder[i].cFileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
wcscat(consoleOut, str);
|
|
ConPrintf(StdOut, L"%s\n", consoleOut);
|
|
|
|
if (drawfolder)
|
|
{
|
|
PWSTR str = (PWSTR)malloc(STR_MAX * sizeof(WCHAR));
|
|
ZeroMemory(str, STR_MAX * sizeof(WCHAR));
|
|
|
|
wcscat(str, strPath);
|
|
wcscat(str, L"\\");
|
|
wcscat(str, arrFolder[i].cFileName);
|
|
GetDirectoryStructure(str, width + 4, consoleOut);
|
|
|
|
free(str);
|
|
}
|
|
free(consoleOut);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @name: GetDirectoryStructure
|
|
*
|
|
* @param strPath
|
|
* Must specify folder name
|
|
*
|
|
* @param width
|
|
* specifies drawing distance for correct formatting of tree structure being drawn on console screen
|
|
*
|
|
* @param prevLine
|
|
* specifies the previous line written on console, is used for correct formatting
|
|
* @return
|
|
* void
|
|
*/
|
|
static VOID
|
|
GetDirectoryStructure(PWSTR strPath, UINT width, PCWSTR prevLine)
|
|
{
|
|
WIN32_FIND_DATAW FindFileData;
|
|
HANDLE hFind = NULL;
|
|
//DWORD err = 0;
|
|
/* Fill up with names of all sub-folders */
|
|
WIN32_FIND_DATAW *arrFolder = NULL;
|
|
UINT arrFoldersz = 0;
|
|
/* Fill up with names of all sub-folders */
|
|
WIN32_FIND_DATAW *arrFile = NULL;
|
|
UINT arrFilesz = 0;
|
|
|
|
ZeroMemory(&FindFileData, sizeof(FindFileData));
|
|
|
|
{
|
|
static WCHAR tmp[STR_MAX] = L"";
|
|
ZeroMemory(tmp, sizeof(tmp));
|
|
wcscat(tmp, strPath);
|
|
wcscat(tmp, L"\\*.*");
|
|
hFind = FindFirstFileW(tmp, &FindFileData);
|
|
//err = GetLastError();
|
|
}
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
do
|
|
{
|
|
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
if (wcscmp(FindFileData.cFileName, L".") == 0 ||
|
|
wcscmp(FindFileData.cFileName, L"..") == 0)
|
|
continue;
|
|
|
|
++arrFoldersz;
|
|
arrFolder = (WIN32_FIND_DATAW*)realloc(arrFolder, arrFoldersz * sizeof(FindFileData));
|
|
|
|
if (arrFolder == NULL)
|
|
exit(-1);
|
|
|
|
arrFolder[arrFoldersz - 1] = FindFileData;
|
|
|
|
}
|
|
else
|
|
{
|
|
++arrFilesz;
|
|
arrFile = (WIN32_FIND_DATAW*)realloc(arrFile, arrFilesz * sizeof(FindFileData));
|
|
|
|
if(arrFile == NULL)
|
|
exit(-1);
|
|
|
|
arrFile[arrFilesz - 1] = FindFileData;
|
|
}
|
|
}
|
|
while (FindNextFileW(hFind, &FindFileData));
|
|
|
|
FindClose(hFind);
|
|
|
|
if (bShowFiles)
|
|
{
|
|
/* Will free(arrFile) */
|
|
DrawTree(strPath, arrFile, arrFilesz, width, prevLine, FALSE);
|
|
}
|
|
|
|
/* Will free(arrFile) */
|
|
DrawTree(strPath, arrFolder, arrFoldersz, width, prevLine, TRUE);
|
|
|
|
free(arrFolder);
|
|
free(arrFile);
|
|
}
|
|
|
|
/**
|
|
* @name: main
|
|
* standard main functionality as required by C/C++ for application startup
|
|
*
|
|
* @return
|
|
* error /success value
|
|
*/
|
|
int wmain(int argc, WCHAR* argv[])
|
|
{
|
|
DWORD dwSerial = 0;
|
|
WCHAR t = 0;
|
|
PWSTR strPath = NULL;
|
|
DWORD sz = 0;
|
|
//PWSTR context = NULL;
|
|
PWSTR driveLetter = NULL;
|
|
|
|
int i;
|
|
|
|
/* Initialize the Console Standard Streams */
|
|
ConInitStdStreams();
|
|
|
|
/* Parse the command line */
|
|
for (i = 1; i < argc; ++i)
|
|
{
|
|
if (argv[i][0] == L'-' || argv[i][0] == L'/')
|
|
{
|
|
switch (towlower(argv[i][1]))
|
|
{
|
|
case L'?':
|
|
/* Print help and exit after */
|
|
ConResPuts(StdOut, IDS_USAGE);
|
|
return 0;
|
|
|
|
case L'f':
|
|
bShowFiles = TRUE;
|
|
break;
|
|
|
|
case L'a':
|
|
bUseAscii = TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This must be path to some folder */
|
|
|
|
/* Set the current directory for this executable */
|
|
BOOL b = SetCurrentDirectoryW(argv[i]);
|
|
if (b == FALSE)
|
|
{
|
|
ConResPuts(StdOut, IDS_NO_SUBDIRECTORIES);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ConResPuts(StdOut, IDS_FOLDER_PATH);
|
|
|
|
GetVolumeInformationW(NULL, NULL, 0, &dwSerial, NULL, NULL, NULL, 0);
|
|
ConResPrintf(StdOut, IDS_VOL_SERIAL, dwSerial >> 16, dwSerial & 0xffff);
|
|
|
|
/* get the buffer size */
|
|
sz = GetCurrentDirectoryW(1, &t);
|
|
/* must not return before calling delete[] */
|
|
strPath = (PWSTR)malloc(sz * sizeof(WCHAR));
|
|
|
|
/* get the current directory */
|
|
GetCurrentDirectoryW(sz, strPath);
|
|
|
|
/* get the drive letter , must not return before calling delete[] */
|
|
driveLetter = (PWSTR)malloc(sz * sizeof(WCHAR));
|
|
|
|
/* As we do not seem to have the _s functions properly set up, use the non-secure version for now */
|
|
//wcscpy_s(driveLetter,sz,strPath);
|
|
//wcstok_s(driveLetter,L":", &context); //parse for the drive letter
|
|
wcscpy(driveLetter, strPath);
|
|
wcstok(driveLetter, L":");
|
|
|
|
ConPrintf(StdOut, L"%s:.\n", driveLetter);
|
|
|
|
free(driveLetter);
|
|
|
|
/* get the sub-directories within this current folder */
|
|
GetDirectoryStructure(strPath, 1, L" ");
|
|
|
|
free(strPath);
|
|
ConPuts(StdOut, L"\n");
|
|
|
|
return 0;
|
|
}
|