824 lines
15 KiB
C++
824 lines
15 KiB
C++
/*
|
|
* This file is a part of Winix
|
|
* and is distributed under the 2-Clause BSD licence.
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2008-2021, Tomasz Sowa
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <ctime>
|
|
#include "dirs.h"
|
|
#include "error.h"
|
|
#include "notify/notify.h"
|
|
|
|
|
|
namespace Winix
|
|
{
|
|
|
|
|
|
|
|
void Dirs::SetDb(Db * pdb)
|
|
{
|
|
db = pdb;
|
|
}
|
|
|
|
|
|
void Dirs::SetCur(Cur * pcur)
|
|
{
|
|
cur = pcur;
|
|
}
|
|
|
|
|
|
void Dirs::SetNotify(Notify * pnotify)
|
|
{
|
|
notify = pnotify;
|
|
}
|
|
|
|
|
|
void Dirs::set_dependency(WinixModelDeprecated * winix_model)
|
|
{
|
|
WinixModelDeprecated::set_dependency(winix_model);
|
|
dir_tab.set_dependency(winix_model);
|
|
}
|
|
|
|
|
|
void Dirs::Clear()
|
|
{
|
|
dir_tab.Clear();
|
|
}
|
|
|
|
|
|
bool Dirs::HasReadExecAccessForRoot(const Item & item)
|
|
{
|
|
// there must be at least one 'x' (for the root)
|
|
return (item.item_content.privileges & 01111) != 0;
|
|
}
|
|
|
|
|
|
void Dirs::CheckRootDir()
|
|
{
|
|
DirContainer::Iterator i = dir_tab.GetRoot();
|
|
|
|
if( i != dir_tab.End() )
|
|
{
|
|
if( !HasReadExecAccessForRoot(*i) )
|
|
{
|
|
i->item_content.privileges = 07555;
|
|
log << log1 << "Dirs: there is no access for a root (admin) to the root dir, setting 07555 for the root directory" << logend;
|
|
i->item_content.set_connector(model_connector);
|
|
i->item_content.date_modification = std::time(nullptr);
|
|
i->item_content.save(false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
log << log1 << "Dirs: there is no a root directory in the database (creating one)" << logend;
|
|
|
|
ItemModelData item_data;
|
|
item_data.prepare_unique_url = false;
|
|
|
|
Item root;
|
|
root.set_connector(model_connector);
|
|
root.type = Item::dir;
|
|
root.parent_id = -1;
|
|
root.item_content.user_id = -1;
|
|
root.item_content.group_id = -1;
|
|
root.item_content.privileges = 07555;
|
|
root.item_content.date_creation = std::time(nullptr);
|
|
root.item_content.date_modification = root.item_content.date_creation;
|
|
|
|
if( root.insert(item_data) )
|
|
{
|
|
dir_tab.PushBack(root);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void Dirs::ReadDirs()
|
|
{
|
|
Clear();
|
|
|
|
//db->GetDirs(dir_tab);
|
|
morm::Finder<Item> finder(model_connector);
|
|
std::list<Item> all_dirs = finder.select().where().eq(L"type", static_cast<int>(Item::Type::dir)).get_list();
|
|
|
|
for(Item & item : all_dirs)
|
|
{
|
|
dir_tab.PushBack(item);
|
|
}
|
|
|
|
CheckRootDir();
|
|
dir_tab.FindSpecialFolders();
|
|
}
|
|
|
|
|
|
bool Dirs::ExtractName(const wchar_t * & s, std::wstring & name)
|
|
{
|
|
name.clear();
|
|
|
|
// skipping first slashes (can be more than one)
|
|
for( ; *s == '/' ; ++s);
|
|
|
|
for( ; *s != 0 && *s != '/' ; ++s)
|
|
name += *s;
|
|
|
|
return !name.empty();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Dirs::IsDir(long id)
|
|
{
|
|
DirContainer::Iterator i = dir_tab.FindId(id);
|
|
|
|
if( i == dir_tab.End() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// !! dac clearowanie childs_tab
|
|
// !! ewentualnie mozna dodac trzeci domyslny parametr bool clear_tab = true
|
|
bool Dirs::GetDirChilds(long parent, std::vector<Item*> & childs_tab)
|
|
{
|
|
if( parent != -1 && !IsDir(parent) )
|
|
return false;
|
|
|
|
DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent);
|
|
|
|
for( ; i != dir_tab.ParentEnd() ; i = dir_tab.NextChild(i) )
|
|
childs_tab.push_back( &(*i->second) );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
DirContainer::ParentIterator Dirs::FindFirstChild(long parent_id)
|
|
{
|
|
DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent_id);
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
DirContainer::ParentIterator Dirs::NextChild(DirContainer::ParentIterator i)
|
|
{
|
|
return dir_tab.NextChild(i);
|
|
}
|
|
|
|
|
|
DirContainer::ParentIterator Dirs::ParentEnd()
|
|
{
|
|
return dir_tab.ParentEnd();
|
|
}
|
|
|
|
|
|
// !! dodatkowo moze metoda AppendPath dodajaca sciezke do biezacego stringa?
|
|
// albo tutaj stringa nie czyscic?
|
|
// O(m * log n) (m- how many parts are in 'id')
|
|
// path with a slash at the end and at the beginning
|
|
bool Dirs::MakePath(long id, std::wstring & path, bool clear_path)
|
|
{
|
|
DirContainer::Iterator i;
|
|
|
|
if( clear_path )
|
|
path.clear();
|
|
|
|
temp_path = '/';
|
|
|
|
while( true )
|
|
{
|
|
i = dir_tab.FindId(id);
|
|
|
|
if( i == dir_tab.End() ||
|
|
i->parent_id == id ) // means a loop (something wrong in the db)
|
|
{
|
|
// we don't change path if there is no such a directory
|
|
return false;
|
|
}
|
|
|
|
if( i->parent_id == -1 )
|
|
{
|
|
path += temp_path;
|
|
return true;
|
|
}
|
|
|
|
id = i->parent_id;
|
|
|
|
temp_path.insert(0, i->url);
|
|
temp_path.insert(temp_path.begin(), '/');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void Dirs::MakePath(const std::vector<Item*> dir_tab, std::wstring & path, bool clear_path)
|
|
{
|
|
if( clear_path )
|
|
path.clear();
|
|
|
|
for(size_t i=0 ; i<dir_tab.size() ; ++i)
|
|
{
|
|
path += dir_tab[i]->url;
|
|
path += '/';
|
|
}
|
|
}
|
|
|
|
|
|
|
|
size_t Dirs::DirLevel(long id)
|
|
{
|
|
DirContainer::Iterator i;
|
|
size_t level = 0;
|
|
|
|
while( true )
|
|
{
|
|
i = dir_tab.FindId(id);
|
|
|
|
if( i == dir_tab.End() ||
|
|
i->parent_id == id ) // means a loop (something wrong in the db)
|
|
{
|
|
return level;
|
|
}
|
|
|
|
if( i->parent_id == -1 )
|
|
return level;
|
|
|
|
id = i->parent_id;
|
|
level += 1;
|
|
}
|
|
}
|
|
|
|
|
|
bool Dirs::IsChild(long parent_id, long child_id)
|
|
{
|
|
if( child_id == parent_id )
|
|
return false;
|
|
|
|
DirContainer::Iterator i;
|
|
|
|
while( child_id != -1 )
|
|
{
|
|
i = dir_tab.FindId(child_id);
|
|
|
|
if( i == dir_tab.End() )
|
|
return false;
|
|
|
|
if( i->parent_id == parent_id )
|
|
return true;
|
|
|
|
child_id = i->parent_id;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Dirs::ChangeParent(long dir_id, long new_parent_id)
|
|
{
|
|
return dir_tab.ChangeParent(dir_id, new_parent_id);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
checking whether dir_id has a parent parent_id (somewhere in the path)
|
|
*/
|
|
bool Dirs::HasParent(long dir_id, long parent_id)
|
|
{
|
|
DirContainer::Iterator i;
|
|
|
|
while( true )
|
|
{
|
|
i = dir_tab.FindId(dir_id);
|
|
|
|
if( i==dir_tab.End() || i->parent_id==-1 )
|
|
return false;
|
|
|
|
if( i->parent_id == parent_id )
|
|
return true;
|
|
|
|
dir_id = i->parent_id;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Dirs::CreateDirTab(long dir_id, std::vector<Item*> & out_dir_tab)
|
|
{
|
|
DirContainer::Iterator i;
|
|
|
|
out_dir_tab.clear();
|
|
|
|
do
|
|
{
|
|
i = dir_tab.FindId(dir_id);
|
|
|
|
if( i == dir_tab.End() )
|
|
return false;
|
|
|
|
if( out_dir_tab.empty() )
|
|
out_dir_tab.insert(out_dir_tab.end(), &(*i)); // !! I am not sure whether begin() can be used on an empty container
|
|
else
|
|
out_dir_tab.insert(out_dir_tab.begin(), &(*i));
|
|
|
|
dir_id = i->parent_id;
|
|
}
|
|
while( dir_id != -1 );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
Item * Dirs::GetRootDir()
|
|
{
|
|
DirContainer::Iterator root = dir_tab.GetRoot();
|
|
|
|
if( root == dir_tab.End() )
|
|
return 0;
|
|
|
|
return &(*root);
|
|
}
|
|
|
|
|
|
Item * Dirs::GetEtcDir()
|
|
{
|
|
DirContainer::Iterator etc = dir_tab.GetEtc();
|
|
|
|
if( etc == dir_tab.End() )
|
|
return 0;
|
|
|
|
return &(*etc);
|
|
}
|
|
|
|
|
|
Item * Dirs::GetVarDir()
|
|
{
|
|
DirContainer::Iterator etc = dir_tab.GetVar();
|
|
|
|
if( etc == dir_tab.End() )
|
|
return 0;
|
|
|
|
return &(*etc);
|
|
}
|
|
|
|
|
|
Item * Dirs::GetDir(const wchar_t * name, long parent)
|
|
{
|
|
DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent);
|
|
|
|
for( ; i!=dir_tab.ParentEnd() ; i = dir_tab.NextChild(i) )
|
|
if( i->second->url == name )
|
|
return &(*i->second);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
Item * Dirs::GetDir(const std::wstring & name, long parent)
|
|
{
|
|
return GetDir(name.c_str(), parent);
|
|
}
|
|
|
|
|
|
|
|
Item * Dirs::GetDir(const wchar_t * path)
|
|
{
|
|
if( *path == 0 )
|
|
return 0;
|
|
|
|
DirContainer::Iterator root = dir_tab.GetRoot();
|
|
|
|
if( root == dir_tab.End() )
|
|
// ops, we do not have a root dir
|
|
return 0;
|
|
|
|
Item * pitem = &(*root);
|
|
const wchar_t * s = path;
|
|
|
|
while( ExtractName(s, get_dir_temp) )
|
|
{
|
|
pitem = GetDir(get_dir_temp, pitem->id);
|
|
|
|
if( !pitem )
|
|
return 0;
|
|
}
|
|
|
|
return pitem;
|
|
}
|
|
|
|
|
|
|
|
Item * Dirs::GetDir(const std::wstring & path)
|
|
{
|
|
return GetDir(path.c_str());
|
|
}
|
|
|
|
|
|
Item * Dirs::GetDir(long id)
|
|
{
|
|
DirContainer::Iterator i = dir_tab.FindId(id);
|
|
|
|
if( i == dir_tab.End() )
|
|
return nullptr;
|
|
|
|
return &(*i);
|
|
}
|
|
|
|
|
|
const Item * Dirs::GetDir(long id) const
|
|
{
|
|
DirContainer::ConstIterator i = dir_tab.FindId(id);
|
|
|
|
if( i == dir_tab.End() )
|
|
return nullptr;
|
|
|
|
return &(*i);
|
|
}
|
|
|
|
|
|
|
|
|
|
Item * Dirs::AddDir(const Item & item)
|
|
{
|
|
return &(*dir_tab.PushBack(item));
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t Dirs::AnalyzeDir(Item * pdir, const std::wstring & path, long & dir_id, std::wstring & dir)
|
|
{
|
|
size_t i = 0;
|
|
size_t old_i;
|
|
|
|
while( true )
|
|
{
|
|
dir_id = pdir->id;
|
|
|
|
// skipping slashes
|
|
for( ; i<path.size() && path[i] == '/' ; ++i );
|
|
|
|
if( i == path.size() )
|
|
return i; // end of the path
|
|
|
|
// creating a name
|
|
old_i = i;
|
|
analyze_temp.clear();
|
|
|
|
for( ; i<path.size() && path[i] != '/' ; ++i)
|
|
analyze_temp += path[i];
|
|
|
|
pdir = GetDir(analyze_temp, pdir->id);
|
|
|
|
if( !pdir )
|
|
return old_i; // analyze_temp is not a directory
|
|
|
|
dir += analyze_temp;
|
|
dir += '/';
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
the path should begin with a slash
|
|
|
|
return values:
|
|
0 - directory exists
|
|
dir_id - id of the directory
|
|
dir - the path to the directory (with a slash at the end)
|
|
file - if not empty means a file name (we don't check if the file really exists)
|
|
1 - there is not a root dir
|
|
2 - the path is empty
|
|
3 - there is not such a directory
|
|
*/
|
|
int Dirs::AnalyzePath(const std::wstring & path, long & dir_id, std::wstring & dir, std::wstring & file)
|
|
{
|
|
Item * pdir = GetRootDir();
|
|
dir = '/';
|
|
file.clear();
|
|
|
|
if( !pdir )
|
|
return 1;
|
|
|
|
if( path.empty() )
|
|
return 2;
|
|
|
|
if( path[0] != '/' )
|
|
return 3;
|
|
|
|
size_t i = AnalyzeDir(pdir, path, dir_id, dir);
|
|
|
|
if( i < path.size() )
|
|
{
|
|
// checking if at least one slash has left
|
|
for(size_t a=i ; a < path.size() ; ++a)
|
|
if( path[a] == '/' )
|
|
return 3; // there is not such a directory
|
|
|
|
// the rest of the path is a file name
|
|
file = path.c_str() + i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// current_dir_tab can be the same container as out_dir_tab
|
|
void Dirs::CopyDirTab(const std::vector<Item*> & in, std::vector<Item*> & out)
|
|
{
|
|
if( &in != &out )
|
|
{
|
|
out.resize(in.size());
|
|
|
|
for(size_t i=0 ; i<in.size() ; ++i)
|
|
out[i] = in[i];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
!! IMPROVE ME
|
|
may dir_tab can be empty when link_to is not relative?
|
|
and now the algorith doesn't check if link_to is not relative (it only uses dir_tab)
|
|
*/
|
|
bool Dirs::AnalyzeDir(std::vector<Item*> & dir_tab, const std::wstring & link_to, size_t & i)
|
|
{
|
|
size_t old_i;
|
|
i = 0;
|
|
|
|
if( dir_tab.empty() )
|
|
return false;
|
|
|
|
while( true )
|
|
{
|
|
// skipping slashes
|
|
for( ; i<link_to.size() && link_to[i] == '/' ; ++i);
|
|
|
|
if( i == link_to.size() )
|
|
return true; // end of the path
|
|
|
|
// creating a name
|
|
old_i = i;
|
|
analyze_temp.clear();
|
|
|
|
for( ; i<link_to.size() && link_to[i] != '/' ; ++i)
|
|
analyze_temp += link_to[i];
|
|
|
|
if( analyze_temp == L".." )
|
|
{
|
|
if( dir_tab.size() <= 1 )
|
|
return false;
|
|
|
|
dir_tab.pop_back();
|
|
}
|
|
else
|
|
if( analyze_temp != L"." )
|
|
{
|
|
Item * pdir = GetDir(analyze_temp, dir_tab.back()->id);
|
|
|
|
if( !pdir )
|
|
{
|
|
i = old_i;
|
|
return true; // analyze_temp is not a directory
|
|
}
|
|
|
|
dir_tab.push_back(pdir);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int Dirs::FollowLink(std::vector<Item*> & dir_tab, const std::wstring & link_to, std::wstring & out_item)
|
|
{
|
|
size_t i;
|
|
|
|
if( !AnalyzeDir(dir_tab, link_to, i) )
|
|
return 2; // incorrect link_to
|
|
|
|
if( i < link_to.size() )
|
|
{
|
|
// checking if at least one slash has left
|
|
for(size_t a=i ; a < link_to.size() ; ++a)
|
|
if( link_to[a] == '/' )
|
|
return 2; // there is not such a directory
|
|
|
|
// the rest of the path is a file name
|
|
out_item = link_to.c_str() + i;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
return codes:
|
|
ok:
|
|
0 - the link_to is a path to a directory (out_item skipped, out_dir_tab will not be empty)
|
|
1 - the link_to is a path to a file (out_item is used, out_dir_tab will not be empty)
|
|
|
|
error:
|
|
2 - incorrect link_to
|
|
3 - there is not a root dir
|
|
4 - current_dir_tab was empty
|
|
|
|
current_dir_tab can be the same container as out_dir_tab
|
|
link_to can be a relative path (without the first slash) and can contain ".." or "."
|
|
*/
|
|
int Dirs::FollowLink(const std::vector<Item*> & current_dir_tab, const std::wstring & link_to,
|
|
std::vector<Item*> & out_dir_tab, std::wstring & out_item)
|
|
{
|
|
temp_link_to = link_to; // link_to can be from the out_item and would be cleared next
|
|
out_item.clear();
|
|
|
|
if( current_dir_tab.empty() )
|
|
return 4;
|
|
|
|
if( temp_link_to.empty() )
|
|
{
|
|
CopyDirTab(current_dir_tab, out_dir_tab);
|
|
return 0;
|
|
}
|
|
|
|
if( temp_link_to[0] == '/' )
|
|
{
|
|
// temp_link_to is an absolute path
|
|
Item * pdir = GetRootDir();
|
|
|
|
if( !pdir )
|
|
return 3;
|
|
|
|
out_dir_tab.clear();
|
|
out_dir_tab.push_back(pdir);
|
|
}
|
|
else
|
|
{
|
|
// temp_link_to is a relative path
|
|
CopyDirTab(current_dir_tab, out_dir_tab);
|
|
}
|
|
|
|
return FollowLink(out_dir_tab, temp_link_to, out_item);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Dirs::SplitPath(const std::wstring & path, std::wstring & dir, std::wstring & file)
|
|
{
|
|
std::wstring::size_type i;
|
|
|
|
dir.clear();
|
|
file.clear();
|
|
|
|
if( path.empty() )
|
|
// !! moze dir ustawic na '/' ?
|
|
return;
|
|
|
|
for( i=path.size()-1 ; i>0 && path[i]!='/' ; --i);
|
|
|
|
if( path[i] != '/' )
|
|
{
|
|
// we do not have any slashes '/'
|
|
file = path;
|
|
return;
|
|
}
|
|
|
|
dir.assign(path, 0, i + 1); // +1 means with a slash at the end
|
|
|
|
if( i < path.size() - 1 )
|
|
file.assign(path, i+1, path.size() - i - 1);
|
|
}
|
|
|
|
|
|
// !! dodac kasowanie z bazy
|
|
bool Dirs::DelDir(long dir_id)
|
|
{
|
|
return dir_tab.DelById(dir_id);
|
|
}
|
|
|
|
|
|
bool Dirs::AddDirectory(Item & item, bool add_to_dir_tab, Item ** pdir, int notify_code)
|
|
{
|
|
if( pdir )
|
|
*pdir = 0;
|
|
|
|
if( item.type != Item::dir )
|
|
return WINIX_ERR_DIR_EXPECTED;
|
|
|
|
//Error status = db->AddItem(item);
|
|
//item.set_connector(model_connector);
|
|
bool status = item.insert();
|
|
|
|
if( status )
|
|
{
|
|
Item * d = AddDir(item);
|
|
|
|
if( add_to_dir_tab && !cur->request->dir_tab.empty() && cur->request->dir_tab.back()->id == item.parent_id )
|
|
cur->request->dir_tab.push_back(d);
|
|
|
|
if( pdir )
|
|
*pdir = d;
|
|
|
|
if( notify_code )
|
|
notify->ItemChanged(notify_code, item);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
Item * Dirs::CreateVarDir()
|
|
{
|
|
Item * var = GetVarDir();
|
|
|
|
if( var )
|
|
return var;
|
|
|
|
Item v;
|
|
Item * root = GetRootDir();
|
|
|
|
if( root )
|
|
{
|
|
v.set_connector(root->get_connector());
|
|
v.parent_id = root->id;
|
|
v.item_content.user_id = root->item_content.user_id;
|
|
v.item_content.group_id = root->item_content.group_id;
|
|
v.item_content.privileges = root->item_content.privileges;
|
|
v.subject = L"var";
|
|
v.url = L"var";
|
|
v.type = Item::dir;
|
|
|
|
AddDirectory(v, false, &var);
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
|
|
// printing first and last slash
|
|
void Dirs::LogDir(const std::vector<Item*> & dir_tab)
|
|
{
|
|
log << '/';
|
|
|
|
// skipping the first (root) directory
|
|
for(size_t i=1 ; i<dir_tab.size() ; ++i)
|
|
log << dir_tab[i]->url << '/';
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Winix
|
|
|