1 /* 2 * PROJECT: ReactOS 3 * LICENSE: GNU GPLv2 only as published by the Free Software Foundation 4 * PURPOSE: Implements tree.com command similar to Windows 5 * PROGRAMMERS: Asif Bahrainwala (asif_bahrainwala@hotmail.com) 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 11 #include <windef.h> 12 #include <winbase.h> 13 14 #include <conutils.h> 15 16 #include "resource.h" 17 18 #define STR_MAX 2048 19 20 static VOID GetDirectoryStructure(PWSTR strPath, UINT width, PCWSTR prevLine); 21 22 /* If this flag is set to true, files will also be listed within the folder structure */ 23 BOOL bShowFiles = FALSE; 24 25 /* If this flag is true, ASCII characters will be used instead of UNICODE ones */ 26 BOOL bUseAscii = FALSE; 27 28 /** 29 * @name: HasSubFolder 30 * 31 * @param strPath 32 * Must specify folder name 33 * 34 * @return 35 * true if folder has sub-folders, else will return false 36 */ 37 static BOOL HasSubFolder(PCWSTR strPath1) 38 { 39 BOOL ret = FALSE; 40 WIN32_FIND_DATAW FindFileData; 41 HANDLE hFind = NULL; 42 static WCHAR strPath[STR_MAX] = L""; 43 ZeroMemory(strPath, sizeof(strPath)); 44 45 wcscat(strPath, strPath1); 46 wcscat(strPath, L"\\*."); 47 48 hFind = FindFirstFileW(strPath, &FindFileData); 49 do 50 { 51 if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 52 { 53 if (wcscmp(FindFileData.cFileName, L".") == 0 || 54 wcscmp(FindFileData.cFileName, L"..") == 0 ) 55 { 56 continue; 57 } 58 59 ret = TRUE; // Sub-folder found 60 break; 61 } 62 } 63 while (FindNextFileW(hFind, &FindFileData)); 64 65 FindClose(hFind); 66 return ret; 67 } 68 69 /** 70 * @name: DrawTree 71 * 72 * @param strPath 73 * Must specify folder name 74 * 75 * @param arrFolder 76 * must be a list of folder names to be drawn in tree format 77 * 78 * @param width 79 * specifies drawing distance for correct formatting of tree structure being drawn on console screen 80 * used internally for adding spaces 81 * 82 * @param prevLine 83 * used internally for formatting reasons 84 * 85 * @return 86 * void 87 */ 88 static VOID DrawTree(PCWSTR strPath, 89 const WIN32_FIND_DATAW* arrFolder, 90 const size_t szArr, 91 UINT width, 92 PCWSTR prevLine, 93 BOOL drawfolder) 94 { 95 BOOL bHasSubFolder = HasSubFolder(strPath); 96 UINT i = 0; 97 98 /* This will format the spaces required for correct formatting */ 99 for (i = 0; i < szArr; ++i) 100 { 101 PWSTR consoleOut = (PWSTR)malloc(STR_MAX * sizeof(WCHAR)); 102 UINT j = 0; 103 static WCHAR str[STR_MAX]; 104 105 /* As we do not seem to have the _s functions properly set up, use the non-secure version for now */ 106 //wcscpy_s(consoleOut, STR_MAX, L""); 107 //wcscpy_s(str, STR_MAX, L""); 108 wcscpy(consoleOut, L""); 109 wcscpy(str, L""); 110 111 for (j = 0; j < width - 1; ++j) 112 { 113 /* If the previous line has '├' or '│' then the current line will 114 add '│' to continue the connecting line */ 115 if (prevLine[j] == L'\x251C' || prevLine[j] == L'\x2502' || 116 prevLine[j] == L'+' || prevLine[j] == L'|') 117 { 118 if (!bUseAscii) 119 { 120 wcscat(consoleOut, L"\x2502"); 121 } 122 else 123 { 124 wcscat(consoleOut, L"|"); 125 } 126 } 127 else 128 { 129 wcscat(consoleOut, L" "); 130 } 131 } 132 133 if (szArr - 1 != i) 134 { 135 if (drawfolder) 136 { 137 /* Add '├───Folder name' (\xC3\xC4\xC4\xC4 or \x251C\x2500\x2500\x2500) */ 138 if (bUseAscii) 139 swprintf(str, L"+---%s", arrFolder[i].cFileName); 140 else 141 swprintf(str, L"\x251C\x2500\x2500\x2500%s", arrFolder[i].cFileName); 142 } 143 else 144 { 145 if (bHasSubFolder) 146 { 147 /* Add '│ FileName' (\xB3 or \x2502) */ 148 // This line is added to connect the below-folder sub-structure 149 if (bUseAscii) 150 swprintf(str, L"| %s", arrFolder[i].cFileName); 151 else 152 swprintf(str, L"\x2502 %s", arrFolder[i].cFileName); 153 } 154 else 155 { 156 /* Add ' FileName' */ 157 swprintf(str, L" %s", arrFolder[i].cFileName); 158 } 159 } 160 } 161 else 162 { 163 if (drawfolder) 164 { 165 /* '└───Folder name' (\xC0\xC4\xC4\xC4 or \x2514\x2500\x2500\x2500) */ 166 if (bUseAscii) 167 swprintf(str, L"\\---%s", arrFolder[i].cFileName); 168 else 169 swprintf(str, L"\x2514\x2500\x2500\x2500%s", arrFolder[i].cFileName); 170 } 171 else 172 { 173 if (bHasSubFolder) 174 { 175 /* '│ FileName' (\xB3 or \x2502) */ 176 if (bUseAscii) 177 swprintf(str, L"| %s", arrFolder[i].cFileName); 178 else 179 swprintf(str, L"\x2502 %s", arrFolder[i].cFileName); 180 } 181 else 182 { 183 /* ' FileName' */ 184 swprintf(str, L" %s", arrFolder[i].cFileName); 185 } 186 } 187 } 188 189 wcscat(consoleOut, str); 190 ConPrintf(StdOut, L"%s\n", consoleOut); 191 192 if (drawfolder) 193 { 194 PWSTR str = (PWSTR)malloc(STR_MAX * sizeof(WCHAR)); 195 ZeroMemory(str, STR_MAX * sizeof(WCHAR)); 196 197 wcscat(str, strPath); 198 wcscat(str, L"\\"); 199 wcscat(str, arrFolder[i].cFileName); 200 GetDirectoryStructure(str, width + 4, consoleOut); 201 202 free(str); 203 } 204 free(consoleOut); 205 } 206 } 207 208 /** 209 * @name: GetDirectoryStructure 210 * 211 * @param strPath 212 * Must specify folder name 213 * 214 * @param width 215 * specifies drawing distance for correct formatting of tree structure being drawn on console screen 216 * 217 * @param prevLine 218 * specifies the previous line written on console, is used for correct formatting 219 * @return 220 * void 221 */ 222 static VOID 223 GetDirectoryStructure(PWSTR strPath, UINT width, PCWSTR prevLine) 224 { 225 WIN32_FIND_DATAW FindFileData; 226 HANDLE hFind = NULL; 227 //DWORD err = 0; 228 /* Fill up with names of all sub-folders */ 229 WIN32_FIND_DATAW *arrFolder = NULL; 230 UINT arrFoldersz = 0; 231 /* Fill up with names of all sub-folders */ 232 WIN32_FIND_DATAW *arrFile = NULL; 233 UINT arrFilesz = 0; 234 235 ZeroMemory(&FindFileData, sizeof(FindFileData)); 236 237 { 238 static WCHAR tmp[STR_MAX] = L""; 239 ZeroMemory(tmp, sizeof(tmp)); 240 wcscat(tmp, strPath); 241 wcscat(tmp, L"\\*.*"); 242 hFind = FindFirstFileW(tmp, &FindFileData); 243 //err = GetLastError(); 244 } 245 246 if (hFind == INVALID_HANDLE_VALUE) 247 return; 248 249 do 250 { 251 if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 252 { 253 if (wcscmp(FindFileData.cFileName, L".") == 0 || 254 wcscmp(FindFileData.cFileName, L"..") == 0) 255 continue; 256 257 ++arrFoldersz; 258 arrFolder = (WIN32_FIND_DATAW*)realloc(arrFolder, arrFoldersz * sizeof(FindFileData)); 259 260 if (arrFolder == NULL) 261 exit(-1); 262 263 arrFolder[arrFoldersz - 1] = FindFileData; 264 265 } 266 else 267 { 268 ++arrFilesz; 269 arrFile = (WIN32_FIND_DATAW*)realloc(arrFile, arrFilesz * sizeof(FindFileData)); 270 271 if(arrFile == NULL) 272 exit(-1); 273 274 arrFile[arrFilesz - 1] = FindFileData; 275 } 276 } 277 while (FindNextFileW(hFind, &FindFileData)); 278 279 FindClose(hFind); 280 281 if (bShowFiles) 282 { 283 /* Will free(arrFile) */ 284 DrawTree(strPath, arrFile, arrFilesz, width, prevLine, FALSE); 285 } 286 287 /* Will free(arrFile) */ 288 DrawTree(strPath, arrFolder, arrFoldersz, width, prevLine, TRUE); 289 290 free(arrFolder); 291 free(arrFile); 292 } 293 294 /** 295 * @name: main 296 * standard main functionality as required by C/C++ for application startup 297 * 298 * @return 299 * error /success value 300 */ 301 int wmain(int argc, WCHAR* argv[]) 302 { 303 DWORD dwSerial = 0; 304 WCHAR t = 0; 305 PWSTR strPath = NULL; 306 DWORD sz = 0; 307 //PWSTR context = NULL; 308 PWSTR driveLetter = NULL; 309 310 int i; 311 312 /* Initialize the Console Standard Streams */ 313 ConInitStdStreams(); 314 315 /* Parse the command line */ 316 for (i = 1; i < argc; ++i) 317 { 318 if (argv[i][0] == L'-' || argv[i][0] == L'/') 319 { 320 switch (towlower(argv[i][1])) 321 { 322 case L'?': 323 /* Print help and exit after */ 324 ConResPuts(StdOut, IDS_USAGE); 325 return 0; 326 327 case L'f': 328 bShowFiles = TRUE; 329 break; 330 331 case L'a': 332 bUseAscii = TRUE; 333 break; 334 335 default: 336 break; 337 } 338 } 339 else 340 { 341 /* This must be path to some folder */ 342 343 /* Set the current directory for this executable */ 344 BOOL b = SetCurrentDirectoryW(argv[i]); 345 if (b == FALSE) 346 { 347 ConResPuts(StdOut, IDS_NO_SUBDIRECTORIES); 348 return 1; 349 } 350 } 351 } 352 353 ConResPuts(StdOut, IDS_FOLDER_PATH); 354 355 GetVolumeInformationW(NULL, NULL, 0, &dwSerial, NULL, NULL, NULL, 0); 356 ConResPrintf(StdOut, IDS_VOL_SERIAL, dwSerial >> 16, dwSerial & 0xffff); 357 358 /* get the buffer size */ 359 sz = GetCurrentDirectoryW(1, &t); 360 /* must not return before calling delete[] */ 361 strPath = (PWSTR)malloc(sz * sizeof(WCHAR)); 362 363 /* get the current directory */ 364 GetCurrentDirectoryW(sz, strPath); 365 366 /* get the drive letter , must not return before calling delete[] */ 367 driveLetter = (PWSTR)malloc(sz * sizeof(WCHAR)); 368 369 /* As we do not seem to have the _s functions properly set up, use the non-secure version for now */ 370 //wcscpy_s(driveLetter,sz,strPath); 371 //wcstok_s(driveLetter,L":", &context); //parse for the drive letter 372 wcscpy(driveLetter, strPath); 373 wcstok(driveLetter, L":"); 374 375 ConPrintf(StdOut, L"%s:.\n", driveLetter); 376 377 free(driveLetter); 378 379 /* get the sub-directories within this current folder */ 380 GetDirectoryStructure(strPath, 1, L" "); 381 382 free(strPath); 383 ConPuts(StdOut, L"\n"); 384 385 return 0; 386 } 387