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 */
HasSubFolder(PCWSTR strPath1)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 */
DrawTree(PCWSTR strPath,const WIN32_FIND_DATAW * arrFolder,const size_t szArr,UINT width,PCWSTR prevLine,BOOL drawfolder)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
GetDirectoryStructure(PWSTR strPath,UINT width,PCWSTR prevLine)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 */
wmain(int argc,WCHAR * argv[])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