1 /*
2  * Copyright 2003, 2004, 2005 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // entries.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 //#include "entries.h"
32 
33 
34  // allocate and initialise a directory entry
Entry(ENTRY_TYPE etype)35 Entry::Entry(ENTRY_TYPE etype)
36  :	_etype(etype)
37 {
38 	_up = NULL;
39 	_next = NULL;
40 	_down = NULL;
41 	_expanded = false;
42 	_scanned = false;
43 	_bhfi_valid = false;
44 	_level = 0;
45 	_icon_id = ICID_UNKNOWN;
46 	_display_name = _data.cFileName;
47 	_type_name = NULL;
48 	_content = NULL;
49 }
50 
Entry(Entry * parent,ENTRY_TYPE etype)51 Entry::Entry(Entry* parent, ENTRY_TYPE etype)
52  :	_up(parent),
53 	_etype(etype)
54 {
55 	_next = NULL;
56 	_down = NULL;
57 	_expanded = false;
58 	_scanned = false;
59 	_bhfi_valid = false;
60 	_level = 0;
61 	_icon_id = ICID_UNKNOWN;
62 	_shell_attribs = 0;
63 	_display_name = _data.cFileName;
64 	_type_name = NULL;
65 	_content = NULL;
66 }
67 
Entry(const Entry & other)68 Entry::Entry(const Entry& other)
69 {
70 	_next = NULL;
71 	_down = NULL;
72 	_up = NULL;
73 
74 	assert(!other._next);
75 	assert(!other._down);
76 	assert(!other._up);
77 
78 	_expanded = other._expanded;
79 	_scanned = other._scanned;
80 	_level = other._level;
81 
82 	_data = other._data;
83 
84 	_shell_attribs = other._shell_attribs;
85 	_display_name = other._display_name==other._data.cFileName? _data.cFileName: _tcsdup(other._display_name);
86 	_type_name = other._type_name? _tcsdup(other._type_name): NULL;
87 	_content = other._content? _tcsdup(other._content): NULL;
88 
89 	_etype = other._etype;
90 	_icon_id = other._icon_id;
91 
92 	_bhfi = other._bhfi;
93 	_bhfi_valid = other._bhfi_valid;
94 }
95 
96  // free a directory entry
~Entry()97 Entry::~Entry()
98 {
99 	free_subentries();
100 
101 	if (_icon_id > ICID_NONE)
102 		g_Globals._icon_cache.free_icon(_icon_id);
103 
104 	if (_display_name != _data.cFileName)
105 		free(_display_name);
106 
107 	if (_type_name)
108 		free(_type_name);
109 
110 	if (_content)
111 		free(_content);
112 
113 	if (_down)
114 		delete _down;
115 }
116 
117 
118  // read directory tree and expand to the given location
read_tree(const void * path,SORT_ORDER sortOrder,int scan_flags)119 Entry* Entry::read_tree(const void* path, SORT_ORDER sortOrder, int scan_flags)
120 {
121 	CONTEXT("Entry::read_tree()");
122 
123 	WaitCursor wait;
124 
125 	Entry* entry = this;
126 
127 	for(const void*p=path; p && entry; ) {
128 		entry->smart_scan(sortOrder, scan_flags);
129 
130 		if (entry->_down)
131 			entry->_expanded = true;
132 
133 		Entry* found = entry->find_entry(p);
134 		p = entry->get_next_path_component(p);
135 
136 		entry = found;
137 	}
138 
139 	return entry;
140 }
141 
142 
read_directory_base(SORT_ORDER sortOrder,int scan_flags)143 void Entry::read_directory_base(SORT_ORDER sortOrder, int scan_flags)
144 {
145 	CONTEXT("Entry::read_directory_base()");
146 
147 	 // call into subclass
148 	read_directory(scan_flags);
149 
150 #ifndef ROSSHELL
151 	if (g_Globals._prescan_nodes) {	///@todo _prescan_nodes should not be used for reading the start menu.
152 		for(Entry*entry=_down; entry; entry=entry->_next)
153 			if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
154 				entry->read_directory(scan_flags);
155 				entry->sort_directory(sortOrder);
156 			}
157 	}
158 #endif
159 
160 	sort_directory(sortOrder);
161 }
162 
163 
Root()164 Root::Root()
165 {
166 	memset(this, 0, sizeof(Root));
167 }
168 
~Root()169 Root::~Root()
170 {
171 	if (_entry) {
172 		_entry->free_subentries();
173 		delete _entry;
174 	}
175 }
176 
177 
178  // sort order for different directory/file types
179 enum TYPE_ORDER {
180 	TO_DIR,
181 	TO_DOT,
182 	TO_DOTDOT,
183 	TO_OTHER_DIR,
184 	TO_VIRTUAL_FOLDER,
185 	TO_FILE
186 };
187 
188  // distinguish between ".", ".." and any other directory names
TypeOrderFromDirname(LPCTSTR name)189 static TYPE_ORDER TypeOrderFromDirname(LPCTSTR name)
190 {
191 	if (name[0] == '.') {
192 		if (name[1] == '\0')
193 			return TO_DOT;		// "."
194 
195 		if (name[1]=='.' && name[2]=='\0')
196 			return TO_DOTDOT;	// ".."
197 	}
198 
199 	return TO_OTHER_DIR;		// any other directory
200 }
201 
202  // directories first...
compareType(const Entry * entry1,const Entry * entry2)203 static int compareType(const Entry* entry1, const Entry* entry2)
204 {
205 	const WIN32_FIND_DATA* fd1 = &entry1->_data;
206 	const WIN32_FIND_DATA* fd2 = &entry2->_data;
207 
208 	TYPE_ORDER order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
209 	TYPE_ORDER order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
210 
211 	 // Handle "." and ".." as special case and move them at the very first beginning.
212 	if (order1==TO_DIR && order2==TO_DIR) {
213 		order1 = TypeOrderFromDirname(fd1->cFileName);
214 		order2 = TypeOrderFromDirname(fd2->cFileName);
215 
216 		 // Move virtual folders after physical folders
217 		if (!(entry1->_shell_attribs & SFGAO_FILESYSTEM))
218 			order1 = TO_VIRTUAL_FOLDER;
219 
220 		if (!(entry2->_shell_attribs & SFGAO_FILESYSTEM))
221 			order2 = TO_VIRTUAL_FOLDER;
222 	}
223 
224 	return order2==order1? 0: order1<order2? -1: 1;
225 }
226 
227 
compareNothing(const void * arg1,const void * arg2)228 static int compareNothing(const void* arg1, const void* arg2)
229 {
230 	return -1;
231 }
232 
compareName(const void * arg1,const void * arg2)233 static int compareName(const void* arg1, const void* arg2)
234 {
235 	const Entry* entry1 = *(const Entry**)arg1;
236 	const Entry* entry2 = *(const Entry**)arg2;
237 
238 	int cmp = compareType(entry1, entry2);
239 	if (cmp)
240 		return cmp;
241 
242 	return lstrcmpi(entry1->_data.cFileName, entry2->_data.cFileName);
243 }
244 
compareExt(const void * arg1,const void * arg2)245 static int compareExt(const void* arg1, const void* arg2)
246 {
247 	const Entry* entry1 = *(const Entry**)arg1;
248 	const Entry* entry2 = *(const Entry**)arg2;
249 	const TCHAR *name1, *name2, *ext1, *ext2;
250 
251 	int cmp = compareType(entry1, entry2);
252 	if (cmp)
253 		return cmp;
254 
255 	name1 = entry1->_data.cFileName;
256 	name2 = entry2->_data.cFileName;
257 
258 	ext1 = _tcsrchr(name1, TEXT('.'));
259 	ext2 = _tcsrchr(name2, TEXT('.'));
260 
261 	if (ext1)
262 		++ext1;
263 	else
264 		ext1 = TEXT("");
265 
266 	if (ext2)
267 		++ext2;
268 	else
269 		ext2 = TEXT("");
270 
271 	cmp = lstrcmpi(ext1, ext2);
272 	if (cmp)
273 		return cmp;
274 
275 	return lstrcmpi(name1, name2);
276 }
277 
compareSize(const void * arg1,const void * arg2)278 static int compareSize(const void* arg1, const void* arg2)
279 {
280 	const Entry* entry1 = *(const Entry**)arg1;
281 	const Entry* entry2 = *(const Entry**)arg2;
282 
283 	int cmp = compareType(entry1, entry2);
284 	if (cmp)
285 		return cmp;
286 
287 	cmp = entry2->_data.nFileSizeHigh - entry1->_data.nFileSizeHigh;
288 
289 	if (cmp < 0)
290 		return -1;
291 	else if (cmp > 0)
292 		return 1;
293 
294 	cmp = entry2->_data.nFileSizeLow - entry1->_data.nFileSizeLow;
295 
296 	return cmp<0? -1: cmp>0? 1: 0;
297 }
298 
compareDate(const void * arg1,const void * arg2)299 static int compareDate(const void* arg1, const void* arg2)
300 {
301 	const Entry* entry1 = *(const Entry**)arg1;
302 	const Entry* entry2 = *(const Entry**)arg2;
303 
304 	int cmp = compareType(entry1, entry2);
305 	if (cmp)
306 		return cmp;
307 
308 	return CompareFileTime(&entry2->_data.ftLastWriteTime, &entry1->_data.ftLastWriteTime);
309 }
310 
311 
312 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
313 	compareNothing,	// SORT_NONE
314 	compareName,	// SORT_NAME
315 	compareExt, 	// SORT_EXT
316 	compareSize,	// SORT_SIZE
317 	compareDate 	// SORT_DATE
318 };
319 
320 
sort_directory(SORT_ORDER sortOrder)321 void Entry::sort_directory(SORT_ORDER sortOrder)
322 {
323 	if (sortOrder != SORT_NONE) {
324 		Entry* entry = _down;
325 		Entry** array, **p;
326 		int len;
327 
328 		len = 0;
329 		for(entry=_down; entry; entry=entry->_next)
330 			++len;
331 
332 		if (len) {
333 			array = (Entry**) alloca(len*sizeof(Entry*));
334 
335 			p = array;
336 			for(entry=_down; entry; entry=entry->_next)
337 				*p++ = entry;
338 
339 			 // call qsort with the appropriate compare function
340 			qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
341 
342 			_down = array[0];
343 
344 			for(p=array; --len; p++)
345 				(*p)->_next = p[1];
346 
347 			(*p)->_next = 0;
348 		}
349 	}
350 }
351 
352 
smart_scan(SORT_ORDER sortOrder,int scan_flags)353 void Entry::smart_scan(SORT_ORDER sortOrder, int scan_flags)
354 {
355 	CONTEXT("Entry::smart_scan()");
356 
357 	if (!_scanned) {
358 		free_subentries();
359 		read_directory_base(sortOrder, scan_flags);	///@todo We could use IShellFolder2::GetDefaultColumn to determine sort order.
360 	}
361 }
362 
363 
364 
extract_icon(ICONCACHE_FLAGS flags)365 int Entry::extract_icon(ICONCACHE_FLAGS flags)
366 {
367 	TCHAR path[MAX_PATH];
368 
369 	ICON_ID icon_id = ICID_NONE;
370 
371 	if (_etype!=ET_SHELL && get_path(path, COUNTOF(path)))	// not for ET_SHELL to display the correct desktop icon
372 		if (!(flags & ICF_MIDDLE))	// not for ICF_MIDDLE to extract 24x24 icons because SHGetFileInfo() doesn't support this icon size
373 			icon_id = g_Globals._icon_cache.extract(path, flags);
374 
375 	if (icon_id == ICID_NONE) {
376 		if (!(flags & ICF_OVERLAYS)) {
377 			IExtractIcon* pExtract;
378 			if (SUCCEEDED(GetUIObjectOf(0, IID_IExtractIcon, (LPVOID*)&pExtract))) {
379 				unsigned gil_flags = 0;
380 				int idx;
381 
382 				if (flags & ICF_OPEN)
383 					gil_flags |= GIL_OPENICON;
384 
385 				if (SUCCEEDED(pExtract->GetIconLocation(GIL_FORSHELL, path, COUNTOF(path), &idx, &gil_flags))) {
386 					if (gil_flags & GIL_NOTFILENAME)
387 						icon_id = g_Globals._icon_cache.extract(pExtract, path, idx, flags);
388 					else {
389 						if (idx == -1)
390 							idx = 0;	// special case for some control panel applications ("System")
391 
392 						icon_id = g_Globals._icon_cache.extract(path, idx, flags);
393 					}
394 
395 				/* using create_absolute_pidl() [see below] results in more correct icons for some control panel applets (NVidia display driver).
396 					if (icon_id == ICID_NONE) {
397 						SHFILEINFO sfi;
398 
399 						if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
400 							icon_id = g_Globals._icon_cache.add(sfi.hIcon)._id;
401 					} */
402 				/*
403 					if (icon_id == ICID_NONE) {
404 						LPBYTE b = (LPBYTE) alloca(0x10000);
405 						SHFILEINFO sfi;
406 
407 						FILE* file = fopen(path, "rb");
408 						if (file) {
409 							int l = fread(b, 1, 0x10000, file);
410 							fclose(file);
411 
412 							if (l)
413 								icon_id = g_Globals._icon_cache.add(CreateIconFromResourceEx(b, l, TRUE, 0x00030000, 16, 16, LR_DEFAULTCOLOR));
414 						}
415 					} */
416 				}
417 			}
418 		}
419 
420 		if (icon_id == ICID_NONE) {
421 			const ShellPath& pidl_abs = create_absolute_pidl();
422 			LPCITEMIDLIST pidl = pidl_abs;
423 
424 			icon_id = g_Globals._icon_cache.extract(pidl, flags);
425 		}
426 	}
427 
428 	return icon_id;
429 }
430 
safe_extract_icon(ICONCACHE_FLAGS flags)431 int Entry::safe_extract_icon(ICONCACHE_FLAGS flags)
432 {
433 	try {
434 		return extract_icon(flags);
435 	} catch(COMException&) {
436 		// ignore unexpected exceptions while extracting icons
437 	}
438 
439 	return ICID_NONE;
440 }
441 
442 
launch_entry(HWND hwnd,UINT nCmdShow)443 BOOL Entry::launch_entry(HWND hwnd, UINT nCmdShow)
444 {
445 	TCHAR cmd[MAX_PATH];
446 
447 	if (!get_path(cmd, COUNTOF(cmd)))
448 		return FALSE;
449 
450 	 // add path to the recent file list
451 	SHAddToRecentDocs(SHARD_PATH, cmd);
452 
453 	  // start program, open document...
454 	return launch_file(hwnd, cmd, nCmdShow);
455 }
456 
457 
458  // local replacement implementation for SHBindToParent()
459  // (derived from http://www.geocities.com/SiliconValley/2060/articles/shell-helpers.html)
my_SHBindToParent(LPCITEMIDLIST pidl,REFIID riid,VOID ** ppv,LPCITEMIDLIST * ppidlLast)460 static HRESULT my_SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, VOID** ppv, LPCITEMIDLIST* ppidlLast)
461 {
462 	HRESULT hr;
463 
464 	if (!ppv)
465 		return E_POINTER;
466 
467 	// There must be at least one item ID.
468 	if (!pidl || !pidl->mkid.cb)
469 		return E_INVALIDARG;
470 
471 	 // Get the desktop folder as root.
472 	ShellFolder desktop;
473 /*	IShellFolderPtr desktop;
474 	hr = SHGetDesktopFolder(&desktop);
475 	if (FAILED(hr))
476 		return hr; */
477 
478 	// Walk to the penultimate item ID.
479 	LPCITEMIDLIST marker = pidl;
480 	for (;;)
481 	{
482 		LPCITEMIDLIST next = reinterpret_cast<LPCITEMIDLIST>(
483 			marker->mkid.abID - sizeof(marker->mkid.cb) + marker->mkid.cb);
484 		if (!next->mkid.cb)
485 			break;
486 		marker = next;
487 	}
488 
489 	if (marker == pidl)
490 	{
491 		// There was only a single item ID, so bind to the root folder.
492 		hr = desktop->QueryInterface(riid, ppv);
493 	}
494 	else
495 	{
496 		// Copy the ID list, truncating the last item.
497 		int length = marker->mkid.abID - pidl->mkid.abID;
498 		if (LPITEMIDLIST parent_id = reinterpret_cast<LPITEMIDLIST>(
499 			malloc(length + sizeof(pidl->mkid.cb))))
500 		{
501 			LPBYTE raw_data = reinterpret_cast<LPBYTE>(parent_id);
502 			memcpy(raw_data, pidl, length);
503 			memset(raw_data + length, 0, sizeof(pidl->mkid.cb));
504 			hr = desktop->BindToObject(parent_id, 0, riid, ppv);
505 			free(parent_id);
506 		}
507 		else
508 			return E_OUTOFMEMORY;
509 	}
510 
511 	// Return a pointer to the last item ID.
512 	if (ppidlLast)
513 		*ppidlLast = marker;
514 
515 	return hr;
516 }
517 #define USE_MY_SHBINDTOPARENT
518 
do_context_menu(HWND hwnd,const POINT & pos,CtxMenuInterfaces & cm_ifs)519 HRESULT Entry::do_context_menu(HWND hwnd, const POINT& pos, CtxMenuInterfaces& cm_ifs)
520 {
521 	ShellPath shell_path = create_absolute_pidl();
522 	LPCITEMIDLIST pidl_abs = shell_path;
523 
524 	if (!pidl_abs)
525 		return S_FALSE;	// no action for registry entries, etc.
526 
527 #ifdef USE_MY_SHBINDTOPARENT
528 	IShellFolder* parentFolder;
529 	LPCITEMIDLIST pidlLast;
530 
531 	 // get and use the parent folder to display correct context menu in all cases -> correct "Properties" dialog for directories, ...
532 	HRESULT hr = my_SHBindToParent(pidl_abs, IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast);
533 
534 	if (SUCCEEDED(hr)) {
535 		hr = ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pos.x, pos.y, cm_ifs);
536 
537 		parentFolder->Release();
538 	}
539 
540 	return hr;
541 #else
542 	static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
543 
544 	if (SHBindToParent) {
545 		IShellFolder* parentFolder;
546 		LPCITEMIDLIST pidlLast;
547 
548 		 // get and use the parent folder to display correct context menu in all cases -> correct "Properties" dialog for directories, ...
549 		HRESULT hr = (*SHBindToParent)(pidl_abs, IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast);
550 
551 		if (SUCCEEDED(hr)) {
552 			hr = ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pos.x, pos.y, cm_ifs);
553 
554 			parentFolder->Release();
555 		}
556 
557 		return hr;
558 	} else {
559 		/**@todo use parent folder instead of desktop folder
560 		Entry* dir = _up;
561 
562 		ShellPath parent_path;
563 
564 		if (dir)
565 			parent_path = dir->create_absolute_pidl();
566 		else
567 			parent_path = DesktopFolderPath();
568 
569 		ShellPath shell_path = create_relative_pidl(parent_path);
570 		LPCITEMIDLIST pidl = shell_path;
571 
572 		ShellFolder parent_folder = parent_path;
573 		return ShellFolderContextMenu(parent_folder, hwnd, 1, &pidl, pos.x, pos.y);
574 		*/
575 		return ShellFolderContextMenu(GetDesktopFolder(), hwnd, 1, &pidl_abs, pos.x, pos.y, cm_ifs);
576 	}
577 #endif
578 }
579 
580 
GetUIObjectOf(HWND hWnd,REFIID riid,LPVOID * ppvOut)581 HRESULT Entry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
582 {
583 	TCHAR path[MAX_PATH];
584 /*
585 	if (!get_path(path, COUNTOF(path)))
586 		return E_FAIL;
587 
588 	ShellPath shell_path(path);
589 
590 	IShellFolder* pFolder;
591 	LPCITEMIDLIST pidl_last = NULL;
592 
593 	static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
594 
595 	if (!SHBindToParent)
596 		return E_NOTIMPL;
597 
598 	HRESULT hr = (*SHBindToParent)(shell_path, IID_IShellFolder, (LPVOID*)&pFolder, &pidl_last);
599 	if (FAILED(hr))
600 		return hr;
601 
602 	ShellFolder shell_folder(pFolder);
603 
604 	shell_folder->Release();
605 
606 	return shell_folder->GetUIObjectOf(hWnd, 1, &pidl_last, riid, NULL, ppvOut);
607 */
608 	if (!_up)
609 		return E_INVALIDARG;
610 
611 	if (!_up->get_path(path, COUNTOF(path)))
612 		return E_FAIL;
613 
614 	ShellPath shell_path(path);
615 	ShellFolder shell_folder(shell_path);
616 
617 #ifdef UNICODE
618 	LPWSTR wname = _data.cFileName;
619 #else
620 	WCHAR wname[MAX_PATH];
621 	MultiByteToWideChar(CP_ACP, 0, _data.cFileName, -1, wname, COUNTOF(wname));
622 #endif
623 
624 	LPITEMIDLIST pidl_last = NULL;
625 	HRESULT hr = shell_folder->ParseDisplayName(hWnd, NULL, wname, NULL, &pidl_last, NULL);
626 
627 	if (FAILED(hr))
628 		return hr;
629 
630 	hr = shell_folder->GetUIObjectOf(hWnd, 1, (LPCITEMIDLIST*)&pidl_last, riid, NULL, ppvOut);
631 
632 	ShellMalloc()->Free((void*)pidl_last);
633 
634 	return hr;
635 }
636 
637 
638  // get full path of specified directory entry
get_path_base(PTSTR path,size_t path_count,ENTRY_TYPE etype) const639 bool Entry::get_path_base ( PTSTR path, size_t path_count, ENTRY_TYPE etype ) const
640 {
641 	int level = 0;
642 	size_t len = 0;
643 	size_t l = 0;
644 	LPCTSTR name = NULL;
645 	TCHAR buffer[MAX_PATH];
646 
647 	if (!path || path_count==0)
648 		return false;
649 
650 	const Entry* entry;
651 	if ( path_count > 1 )
652 	{
653 		for(entry=this; entry; level++) {
654 			l = 0;
655 
656 			if (entry->_etype == etype) {
657 				name = entry->_data.cFileName;
658 
659 				for(LPCTSTR s=name; *s && *s!=TEXT('/') && *s!=TEXT('\\'); s++)
660 					++l;
661 
662 				if (!entry->_up)
663 					break;
664 			} else {
665 				if (entry->get_path(buffer, COUNTOF(buffer))) {
666 					l = _tcslen(buffer);
667 					name = buffer;
668 
669 					/* special handling of drive names */
670 					if (l>0 && buffer[l-1]=='\\' && path[0]=='\\')
671 						--l;
672 
673 					if ( len+l >= path_count )
674 					{
675 						if ( l + 1 > path_count )
676 							len = 0;
677 						else
678 							len = path_count - l - 1;
679 					}
680 					memmove(path+l, path, len*sizeof(TCHAR));
681 					if ( l+1 >= path_count )
682 						l = path_count - 1;
683 					memcpy(path, name, l*sizeof(TCHAR));
684 					len += l;
685 				}
686 
687 				entry = NULL;
688 				break;
689 			}
690 
691 			if (l > 0) {
692 				if ( len+l+1 >= path_count )
693 				{
694 					/* compare to 2 here because of terminator plus the '\\' we prepend */
695 					if ( l + 2 > path_count )
696 						len = 0;
697 					else
698 						len = path_count - l - 2;
699 				}
700 				memmove(path+l+1, path, len*sizeof(TCHAR));
701 				/* compare to 2 here because of terminator plus the '\\' we prepend */
702 				if ( l+2 >= path_count )
703 					l = path_count - 2;
704 				memcpy(path+1, name, l*sizeof(TCHAR));
705 				len += l+1;
706 
707 #ifndef _NO_WIN_FS
708 				if (etype == ET_WINDOWS && entry->_up && !(entry->_up->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))	// a NTFS stream?
709 					path[0] = TEXT(':');
710 				else
711 #endif
712 					path[0] = TEXT('\\');
713 			}
714 
715 			entry = entry->_up;
716 		}
717 
718 		if (entry) {
719 			if ( len+l >= path_count )
720 			{
721 				if ( l + 1 > path_count )
722 					len = 0;
723 				else
724 					len = path_count - l - 1;
725 			}
726 			memmove(path+l, path, len*sizeof(TCHAR));
727 			if ( l+1 >= path_count )
728 				l = path_count - 1;
729 			memcpy(path, name, l*sizeof(TCHAR));
730 			len += l;
731 		}
732 
733 		if ( !level && (len+1 < path_count) )
734 			path[len++] = TEXT('\\');
735 	}
736 
737 	path[len] = TEXT('\0');
738 
739 	return true;
740 }
741 
742  // recursively free all child entries
free_subentries()743 void Entry::free_subentries()
744 {
745 	Entry *entry, *next=_down;
746 
747 	if (next) {
748 		_down = 0;
749 
750 		do {
751 			entry = next;
752 			next = entry->_next;
753 
754 			entry->free_subentries();
755 			delete entry;
756 		} while(next);
757 	}
758 }
759 
760 
read_tree(LPCTSTR path,int scan_flags)761 Entry* Root::read_tree(LPCTSTR path, int scan_flags)
762 {
763 	Entry* entry;
764 
765 	if (path && *path)
766 		entry = _entry->read_tree(path, _sort_order);
767 	else {
768 		entry = _entry->read_tree(NULL, _sort_order);
769 
770 		_entry->smart_scan();
771 
772 		if (_entry->_down)
773 			_entry->_expanded = true;
774 	}
775 
776 	return entry;
777 }
778 
779 
read_tree(LPCITEMIDLIST pidl,int scan_flags)780 Entry* Root::read_tree(LPCITEMIDLIST pidl, int scan_flags)
781 {
782 	return _entry->read_tree(pidl, _sort_order);
783 }
784