/* Fill a path list with arguments from the command line or a file. Copyright (C) 2003 Walter Landry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "boost/filesystem/operations.hpp" #include "boost/filesystem/exception.hpp" #include "boost/filesystem/fstream.hpp" #include "arx_error.hpp" #include #include "Current_Path.hpp" #include "tree_root.hpp" #include "boost/archive/text_iarchive.hpp" #include "file_attributes.hpp" #include "Path_List.hpp" using namespace std; using namespace boost; namespace fs=boost::filesystem; using fs::path; /* A simple function to normalize a path by changing to its directory and getting the current directory. There is some fiddling to deal with non-existant paths. */ path normalize_path(const path &ph) { path temp_path(system_complete(ph)); path discarded_path; while(!lexists(temp_path) || symbolic_link_exists(temp_path) || !is_directory(temp_path)) { if(temp_path.leaf()!=".") discarded_path=temp_path.leaf() / discarded_path; temp_path=temp_path.branch_path(); } Current_Path current(temp_path); return fs::current_path()/discarded_path; } bool fill_path_list(const path &project_root, const string &paths_file, list &argument_list, Path_List &path_list) { const path root(normalize_path(tree_root(project_root))); bool result(false); while(!argument_list.empty()) { path_list.push_back(fs::relative_path (normalize_path(*(argument_list.begin())), root)); argument_list.pop_front(); result=true; } if(!paths_file.empty()) { result=true; ifstream paths(paths_file.c_str()); if(!paths) throw arx_error("Can't open the paths_file " + paths_file); string path_line; while(getline(paths,path_line)) { path_list.push_back(fs::relative_path (normalize_path(path_line),root)); } } /* We only read the ++edit file if we are not doing partial commits or when rewriting the edit list after a partial commit. */ if(!result && exists(root/"_arx/++edit")) { path_list.no_edit_diff=true; list string_list; { fs::ifstream edit_file(root/"_arx/++edit"); archive::text_iarchive edit_archive(edit_file); while(edit_file) { /* We have to declare filename in the loop, because otherwise there is a problem with the internal representation of strings that causes the internal memory to be reused, thus duplicating some entries and discarding others if they have the same length. */ string filename; edit_archive >> filename; if(edit_file) string_list.push_back(filename); } string_list.sort(); string_list.unique(); } list > moves; fs::ifstream changes(root/"_arx/++changes"); while(changes) { string action; changes >> action; if(changes) { /* Get the tab character following the action */ changes.get(); if(action=="set" || action=="unset") { string filename, key, value; archive::text_iarchive changes_archive(changes,archive::no_header); changes_archive >> filename >> key >> value; if(find(string_list.begin(),string_list.end(),filename) ==string_list.end()) string_list.push_back(filename); } else { sha256 checksum; file_attributes f; f.input(changes,checksum); if(action=="add" || action=="delete") { bool found(false); if(action=="add") { moves.push_back(make_pair(string(), f.file_path.string())); } else { /* If deleting, we need to use the original path in case of moves. */ for(list >::iterator i=moves.begin(); i!=moves.end(); ++i) { if(i->second==f.file_path.string()) { list::iterator j=find(string_list.begin(), string_list.end(), i->second); /* If we are deleting a file that we just added, then just remove the entry completely. */ if(i->first.empty()) { string_list.erase(j); } else { *j=i->first; } found=true; break; } } } /* If we're adding or we couldn't find a match for the delete among the moves, then add the path to the list. */ if(!found && find(string_list.begin(),string_list.end(), f.file_path.string())==string_list.end()) string_list.push_back(f.file_path.string()); } else if(action=="move") { string destination; archive::text_iarchive changes_archive(changes,archive::no_header); changes_archive >> destination; list::iterator i=find(string_list.begin(),string_list.end(), f.file_path.string()); if(i!=string_list.end()) { *i=destination; /* We need to do more searching in case a path has been moved more than once. This just removes duplicates. */ list::iterator j=find(i,string_list.end(),f.file_path.string()); while(j!=string_list.end()) { string_list.erase(j); j=find(i,string_list.end(),f.file_path.string()); } } else if(find(string_list.begin(),string_list.end(), destination)==string_list.end()) string_list.push_back(destination); /* Add or update an entry in the move list. */ bool found(false); for(list >::iterator j=moves.begin(); j!=moves.end(); ++j) { if(j->second==f.file_path.string()) { j->second=destination; found=true; break; } } if(!found) moves.push_back(make_pair(f.file_path.string(), destination)); } else { throw arx_error("INTERNAL ERROR: Your _arx/++changes file seems to be corrupted. I don't\nknow how to interpret this action\n\t" + action); } } } } for(list::iterator i=string_list.begin(); i!=string_list.end(); ++i) path_list.push_back(*i); } return result; }