1 /*********************************************************************/ 2 // dar - disk archive - a backup/restoration program 3 // Copyright (C) 2002-2052 Denis Corbin 4 // 5 // This program is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 2 8 // of the License, or (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 // 19 // to contact the author : http://dar.linux.free.fr/email.html 20 /*********************************************************************/ 21 22 #include "../my_config.h" 23 24 extern "C" 25 { 26 #if HAVE_ERRNO_H 27 #include <errno.h> 28 #endif 29 30 #if HAVE_UNISTD_H 31 #include <unistd.h> 32 #endif 33 34 #if HAVE_FCNTL_H 35 #include <fcntl.h> 36 #endif 37 38 #if HAVE_STRING_H 39 #include <string.h> 40 #endif 41 42 } // end extern "C" 43 44 #include "mask_list.hpp" 45 #include "erreurs.hpp" 46 #include "tools.hpp" 47 #include "cygwin_adapt.hpp" 48 #include "fichier_local.hpp" 49 #include "nls_swap.hpp" 50 51 using namespace std; 52 53 namespace libdar 54 { 55 56 static bool modified_lexicalorder_a_lessthan_b(const std::string & a, const std::string & b); 57 mask_list(const string & filename_list_st,bool case_sensit,const path & prefix_t,bool include)58 mask_list::mask_list(const string & filename_list_st, bool case_sensit, const path & prefix_t, bool include) 59 { 60 NLS_SWAP_IN; 61 try 62 { 63 try 64 { 65 case_s = case_sensit; //< object's field 66 including = include; //< object's field 67 fichier_local source = filename_list_st; //< where we read data from 68 char *buffer = nullptr; //< hold the just read data 69 static const U_I buf_size = 20480; //< size of buffer: we read at most this number of bytes at a time 70 list <string> tmp; //< list of all raw lines read, without any prefix 71 U_I lu = 0, curs; //< cursor used as cisors to split data in line 72 char *beg = nullptr; //< points to the beginning of the next line inside buffer, when more than one line can be found in buffer 73 string str_beg; //< holds the std::string copy of beg, eventually uppercased 74 string current_entry = ""; //< holds the current line converted to string between each read() 75 path prefix = prefix_t; //< the prefix to add to relative paths 76 77 78 79 ///////////// 80 // changing the prefix to uppercase if case sensitivity is disabled 81 82 if(!case_sensit) 83 { 84 string ptp = prefix_t.display(); 85 string upper; 86 tools_to_upper(ptp, upper); 87 prefix = path(upper); 88 } 89 90 91 92 ///////////// 93 // building buffer that will be used to split read data line by line 94 95 meta_new(buffer, buf_size+1); // one char more to be able to add a '\0' if necessary 96 if(buffer == nullptr) 97 throw Erange("mask_list::mask_list", tools_printf(gettext("Cannot allocate memory for buffer while reading %S"), &filename_list_st)); 98 99 100 101 ///////////// 102 // filling 'tmp' with with each line read 103 104 try 105 { 106 do 107 { 108 lu = source.read(buffer, buf_size); 109 110 if(lu > 0) 111 { 112 curs = 0; 113 beg = buffer; 114 115 do 116 { 117 while(curs < lu && buffer[curs] != '\n' && buffer[curs] != '\0') 118 curs++; 119 120 if(curs < lu) 121 { 122 if(buffer[curs] == '\0') 123 throw Erange("mask_list::mask_list", tools_printf(gettext("Found '\0' character in %S, not a plain file, aborting"), &filename_list_st)); 124 if(buffer[curs] == '\n') 125 { 126 buffer[curs] = '\0'; 127 if(!case_s) 128 tools_to_upper(beg, str_beg); 129 else 130 str_beg = string(beg); 131 current_entry += str_beg; 132 if(current_entry != "") 133 tmp.push_back(current_entry); 134 current_entry = ""; 135 curs++; 136 beg = buffer + curs; 137 } 138 else 139 throw SRC_BUG; 140 } 141 else // reached end of buffer 142 { 143 if(lu == buf_size && beg == buffer) 144 { 145 buffer[buf_size - 1] = '\0'; 146 throw Erange("mask_list::mask_list", 147 tools_printf(gettext("line exceeding the maximum of %d characters in listing file %S, aborting. Concerned line starts with: %s"), 148 buf_size - 1, 149 &filename_list_st, 150 buffer)); 151 } 152 buffer[lu] = '\0'; 153 if(!case_s) 154 tools_to_upper(beg, str_beg); 155 else 156 str_beg = string(beg); 157 current_entry += str_beg; 158 } 159 } 160 while(curs < lu); 161 } 162 } 163 while(lu > 0); 164 165 if(current_entry != "") 166 tmp.push_back(current_entry); 167 } 168 catch(...) 169 { 170 meta_delete(buffer); 171 throw; 172 } 173 meta_delete(buffer); 174 buffer = nullptr; 175 176 177 ///////////// 178 // completing relative paths of the list 179 180 if(prefix.is_relative() && !prefix.is_subdir_of("<ROOT>", true)) 181 throw Erange("mask_list::mask_list", gettext("Mask_list's prefix must be an absolute path or start with \"<ROOT>\" string for archive merging")); 182 else 183 { 184 path current = "/"; 185 list <string>::iterator it = tmp.begin(); 186 187 while(it != tmp.end()) 188 { 189 try 190 { 191 current = *it; 192 if(current.is_relative()) 193 { 194 current = prefix + current; 195 *it = current.display(); 196 } 197 } 198 catch(Egeneric & e) 199 { 200 string err = e.get_message(); 201 string line = *it; 202 203 throw Erange("mask_list::mask_list", tools_printf(gettext("Error met while reading line\n\t%S\n from file %S: %S"), &line, &filename_list_st, &err)); 204 } 205 it++; 206 } 207 } 208 209 ///////////// 210 // sorting the list of entry 211 212 // sorting the list with a modified lexicographical order where the / as is lowest character, other letter order unchanged 213 tmp.sort(&modified_lexicalorder_a_lessthan_b); 214 tmp.unique(); // remove duplicates 215 216 // converting the sorted list to vector, to get the indexing feature of this type 217 contenu.assign(tmp.begin(), tmp.end()); 218 taille = contenu.size(); 219 if(taille < contenu.size()) 220 throw Erange("mask_list::mask_list", tools_printf(gettext("Too much line in file %S (integer overflow)"), &filename_list_st)); 221 } 222 catch(Egeneric & e) 223 { 224 e.prepend_message(tools_printf(gettext("Error met while opening %S: "), &filename_list_st)); 225 throw; 226 } 227 } 228 catch(...) 229 { 230 NLS_SWAP_OUT; 231 throw; 232 } 233 NLS_SWAP_OUT; 234 } 235 is_covered(const string & expression) const236 bool mask_list::is_covered(const string & expression) const 237 { 238 if(taille == 0) 239 return false; 240 241 U_I min = 0, max = taille-1, tmp; 242 string target; 243 bool ret; 244 245 if(case_s) 246 target = expression; 247 else 248 tools_to_upper(expression, target); 249 250 // divide & conquer algorithm on a sorted list (aka binary search) 251 while(max - min > 1) 252 { 253 tmp = (min + max)/2; 254 if(modified_lexicalorder_a_lessthan_b(contenu[tmp], target)) 255 min = tmp; 256 else 257 if(contenu[tmp] == target) 258 max = min = tmp; 259 else 260 max = tmp; 261 } 262 if(min == 0 && modified_lexicalorder_a_lessthan_b(target, contenu[min])) 263 max = min; 264 265 ret = contenu[max] == target || contenu[min] == target; 266 if(including && !ret) // if including files, we must also include directories leading to a listed file 267 { 268 string c_max = contenu[max]; 269 ret = path(c_max).is_subdir_of(expression, case_s); 270 } 271 272 return ret; 273 } 274 dump(const string & prefix) const275 string mask_list::dump(const string & prefix) const 276 { 277 vector<string>::const_iterator it = contenu.begin(); 278 string rec_pref = prefix + " | "; 279 280 string ret = prefix + "If matches one of the following line(s):\n"; 281 while(it != contenu.end()) 282 { 283 ret += rec_pref + *it + "\n"; 284 ++it; 285 } 286 ret += prefix + " +--"; 287 288 return ret; 289 } 290 291 modified_lexicalorder_a_lessthan_b(const string & a,const string & b)292 static bool modified_lexicalorder_a_lessthan_b(const string & a, const string & b) 293 { 294 string::const_iterator at = a.begin(); 295 string::const_iterator bt = b.begin(); 296 297 while(at != a.end() && bt != b.end()) 298 { 299 if(*at == '/') 300 { 301 if(*bt != '/') 302 return true; 303 304 // else both a and b current letter are equal to '/' 305 // reading further 306 } 307 else 308 { 309 if(*bt == '/') 310 return false; 311 else 312 { 313 if(*at != *bt) 314 return *at < *bt; 315 316 // else a and b letter are equal 317 // reading further to find a difference 318 } 319 } 320 321 ++at; 322 ++bt; 323 } 324 325 if(at == a.end()) 326 return true; // even if bt == b.end(), we have a <= b 327 328 if(bt == b.end()) 329 return false; // because at != a.end(), we thus have neither a == b nor a < b 330 else 331 throw SRC_BUG; // at != a.end() and bt != b.end() how did we escaped the while loop? 332 } 333 334 } // end of namespace 335