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_STRING_H 27 #include <string.h> 28 #endif 29 30 #if HAVE_STRINGS_H 31 #include <strings.h> 32 #endif 33 34 #if STDC_HEADERS 35 # include <string.h> 36 #else 37 # if !HAVE_STRCHR 38 # define strchr index 39 # define strrchr rindex 40 # endif 41 char *strchr (), *strrchr (); 42 # if !HAVE_MEMCPY 43 # define memcpy(d, s, n) bcopy ((s), (d), (n)) 44 # define memmove(d, s, n) bcopy ((s), (d), (n)) 45 # endif 46 #endif 47 48 #if HAVE_SYS_TYPES_H 49 #include <sys/types.h> 50 #endif 51 #if HAVE_SYS_WAIT_H 52 # include <sys/wait.h> 53 #endif 54 #ifndef WEXITSTATUS 55 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 56 #endif 57 #ifndef WIFEXITED 58 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 59 #endif 60 #ifndef WIFSTOPPED 61 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) 62 #endif 63 #ifndef WIFSIGNALED 64 # define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) 65 #endif 66 #ifndef WTERMSIG 67 #define WTERMSIG(status) ((status) & 0x7f) 68 #endif 69 70 #if HAVE_ERRNO_H 71 #include <errno.h> 72 #endif 73 74 #if HAVE_SYS_STAT_H 75 #include <sys/stat.h> 76 #endif 77 78 #if HAVE_FCNTL_H 79 #include <fcntl.h> 80 #endif 81 82 #if HAVE_UNISTD_H 83 #include <unistd.h> 84 #endif 85 86 #if HAVE_SIGNAL_H 87 #include <signal.h> 88 #endif 89 90 #if HAVE_SYS_TYPE_H 91 #include <sys/types.h> 92 #endif 93 94 #if HAVE_UTIME_H 95 #include <utime.h> 96 #endif 97 98 #if HAVE_SYS_TIME_H 99 #include <sys/time.h> 100 #endif 101 102 #if HAVE_CTYPE_H 103 #include <ctype.h> 104 #endif 105 106 #if HAVE_PWD_H 107 #include <pwd.h> 108 #endif 109 110 #if HAVE_GRP_H 111 #include <grp.h> 112 #endif 113 114 #if HAVE_STDLIB_H 115 #include <stdlib.h> 116 #endif 117 118 #if HAVE_SYS_UTSNAME_H 119 #include <sys/utsname.h> 120 #endif 121 122 #if HAVE_WCHAR_H 123 #include <wchar.h> 124 #endif 125 126 #if HAVE_WCTYPE_H 127 #include <wctype.h> 128 #endif 129 130 #if HAVE_STDDEF_H 131 #include <stddef.h> 132 #endif 133 134 #if HAVE_DIRENT_H 135 #include <dirent.h> 136 #endif 137 } // end extern "C" 138 139 #include <iostream> 140 #include <algorithm> 141 #include <sstream> 142 143 #include "nls_swap.hpp" 144 #include "tools.hpp" 145 #include "erreurs.hpp" 146 #include "deci.hpp" 147 #include "user_interaction.hpp" 148 #include "integers.hpp" 149 #include "mask.hpp" 150 #include "etage.hpp" 151 #include "elastic.hpp" 152 #ifdef __DYNAMIC__ 153 #include "user_group_bases.hpp" 154 #endif 155 #include "compile_time_features.hpp" 156 #include "memory_file.hpp" 157 158 #define YES_NO(x) (x ? gettext("YES") : gettext("NO")) 159 160 using namespace std; 161 162 namespace libdar 163 { 164 165 #ifdef __DYNAMIC__ 166 // Yes, this is a static variable, 167 // it contains the necessary mutex to keep libdar thread-safe 168 static const user_group_bases *user_group = nullptr; 169 #endif 170 171 // the following variable is static this breaks the threadsafe support 172 // while it also concerns the signaling which is out process related 173 174 static void runson(user_interaction & dialog, char * const argv[]); 175 static void ignore_deadson(S_I sig); 176 static void abort_on_deadson(S_I sig); 177 static bool is_a_slice_available(user_interaction & ui, const string & base, const string & extension, memory_pool *pool); 178 static string retreive_basename(const string & base, const string & extension); 179 static void tools_localtime(const time_t & timep, struct tm *result); 180 tools_init()181 void tools_init() 182 { 183 #ifdef __DYNAMIC__ 184 if(user_group == nullptr) 185 { 186 user_group = new (nothrow) user_group_bases(); 187 if(user_group == nullptr) 188 throw Ememory("tools_init"); 189 } 190 #endif 191 } 192 tools_end()193 void tools_end() 194 { 195 #ifdef __DYNAMIC__ 196 if(user_group != nullptr) 197 { 198 delete user_group; 199 user_group = nullptr; 200 } 201 #endif 202 } 203 204 tools_str2charptr(const string & x)205 char *tools_str2charptr(const string &x) 206 { 207 U_I size = x.size(); 208 char *ret = new (nothrow) char[size+1]; 209 210 if(ret == nullptr) 211 throw Ememory("tools_str2charptr"); 212 (void)memcpy(ret, x.c_str(), size); 213 ret[size] = '\0'; 214 215 return ret; 216 } 217 tools_write_string(generic_file & f,const string & s)218 void tools_write_string(generic_file & f, const string & s) 219 { 220 tools_write_string_all(f, s); 221 f.write("", 1); // adding a '\0' at the end; 222 } 223 tools_read_string(generic_file & f,string & s)224 void tools_read_string(generic_file & f, string & s) 225 { 226 char a[2] = { 0, 0 }; 227 S_I lu; 228 229 s = ""; 230 do 231 { 232 lu = f.read(a, 1); 233 if(lu == 1 && a[0] != '\0') 234 s += a; 235 } 236 while(lu == 1 && a[0] != '\0'); 237 238 if(lu != 1 || a[0] != '\0') 239 throw Erange("tools_read_string", dar_gettext("Not a zero terminated string in file")); 240 } 241 tools_write_string_all(generic_file & f,const string & s)242 void tools_write_string_all(generic_file & f, const string & s) 243 { 244 f.write(s.c_str(), s.size()); 245 } 246 tools_read_string_size(generic_file & f,string & s,infinint taille)247 void tools_read_string_size(generic_file & f, string & s, infinint taille) 248 { 249 U_16 small_read = 0; 250 U_I max_read = 0; 251 S_I lu = 0; 252 const U_I buf_size = 10240; 253 char buffer[buf_size]; 254 255 s = ""; 256 do 257 { 258 if(small_read > 0) 259 { 260 max_read = small_read > buf_size ? buf_size : small_read; 261 lu = f.read(buffer, max_read); 262 small_read -= lu; 263 s += string((char *)buffer, (char *)buffer+lu); 264 } 265 taille.unstack(small_read); 266 } 267 while(small_read > 0); 268 } 269 tools_get_filesize(const path & p)270 infinint tools_get_filesize(const path &p) 271 { 272 struct stat buf; 273 274 if(lstat(p.display().c_str(), &buf) < 0) 275 { 276 string tmp = tools_strerror_r(errno); 277 throw Erange("tools_get_filesize", tools_printf(dar_gettext("Cannot get file size: %s"), tmp.c_str())); 278 } 279 280 return (U_32)buf.st_size; 281 } 282 tools_get_extended_size(string s,U_I base)283 infinint tools_get_extended_size(string s, U_I base) 284 { 285 U_I len = s.size(); 286 infinint factor = 1; 287 288 if(len < 1) 289 return false; 290 switch(s[len-1]) 291 { 292 case 'K': 293 case 'k': // kilobyte 294 factor = base; 295 break; 296 case 'M': // megabyte 297 factor = infinint(base).power((U_I)2); 298 break; 299 case 'G': // gigabyte 300 factor = infinint(base).power((U_I)3); 301 break; 302 case 'T': // terabyte 303 factor = infinint(base).power((U_I)4); 304 break; 305 case 'P': // petabyte 306 factor = infinint(base).power((U_I)5); 307 break; 308 case 'E': // exabyte 309 factor = infinint(base).power((U_I)6); 310 break; 311 case 'Z': // zettabyte 312 factor = infinint(base).power((U_I)7); 313 break; 314 case 'Y': // yottabyte 315 factor = infinint(base).power((U_I)8); 316 break; 317 case '0': 318 case '1': 319 case '2': 320 case '3': 321 case '4': 322 case '5': 323 case '6': 324 case '7': 325 case '8': 326 case '9': 327 break; 328 default : 329 throw Erange("command_line get_extended_size", tools_printf(dar_gettext("Unknown suffix [%c] in string %S"), s[len-1], &s)); 330 } 331 332 if(factor != 1) 333 s = string(s.begin(), s.end()-1); 334 335 deci tmp = s; 336 factor *= tmp.computer(); 337 338 return factor; 339 } 340 tools_display_integer_in_metric_system(infinint number,const string & unit,bool binary)341 string tools_display_integer_in_metric_system(infinint number, const string & unit, bool binary) 342 { 343 string ret = ""; 344 infinint multiple = binary ? 1024 : 1000; 345 U_I power = 0; 346 // 1 = 'k', 2 = 'M', 3 = 'G', 4 = 'T', 5 = 'P', 6 = 'E', 7 = 'Z', 8 = 'Y' 347 348 while(number >= multiple && power < 8) 349 { 350 ++power; 351 number /= multiple; 352 } 353 354 ret = deci(number).human(); 355 if(unit.size() > 0) 356 ret += " "; // a space is required by convention to separate the number from its unit 357 358 switch(power) 359 { 360 case 0: 361 if(!number.is_zero()) 362 ret += unit; 363 // not displaying unit for zero for clarity in particular when octets symbol is used 364 // which would give "0 o" that is somehow not very easy to read/understand 365 break; 366 case 1: 367 ret += (binary ? "ki" : "k") + unit; 368 break; 369 case 2: 370 ret += (binary ? "Mi" : "M") + unit; 371 break; 372 case 3: 373 ret += (binary ? "Gi" : "G") + unit; 374 break; 375 case 4: 376 ret += (binary ? "Ti" : "T") + unit; 377 break; 378 case 5: 379 ret += (binary ? "Pi" : "P") + unit; 380 break; 381 case 6: 382 ret += (binary ? "Ei" : "E") + unit; 383 break; 384 case 7: 385 ret += (binary ? "Zi" : "Z") + unit; 386 break; 387 default: 388 ret += (binary ? "Yi" : "Y") + unit; 389 break; 390 } 391 392 return ret; 393 } 394 tools_extract_basename(const char * command_name,string & basename)395 void tools_extract_basename(const char *command_name, string &basename) 396 { 397 basename = path(command_name).basename(); 398 } 399 tools_find_last_char_of(string & s,unsigned char v)400 string::iterator tools_find_last_char_of(string &s, unsigned char v) 401 { 402 if(s.empty()) 403 return s.end(); 404 405 string::iterator back, it = s.begin(); 406 bool valid = (it != s.end()) && (*it == v); 407 408 while(it != s.end()) 409 { 410 back = it; 411 it = find(it + 1, s.end(), v); 412 } 413 414 if(!valid && (back == s.begin())) // no char found at all (back has been sticked at the beginning and the first character is not the one we look for 415 return s.end(); 416 else 417 return back; 418 } 419 420 tools_find_first_char_of(string & s,unsigned char v)421 string::iterator tools_find_first_char_of(string &s, unsigned char v) 422 { 423 string::iterator it = s.begin(); 424 425 while(it != s.end() && *it != v) 426 ++it; 427 428 return it; 429 } 430 tools_split_path_basename(const char * all,path * & chemin,string & base,memory_pool * pool)431 void tools_split_path_basename(const char *all, path * &chemin, string & base, memory_pool *pool) 432 { 433 chemin = nullptr; 434 string src = all; 435 string::iterator it = tools_find_last_char_of(src, '/'); 436 437 if(it != src.end()) // path separator found (pointed to by "it") 438 { 439 it += 1; 440 base = string(it, src.end()); 441 chemin = new (pool) path(string(src.begin(), it), true); 442 } 443 else 444 { 445 base = src; 446 chemin = new (pool) path("."); 447 } 448 449 if(chemin == nullptr) 450 throw Ememory("tools_split_path_basename"); 451 } 452 tools_split_path_basename(const string & all,string & chemin,string & base,memory_pool * pool)453 void tools_split_path_basename(const string & all, string & chemin, string & base, memory_pool *pool) 454 { 455 path *tmp = nullptr; 456 457 tools_split_path_basename(all.c_str(), tmp, base, pool); 458 if(tmp == nullptr) 459 throw SRC_BUG; 460 chemin = tmp->display(); 461 delete tmp; 462 } 463 tools_open_pipes(user_interaction & dialog,const string & input,const string & output,tuyau * & in,tuyau * & out,memory_pool * pool)464 void tools_open_pipes(user_interaction & dialog, 465 const string &input, 466 const string & output, 467 tuyau *&in, 468 tuyau *&out, 469 memory_pool *pool) 470 { 471 in = out = nullptr; 472 try 473 { 474 if(input != "") 475 in = new (pool) tuyau(dialog, input, gf_read_only); 476 else 477 in = new (pool) tuyau(dialog, 0, gf_read_only); // stdin by default 478 if(in == nullptr) 479 throw Ememory("tools_open_pipes"); 480 481 if(output != "") 482 out = new (pool) tuyau(dialog, output, gf_write_only); 483 else 484 out = new (pool) tuyau(dialog, 1, gf_write_only); // stdout by default 485 if(out == nullptr) 486 throw Ememory("tools_open_pipes"); 487 488 } 489 catch(...) 490 { 491 if(in != nullptr) 492 delete in; 493 if(out != nullptr) 494 delete out; 495 throw; 496 } 497 } 498 tools_blocking_read(S_I fd,bool mode)499 void tools_blocking_read(S_I fd, bool mode) 500 { 501 S_I flags = fcntl(fd, F_GETFL, 0); 502 if(flags < 0) 503 throw Erange("tools_blocking_read", string(dar_gettext("Cannot read \"fcntl\" file's flags : "))+tools_strerror_r(errno)); 504 if(!mode) 505 flags |= O_NONBLOCK; 506 else 507 flags &= ~O_NONBLOCK; 508 if(fcntl(fd, F_SETFL, flags) < 0) 509 throw Erange("tools_blocking_read", string(dar_gettext("Cannot set \"fcntl\" file's flags : "))+tools_strerror_r(errno)); 510 } 511 tools_name_of_uid(const infinint & uid)512 string tools_name_of_uid(const infinint & uid) 513 { 514 #ifndef __DYNAMIC__ 515 string name = ""; 516 #else 517 string name; 518 if(user_group != nullptr) 519 name = user_group->get_username(uid); 520 else 521 throw SRC_BUG; 522 #endif 523 524 if(name.empty()) // uid not associated with a name 525 { 526 deci d = uid; 527 return d.human(); 528 } 529 else 530 return name; 531 } 532 tools_name_of_gid(const infinint & gid)533 string tools_name_of_gid(const infinint & gid) 534 { 535 #ifndef __DYNAMIC__ 536 string name = ""; 537 #else 538 string name; 539 if(user_group != nullptr) 540 name = user_group->get_groupname(gid); 541 else 542 throw SRC_BUG; 543 #endif 544 545 if(name.empty()) // uid not associated with a name 546 { 547 deci d = gid; 548 return d.human(); 549 } 550 else 551 return name; 552 } 553 tools_uword2str(U_16 x)554 string tools_uword2str(U_16 x) 555 { 556 ostringstream tmp; 557 558 tmp << x; 559 560 return tmp.str(); 561 } 562 tools_int2str(S_I x)563 string tools_int2str(S_I x) 564 { 565 ostringstream tmp; 566 567 tmp << x; 568 569 return tmp.str(); 570 } 571 tools_uint2str(U_I x)572 string tools_uint2str(U_I x) 573 { 574 ostringstream tmp; 575 576 tmp << x; 577 578 return tmp.str(); 579 } 580 581 tools_str2int(const string & x)582 U_I tools_str2int(const string & x) 583 { 584 stringstream tmp(x); 585 U_I ret; 586 string residu; 587 588 if((tmp >> ret).fail()) 589 throw Erange("tools_str2string", string(dar_gettext("Invalid number: ")) + x); 590 591 tmp >> residu; 592 for(U_I i = 0; i < residu.size(); ++i) 593 if(residu[i] != ' ') 594 throw Erange("tools_str2string", string(dar_gettext("Invalid number: ")) + x); 595 596 return ret; 597 } 598 tools_str2signed_int(const string & x)599 S_I tools_str2signed_int(const string & x) 600 { 601 stringstream tmp(x); 602 S_I ret; 603 string residu; 604 605 if((tmp >> ret).fail()) 606 throw Erange("tools_str2string", string(dar_gettext("Invalid number: ")) + x); 607 608 tmp >> residu; 609 for(U_I i = 0; i < residu.size(); ++i) 610 if(residu[i] != ' ') 611 throw Erange("tools_str2string", string(dar_gettext("Invalid number: ")) + x); 612 613 return ret; 614 } 615 tools_my_atoi(const char * a,U_I & val)616 bool tools_my_atoi(const char *a, U_I & val) 617 { 618 try 619 { 620 val = tools_str2int(a); 621 return true; 622 } 623 catch(Erange & e) 624 { 625 val = 0; 626 return false; 627 } 628 } 629 tools_addspacebefore(string s,U_I expected_size)630 string tools_addspacebefore(string s, U_I expected_size) 631 { 632 return string(expected_size - s.size(), ' ') + s; 633 } 634 tools_display_date(const datetime & date)635 string tools_display_date(const datetime & date) 636 { 637 time_t pas = 0; 638 time_t frac = 0; 639 string ret; 640 641 if(!date.get_value(pas, frac, datetime::tu_second)) // conversion to system type failed. Using a replacement string 642 return deci(date.get_second_value()).human(); 643 else 644 { 645 char *val = nullptr; 646 #if HAVE_CTIME_R 647 char *str = new (nothrow) char [50]; //< minimum required is 26 bytes 648 if(str == nullptr) 649 throw Ememory("tools_display_date"); 650 try 651 { 652 val = ctime_r(&pas, str); 653 #else 654 val = ctime(&pas); 655 #endif 656 if(val == nullptr) // ctime() failed 657 ret = tools_int2str(pas); 658 else 659 ret = val; 660 #if HAVE_CTIME_R 661 } 662 catch(...) 663 { 664 delete [] str; 665 throw; 666 } 667 delete [] str; 668 #else 669 ret = val; 670 #endif 671 } 672 673 return string(ret.begin(), ret.end() - 1); // -1 to remove the ending '\n' 674 } 675 tools_convert_date(const string & repres)676 infinint tools_convert_date(const string & repres) 677 { 678 enum status { init, year, month, day, hour, min, sec, error, finish }; 679 680 /// first we define a helper class 681 class scan 682 { 683 public: 684 scan(const tm & now) 685 { 686 etat = init; 687 when = now; 688 when.tm_sec = when.tm_min = when.tm_hour = 0; 689 when.tm_wday = 0; // ignored by mktime 690 when.tm_yday = 0; // ignored by mktime 691 when.tm_isdst = 1; // provided time is local daylight saving time 692 tmp = 0; 693 }; 694 695 status get_etat() const { return etat; }; 696 tm get_struct() const { return when; }; 697 void add_digit(char a) 698 { 699 if(a < 48 || a > 57) // ascii code for zero is 48, for nine is 57 700 throw SRC_BUG; 701 tmp = tmp*10 + (a-48); 702 }; 703 704 void set_etat(const status & val) 705 { 706 switch(etat) 707 { 708 case year: 709 if(tmp < 1970) 710 throw Erange("tools_convert_date", dar_gettext("date before 1970 is not allowed")); 711 when.tm_year = tmp - 1900; 712 break; 713 case month: 714 if(tmp < 1 || tmp > 12) 715 throw Erange("tools_convert_date", dar_gettext("Incorrect month")); 716 when.tm_mon = tmp - 1; 717 break; 718 case day: 719 if(tmp < 1 || tmp > 31) 720 throw Erange("tools_convert_date", dar_gettext("Incorrect day of month")); 721 when.tm_mday = tmp; 722 break; 723 case hour: 724 if(tmp < 0 || tmp > 23) 725 throw Erange("tools_convert_date", dar_gettext("Incorrect hour")); 726 when.tm_hour = tmp; 727 break; 728 case min: 729 if(tmp < 0 || tmp > 59) 730 throw Erange("tools_convert_date", dar_gettext("Incorrect minute")); 731 when.tm_min = tmp; 732 break; 733 case sec: 734 if(tmp < 0 || tmp > 59) 735 throw Erange("tools_convert_date", dar_gettext("Incorrect second")); 736 when.tm_sec = tmp; 737 break; 738 case error: 739 throw Erange("tools_convert_date", dar_gettext("Bad formatted date expression")); 740 default: 741 break; // nothing to do 742 } 743 tmp = 0; 744 etat = val; 745 }; 746 747 private: 748 struct tm when; 749 status etat; 750 S_I tmp; 751 }; 752 753 // then we define local variables 754 time_t now = ::time(nullptr), when; 755 struct tm result; 756 tools_localtime(now, &result); 757 scan scanner = scan(result); 758 U_I c, size = repres.size(), ret; 759 struct tm tmp; 760 761 // now we parse the string to update the stucture tm "when" 762 763 // first, determining initial state 764 switch(tools_count_in_string(repres, '/')) 765 { 766 case 0: 767 switch(tools_count_in_string(repres, '-')) 768 { 769 case 0: 770 scanner.set_etat(hour); 771 break; 772 case 1: 773 scanner.set_etat(day); 774 break; 775 default: 776 scanner.set_etat(error); 777 } 778 break; 779 case 1: 780 scanner.set_etat(month); 781 break; 782 case 2: 783 scanner.set_etat(year); 784 break; 785 default: 786 scanner.set_etat(error); 787 } 788 789 // second, parsing the string 790 for(c = 0; c < size && scanner.get_etat() != error; ++c) 791 switch(repres[c]) 792 { 793 case '/': 794 switch(scanner.get_etat()) 795 { 796 case year: 797 scanner.set_etat(month); 798 break; 799 case month: 800 scanner.set_etat(day); 801 break; 802 default: 803 scanner.set_etat(error); 804 } 805 break; 806 case ':': 807 switch(scanner.get_etat()) 808 { 809 case hour: 810 scanner.set_etat(min); 811 break; 812 case min: 813 scanner.set_etat(sec); 814 break; 815 default: 816 scanner.set_etat(error); 817 } 818 break; 819 case '-': 820 switch(scanner.get_etat()) 821 { 822 case day: 823 scanner.set_etat(hour); 824 break; 825 default: 826 scanner.set_etat(error); 827 } 828 break; 829 case ' ': 830 case '\t': 831 case '\n': 832 case '\r': 833 break; // we ignore spaces, tabs, CR and LF 834 case '0': 835 case '1': 836 case '2': 837 case '3': 838 case '4': 839 case '5': 840 case '6': 841 case '7': 842 case '8': 843 case '9': 844 scanner.add_digit(repres[c]); 845 break; 846 default: 847 scanner.set_etat(error); 848 } 849 850 scanner.set_etat(finish); 851 tmp = scanner.get_struct(); 852 when = mktime(&tmp); 853 if(when > now) 854 throw Erange("tools_convert_date", dar_gettext("Given date must be in the past")); 855 ret = when; 856 857 return ret; 858 } 859 tools_system(user_interaction & dialog,const vector<string> & argvector)860 void tools_system(user_interaction & dialog, const vector<string> & argvector) 861 { 862 if(argvector.empty()) 863 return; // nothing to do 864 865 // ISO C++ forbids variable-size array 866 char **argv = new (nothrow) char * [argvector.size()+1]; 867 868 for(U_I i = 0; i <= argvector.size(); i++) 869 argv[i] = nullptr; 870 871 try 872 { 873 S_I status; 874 bool loop; 875 876 for(U_I i = 0; i < argvector.size(); i++) 877 argv[i] = tools_str2charptr(argvector[i]); 878 argv[argvector.size()] = nullptr; // this is already done above but that does not hurt doing it twice :-) 879 880 do 881 { 882 ignore_deadson(0); 883 loop = false; 884 S_I pid = fork(); 885 886 switch(pid) 887 { 888 case -1: 889 throw Erange("tools_system", string(dar_gettext("Error while calling fork() to launch dar: ")) + tools_strerror_r(errno)); 890 case 0: // fork has succeeded, we are the child process 891 try 892 { 893 runson(dialog, argv); // function that never returns or throws exceptions 894 throw SRC_BUG; // just in case the previous function returned 895 } 896 catch(...) 897 { 898 throw SRC_BUG; 899 } 900 default: 901 if(wait(&status) <= 0) 902 throw Erange("tools_system", 903 string(dar_gettext("Unexpected error while waiting for dar to terminate: ")) + tools_strerror_r(errno)); 904 else // checking the way dar has exit 905 if(WIFSIGNALED(status)) // exited because of a signal 906 { 907 try 908 { 909 dialog.pause(string(dar_gettext("DAR terminated upon signal reception: ")) 910 #if HAVE_DECL_SYS_SIGLIST 911 + (WTERMSIG(status) < NSIG ? sys_siglist[WTERMSIG(status)] : tools_int2str(WTERMSIG(status))) 912 #else 913 + tools_int2str(WTERMSIG(status)) 914 #endif 915 + dar_gettext(" . Retry to launch dar as previously ?")); 916 loop = true; 917 } 918 catch(Euser_abort & e) 919 { 920 dialog.pause(dar_gettext(" Continue anyway ?")); 921 } 922 } 923 else // normal terminaison checking exit status code 924 if(WEXITSTATUS(status) != 0) 925 dialog.pause(string(dar_gettext("DAR sub-process has terminated with exit code ")) 926 + tools_int2str(WEXITSTATUS(status)) 927 + dar_gettext(" Continue anyway ?")); 928 } 929 } 930 while(loop); 931 } 932 catch(...) 933 { 934 for(U_I i = 0; i <= argvector.size(); i++) 935 if(argv[i] != nullptr) 936 delete [] argv[i]; 937 delete [] argv; 938 throw; 939 } 940 941 for(U_I i = 0; i <= argvector.size(); i++) 942 if(argv[i] != nullptr) 943 delete [] argv[i]; 944 delete [] argv; 945 } 946 tools_system_with_pipe(user_interaction & dialog,const string & dar_cmd,const vector<string> & argvpipe,memory_pool * pool)947 void tools_system_with_pipe(user_interaction & dialog, 948 const string & dar_cmd, 949 const vector<string> & argvpipe, 950 memory_pool *pool) 951 { 952 const char *argv[] = { dar_cmd.c_str(), "--pipe-fd", nullptr, nullptr }; 953 bool loop = false; 954 955 do 956 { 957 tuyau *tube = nullptr; 958 959 try 960 { 961 tube = new (pool) tuyau(dialog); 962 if(tube == nullptr) 963 throw Ememory("tools_system_with_pipe"); 964 965 const string read_fd = tools_int2str(tube->get_read_fd()); 966 tlv_list pipeargs; 967 S_I status; 968 969 argv[2] = read_fd.c_str(); 970 signal(SIGCHLD, &abort_on_deadson); // do not accept child death 971 972 loop = false; 973 S_I pid = fork(); 974 975 switch(pid) 976 { 977 case -1: 978 throw Erange("tools_system_with_pipe", string(dar_gettext("Error while calling fork() to launch dar: ")) + tools_strerror_r(errno)); 979 case 0: // fork has succeeded, we are the child process 980 try 981 { 982 if(tube != nullptr) 983 { 984 tube->do_not_close_read_fd(); 985 delete tube; // C++ object is destroyed but read filedescriptor has been kept open 986 tube = nullptr; 987 runson(dialog, const_cast<char * const*>(argv)); 988 throw SRC_BUG; 989 } 990 else 991 throw SRC_BUG; 992 } 993 catch(...) 994 { 995 throw SRC_BUG; 996 } 997 default: // fork has succeeded, we are the parent process 998 tube->close_read_fd(); 999 pipeargs = tools_string2tlv_list(dialog, 0, argvpipe); 1000 pipeargs.dump(*tube); 1001 ignore_deadson(0); // now we can ignore SIGCHLD signals just before destroying the pipe filedescriptor, which will trigger and EOF while reading on pipe 1002 // in the child process 1003 delete tube; 1004 tube = nullptr; 1005 1006 if(wait(&status) <= 0) 1007 throw Erange("tools_system", 1008 string(dar_gettext("Unexpected error while waiting for dar to terminate: ")) + tools_strerror_r(errno)); 1009 else // checking the way dar has exit 1010 if(WIFSIGNALED(status)) // exited because of a signal 1011 { 1012 try 1013 { 1014 dialog.pause(string(dar_gettext("DAR terminated upon signal reception: ")) 1015 #if HAVE_DECL_SYS_SIGLIST 1016 + (WTERMSIG(status) < NSIG ? sys_siglist[WTERMSIG(status)] : tools_int2str(WTERMSIG(status))) 1017 #else 1018 + tools_int2str(WTERMSIG(status)) 1019 #endif 1020 + dar_gettext(" . Retry to launch dar as previously ?")); 1021 loop = true; 1022 } 1023 catch(Euser_abort & e) 1024 { 1025 dialog.pause(dar_gettext(" Continue anyway ?")); 1026 } 1027 } 1028 else // normal terminaison checking exit status code 1029 if(WEXITSTATUS(status) != 0) 1030 dialog.pause(string(dar_gettext("DAR sub-process has terminated with exit code ")) 1031 + tools_int2str(WEXITSTATUS(status)) 1032 + dar_gettext(" Continue anyway ?")); 1033 1034 } 1035 } 1036 catch(...) 1037 { 1038 if(tube != nullptr) 1039 delete tube; 1040 throw; 1041 } 1042 if(tube != nullptr) 1043 delete tube; 1044 } 1045 while(loop); 1046 1047 } 1048 tools_write_vector(generic_file & f,const vector<string> & x)1049 void tools_write_vector(generic_file & f, const vector<string> & x) 1050 { 1051 infinint tmp = x.size(); 1052 vector<string>::const_iterator it = x.begin(); 1053 1054 tmp.dump(f); 1055 while(it != x.end()) 1056 tools_write_string(f, *it++); 1057 } 1058 tools_read_vector(generic_file & f,vector<string> & x)1059 void tools_read_vector(generic_file & f, vector<string> & x) 1060 { 1061 infinint tmp = infinint(f); 1062 string elem; 1063 1064 x.clear(); 1065 while(!tmp.is_zero()) 1066 { 1067 tools_read_string(f, elem); 1068 x.push_back(elem); 1069 tmp--; 1070 } 1071 } 1072 tools_concat_vector(const string & separator,const vector<string> & x)1073 string tools_concat_vector(const string & separator, const vector<string> & x) 1074 { 1075 string ret = separator; 1076 vector<string>::const_iterator it = x.begin(); 1077 1078 while(it != x.end()) 1079 ret += *it++ + separator; 1080 1081 return ret; 1082 } 1083 operator +(vector<string> a,vector<string> b)1084 vector<string> operator + (vector<string> a, vector<string> b) 1085 { 1086 vector<string>::iterator it = b.begin(); 1087 1088 while(it != b.end()) 1089 a.push_back(*it++); 1090 1091 return a; 1092 } 1093 1094 1095 tools_get_from_env(const char ** env,const char * clef)1096 const char *tools_get_from_env(const char **env, const char *clef) 1097 { 1098 unsigned int index = 0; 1099 const char *ret = nullptr; 1100 1101 if(env == nullptr || clef == nullptr) 1102 return nullptr; 1103 1104 while(ret == nullptr && env[index] != nullptr) 1105 { 1106 unsigned int letter = 0; 1107 while(clef[letter] != '\0' 1108 && env[index][letter] != '\0' 1109 && env[index][letter] != '=' 1110 && clef[letter] == env[index][letter]) 1111 letter++; 1112 if(clef[letter] == '\0' && env[index][letter] == '=') 1113 ret = env[index]+letter+1; 1114 else 1115 index++; 1116 } 1117 1118 return ret; 1119 } 1120 tools_display_features(user_interaction & dialog)1121 void tools_display_features(user_interaction & dialog) 1122 { 1123 NLS_SWAP_IN; 1124 try 1125 { 1126 const char *endy = nullptr; 1127 string time_accuracy = ""; 1128 1129 dialog.printf(gettext(" Libz compression (gzip) : %s\n"), YES_NO(compile_time::libz())); 1130 dialog.printf(gettext(" Libbz2 compression (bzip2) : %s\n"), YES_NO(compile_time::libbz2())); 1131 dialog.printf(gettext(" Liblzo2 compression (lzo) : %s\n"), YES_NO(compile_time::liblzo())); 1132 dialog.printf(gettext(" Liblzma compression (xz) : %s\n"), YES_NO(compile_time::libxz())); 1133 dialog.printf(gettext(" Strong encryption (libgcrypt): %s\n"), YES_NO(compile_time::libgcrypt())); 1134 dialog.printf(gettext(" Public key ciphers (gpgme) : %s\n"), YES_NO(compile_time::public_key_cipher())); 1135 dialog.printf(gettext(" Extended Attributes support : %s\n"), YES_NO(compile_time::ea())); 1136 dialog.printf(gettext(" Large files support (> 2GB) : %s\n"), YES_NO(compile_time::largefile())); 1137 dialog.printf(gettext(" ext2fs NODUMP flag support : %s\n"), YES_NO(compile_time::nodump())); 1138 dialog.printf(gettext(" Special allocation scheme : %s\n"), YES_NO(compile_time::special_alloc())); 1139 if(compile_time::bits() == 0) 1140 dialog.printf(gettext(" Integer size used : unlimited\n")); 1141 else 1142 dialog.printf(gettext(" Integer size used : %d bits\n"), compile_time::bits()); 1143 dialog.printf(gettext(" Thread safe support : %s\n"), YES_NO(compile_time::thread_safe())); 1144 dialog.printf(gettext(" Furtive read mode support : %s\n"), YES_NO(compile_time::furtive_read())); 1145 dialog.printf(gettext(" Linux ext2/3/4 FSA support : %s\n"), YES_NO(compile_time::FSA_linux_extX())); 1146 dialog.printf(gettext(" Mac OS X HFS+ FSA support : %s\n"), YES_NO(compile_time::FSA_birthtime())); 1147 1148 switch(compile_time::system_endian()) 1149 { 1150 case compile_time::big: 1151 endy = gettext("big"); 1152 break; 1153 case compile_time::little: 1154 endy = gettext("little"); 1155 break; 1156 case compile_time::error: 1157 endy = gettext("error!"); 1158 break; 1159 default: 1160 throw SRC_BUG; 1161 } 1162 dialog.printf(gettext(" Detected system/CPU endian : %s"), endy); 1163 dialog.printf(gettext(" Posix fadvise support : %s"), YES_NO(compile_time::posix_fadvise())); 1164 dialog.printf(gettext(" Large dir. speed optimi. : %s"), YES_NO(compile_time::fast_dir())); 1165 if(compile_time::microsecond_read()) 1166 time_accuracy = "1 microsecond"; 1167 else 1168 time_accuracy = "1 s"; 1169 dialog.printf(gettext(" Timestamp read accuracy : %S\n"), &time_accuracy); 1170 if(compile_time::microsecond_write()) 1171 time_accuracy = "1 microsecond"; 1172 else 1173 time_accuracy = "1 s"; 1174 dialog.printf(gettext(" Timestamp write accuracy : %S\n"), &time_accuracy); 1175 dialog.printf(gettext(" Restores dates of symlinks : %s\n"), YES_NO(compile_time::symlink_restore_dates())); 1176 if(compile_time::libthreadar()) 1177 dialog.printf(gettext(" Can uses multiple threads : %s\n"), YES_NO(compile_time::libthreadar())); 1178 } 1179 catch(...) 1180 { 1181 NLS_SWAP_OUT; 1182 throw; 1183 } 1184 NLS_SWAP_OUT; 1185 } 1186 tools_is_equal_with_hourshift(const infinint & hourshift,const datetime & date1,const datetime & date2)1187 bool tools_is_equal_with_hourshift(const infinint & hourshift, const datetime & date1, const datetime & date2) 1188 { 1189 infinint num, rest; 1190 datetime t_delta = date1 > date2 ? date1.loose_diff(date2) : date2.loose_diff(date1); 1191 infinint delta; 1192 1193 if(t_delta.is_null()) 1194 return true; // both args are equal without any hourshift consideration 1195 1196 if(!t_delta.is_integer_second()) 1197 return false; // difference is not an integer number of second 1198 else 1199 delta = t_delta.get_second_value(); 1200 1201 // delta = 3600*num + rest 1202 // with 0 <= rest < 3600 1203 // (this is euclidian division) 1204 euclide(delta, 3600, num, rest); 1205 1206 if(!rest.is_zero()) 1207 return false; // difference is not a integer number of hour 1208 else // rest == 0 1209 return num <= hourshift; 1210 } 1211 tools_check_basename(user_interaction & dialog,const path & loc,string & base,const string & extension,memory_pool * pool)1212 void tools_check_basename(user_interaction & dialog, const path & loc, string & base, const string & extension, memory_pool *pool) 1213 { 1214 NLS_SWAP_IN; 1215 try 1216 { 1217 regular_mask suspect = regular_mask(string(".+\\.[1-9][0-9]*\\.")+extension, true); 1218 string old_path = (loc+base).display(); 1219 1220 // is basename is suspect ? 1221 if(!suspect.is_covered(base)) 1222 return; // not a suspect basename 1223 1224 // is there a slice available ? 1225 if(is_a_slice_available(dialog, old_path, extension, pool)) 1226 return; // yes, thus basename is not a mistake 1227 1228 // removing the suspicious end (.<number>.extension) 1229 // and checking the avaibility of such a slice 1230 1231 string new_base = retreive_basename(base, extension); 1232 string new_path = (loc+new_base).display(); 1233 if(is_a_slice_available(dialog, new_path, extension, pool)) 1234 { 1235 try 1236 { 1237 dialog.pause(tools_printf(gettext("Warning, %S seems more to be a slice name than a base name. Do you want to replace it by %S ?"), &base, &new_base)); 1238 base = new_base; 1239 } 1240 catch(Euser_abort & e) 1241 { 1242 dialog.warning(tools_printf(gettext("OK, keeping %S as basename"), &base)); 1243 } 1244 } 1245 } 1246 catch(...) 1247 { 1248 NLS_SWAP_OUT; 1249 throw; 1250 } 1251 NLS_SWAP_OUT; 1252 } 1253 tools_getcwd()1254 string tools_getcwd() 1255 { 1256 const U_I step = 1024; 1257 U_I length = step; 1258 char *buffer = nullptr, *ret; 1259 string cwd; 1260 try 1261 { 1262 do 1263 { 1264 buffer = new (nothrow) char[length]; 1265 if(buffer == nullptr) 1266 throw Ememory("tools_getcwd()"); 1267 ret = getcwd(buffer, length-1); // length-1 to keep a place for ending '\0' 1268 if(ret == nullptr) // could not get the CWD 1269 { 1270 if(errno == ERANGE) // buffer too small 1271 { 1272 delete [] buffer; 1273 buffer = nullptr; 1274 length += step; 1275 } 1276 else // other error 1277 throw Erange("tools_getcwd", string(dar_gettext("Cannot get full path of current working directory: ")) + tools_strerror_r(errno)); 1278 } 1279 } 1280 while(ret == nullptr); 1281 1282 buffer[length - 1] = '\0'; 1283 cwd = buffer; 1284 } 1285 catch(...) 1286 { 1287 if(buffer != nullptr) 1288 delete [] buffer; 1289 throw; 1290 } 1291 if(buffer != nullptr) 1292 delete [] buffer; 1293 return cwd; 1294 } 1295 tools_readlink(const char * root)1296 string tools_readlink(const char *root) 1297 { 1298 U_I length = 10240; 1299 char *buffer = nullptr; 1300 S_I lu; 1301 string ret = ""; 1302 1303 if(root == nullptr) 1304 throw Erange("tools_readlink", dar_gettext("nullptr argument given to tools_readlink()")); 1305 if(strcmp(root, "") == 0) 1306 throw Erange("tools_readlink", dar_gettext("Empty string given as argument to tools_readlink()")); 1307 1308 try 1309 { 1310 do 1311 { 1312 buffer = new (nothrow) char[length]; 1313 if(buffer == nullptr) 1314 throw Ememory("tools_readlink"); 1315 lu = readlink(root, buffer, length-1); // length-1 to have room to add '\0' at the end 1316 1317 if(lu < 0) // error occured with readlink 1318 { 1319 string tmp; 1320 1321 switch(errno) 1322 { 1323 case EINVAL: // not a symbolic link (thus we return the given argument) 1324 ret = root; 1325 break; 1326 case ENAMETOOLONG: // too small buffer 1327 delete [] buffer; 1328 buffer = nullptr; 1329 length *= 2; 1330 break; 1331 default: // other error 1332 tmp = tools_strerror_r(errno); 1333 throw Erange("get_readlink", tools_printf(dar_gettext("Cannot read file information for %s : %s"), root, tmp.c_str())); 1334 } 1335 } 1336 else // got the correct real path of symlink 1337 if((U_I)(lu) < length) 1338 { 1339 buffer[lu] = '\0'; 1340 ret = buffer; 1341 } 1342 else // "lu" should not be greater than length: readlink system call error 1343 { 1344 // trying to workaround with a larger buffer 1345 delete [] buffer; 1346 buffer = nullptr; 1347 length *= 2; 1348 } 1349 } 1350 while(ret == ""); 1351 } 1352 catch(...) 1353 { 1354 if(buffer != nullptr) 1355 delete [] buffer; 1356 throw; 1357 } 1358 if(buffer != nullptr) 1359 delete [] buffer; 1360 return ret; 1361 } 1362 1363 tools_look_for(const char * argument,S_I argc,char * const argv[])1364 bool tools_look_for(const char *argument, S_I argc, char *const argv[]) 1365 { 1366 S_I count = 0; 1367 1368 while(count < argc && strcmp(argv[count], argument) != 0) 1369 count++; 1370 1371 return count < argc; 1372 } 1373 tools_make_date(const std::string & chemin,bool symlink,const datetime & access,const datetime & modif,const datetime & birth)1374 void tools_make_date(const std::string & chemin, bool symlink, const datetime & access, const datetime & modif, const datetime & birth) 1375 { 1376 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1377 struct timeval temps[2]; 1378 #else 1379 struct utimbuf temps; 1380 #endif 1381 time_t tmp = 0; 1382 time_t usec = 0; 1383 int ret; 1384 1385 if(!access.get_value(tmp, usec, datetime::tu_microsecond)) 1386 throw Erange("tools_make_date", "cannot set atime of file, value too high for the system integer type"); 1387 1388 // the first time, setting modification time to the value of birth time 1389 // systems that supports birth time update birth time if the given mtime is older than the current birth time 1390 // so here we assume birth < modif (if not the birth time will be set to modif) 1391 // we run a second time the same call but with the real mtime, which should not change the birthtime if this 1392 // one is as expected older than mtime. 1393 else 1394 { 1395 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1396 temps[0].tv_sec = tmp; 1397 temps[0].tv_usec = usec; 1398 #else 1399 temps.actime = tmp; 1400 #endif 1401 } 1402 1403 if(birth != modif) 1404 { 1405 if(!birth.get_value(tmp, usec, datetime::tu_microsecond)) 1406 throw Erange("tools_make_date", "cannot set birth time of file, value too high for the system integer type"); 1407 else 1408 { 1409 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1410 temps[1].tv_sec = tmp; 1411 temps[1].tv_usec = usec; 1412 #else 1413 temps.modtime = tmp; 1414 #endif 1415 } 1416 1417 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1418 #ifdef HAVE_LUTIMES 1419 ret = lutimes(chemin.c_str(), temps); 1420 #else 1421 if(symlink) 1422 return; // not able to restore dates of symlinks 1423 ret = utimes(chemin.c_str(), temps); 1424 #endif 1425 #else 1426 if(symlink) 1427 return; // not able to restore dates of symlinks 1428 ret = utime(chemin.c_str() , &temps); 1429 #endif 1430 if(ret < 0) 1431 Erange("tools_make_date", string(dar_gettext("Cannot set birth time: ")) + tools_strerror_r(errno)); 1432 } 1433 1434 // we set atime and mtime here 1435 if(!modif.get_value(tmp, usec, datetime::tu_microsecond)) 1436 throw Erange("tools_make_date", "cannot set last modification time of file, value too high for the system integer type"); 1437 else 1438 { 1439 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1440 temps[1].tv_sec = tmp; 1441 temps[1].tv_usec = usec; 1442 #else 1443 temps.modtime = tmp; 1444 #endif 1445 } 1446 1447 #ifdef LIBDAR_MICROSECOND_WRITE_ACCURACY 1448 #ifdef HAVE_LUTIMES 1449 ret = lutimes(chemin.c_str(), temps); 1450 #else 1451 if(symlink) 1452 return; // not able to restore dates of symlinks 1453 ret = utimes(chemin.c_str(), temps); 1454 #endif 1455 #else 1456 if(symlink) 1457 return; // not able to restore dates of symlinks 1458 ret = utime(chemin.c_str() , &temps); 1459 #endif 1460 if(ret < 0) 1461 throw Erange("tools_make_date", string(dar_gettext("Cannot set last access and last modification time: ")) + tools_strerror_r(errno)); 1462 } 1463 tools_noexcept_make_date(const string & chem,bool symlink,const datetime & last_acc,const datetime & last_mod,const datetime & birth)1464 void tools_noexcept_make_date(const string & chem, bool symlink, const datetime & last_acc, const datetime & last_mod, const datetime & birth) 1465 { 1466 try 1467 { 1468 if(!last_acc.is_null() || !last_mod.is_null()) 1469 tools_make_date(chem, symlink, last_acc, last_mod, birth); 1470 // else the directory could not be openned properly 1471 // and time could not be retrieved, so we don't try 1472 // to restore them 1473 } 1474 catch(Erange & e) 1475 { 1476 // cannot restore dates, ignoring 1477 } 1478 } 1479 tools_is_case_insensitive_equal(const string & a,const string & b)1480 bool tools_is_case_insensitive_equal(const string & a, const string & b) 1481 { 1482 U_I curs = 0; 1483 if(a.size() != b.size()) 1484 return false; 1485 1486 while(curs < a.size() && tolower(a[curs]) == tolower(b[curs])) 1487 curs++; 1488 1489 return curs >= a.size(); 1490 } 1491 tools_to_upper(const string & r,string & uppered)1492 void tools_to_upper(const string & r, string & uppered) 1493 { 1494 #if HAVE_WCTYPE_H && HAVE_WCHAR_H 1495 try 1496 { 1497 wstring tmp = tools_string_to_wstring(r); 1498 tools_to_wupper(tmp); 1499 uppered = tools_wstring_to_string(tmp); 1500 } 1501 catch(Erange & e) 1502 { 1503 U_I taille = r.size(); 1504 uppered = r; 1505 1506 for(U_I x = 0; x < taille; ++x) 1507 uppered[x] = toupper(uppered[x]); 1508 } 1509 #else 1510 U_I taille = r.size(); 1511 uppered = r; 1512 1513 for(U_I x = 0; x < taille; ++x) 1514 uppered[x] = toupper(uppered[x]); 1515 #endif 1516 } 1517 1518 #if HAVE_WCTYPE_H tools_to_wupper(wstring & r)1519 void tools_to_wupper(wstring & r) 1520 { 1521 wstring::iterator it = r.begin(); 1522 1523 while(it != r.end()) 1524 { 1525 *it = towupper(*it); 1526 ++it; 1527 } 1528 } 1529 #endif 1530 tools_remove_last_char_if_equal_to(char c,string & s)1531 void tools_remove_last_char_if_equal_to(char c, string & s) 1532 { 1533 if(s[s.size()-1] == c) 1534 s = string(s.begin(), s.begin()+(s.size() - 1)); 1535 } 1536 ignore_deadson(S_I sig)1537 static void ignore_deadson(S_I sig) 1538 { 1539 signal(SIGCHLD, &ignore_deadson); 1540 } 1541 abort_on_deadson(S_I sig)1542 static void abort_on_deadson(S_I sig) 1543 { 1544 // we cannot throw exception in a handler (it would not be caught) we have no other way to report to standard error 1545 cerr << dar_gettext("Aborting program: child process died unexpectedly") << endl; 1546 } 1547 runson(user_interaction & dialog,char * const argv[])1548 static void runson(user_interaction & dialog, char * const argv[]) 1549 { 1550 if(execvp(argv[0], argv) < 0) 1551 { 1552 string tmp = tools_strerror_r(errno); 1553 dialog.warning(tools_printf(dar_gettext("Error trying to run %s: %s"), argv[0], tmp.c_str())); 1554 } 1555 else 1556 dialog.warning(string(dar_gettext("execvp() failed but did not returned error code"))); 1557 #ifndef EXIT_ERROR 1558 #define EXIT_ERROR 2 1559 exit(EXIT_ERROR); 1560 // we need the appropriate dar exit status 1561 // but we are in libdar thus cannot include dar_suite.hpp header 1562 // we thus copy 1563 #undef EXIT_ERROR 1564 #else 1565 exit(EXIT_ERROR); 1566 #endif 1567 } 1568 is_a_slice_available(user_interaction & ui,const string & base,const string & extension,memory_pool * pool)1569 static bool is_a_slice_available(user_interaction & ui, const string & base, const string & extension, memory_pool *pool) 1570 { 1571 path *chem = nullptr; 1572 bool ret = false; 1573 1574 try 1575 { 1576 string rest; 1577 1578 tools_split_path_basename(base.c_str(), chem, rest, pool); 1579 1580 try 1581 { 1582 etage contents = etage(ui, chem->display().c_str(), datetime(0), datetime(0), false, false); // we don't care the dates here so we set them to zero 1583 regular_mask slice = regular_mask(rest + "\\.[1-9][0-9]*\\."+ extension, true); 1584 1585 while(!ret && contents.read(rest)) 1586 ret = slice.is_covered(rest); 1587 } 1588 catch(Erange & e) 1589 { 1590 ret = false; 1591 } 1592 } 1593 catch(...) 1594 { 1595 if(chem != nullptr) 1596 delete chem; 1597 throw; 1598 } 1599 if(chem != nullptr) 1600 delete chem; 1601 1602 return ret; 1603 } 1604 retreive_basename(const string & base,const string & extension)1605 static string retreive_basename(const string & base, const string & extension) 1606 { 1607 string new_base = base; 1608 S_I index; 1609 1610 if(new_base.size() < 2+1+extension.size()) 1611 throw SRC_BUG; // must be a slice filename 1612 index = new_base.find_last_not_of(string(".")+extension); 1613 new_base = string(new_base.begin(), new_base.begin()+index); 1614 index = new_base.find_last_not_of("0123456789"); 1615 new_base = string(new_base.begin(), new_base.begin()+index); 1616 1617 return new_base; 1618 } 1619 tools_localtime(const time_t & timep,struct tm * result)1620 static void tools_localtime(const time_t & timep, struct tm *result) 1621 { 1622 #if HAVE_LOCALTIME_R 1623 struct tm *ret = localtime_r(&timep, result); 1624 if(ret == nullptr) 1625 { 1626 string err = tools_strerror_r(errno); 1627 throw Erange("tools_localtime", 1628 tools_printf(gettext("Error met while retrieving current time: %S"), &err)); 1629 } 1630 #else 1631 struct tm *ret = localtime(&timep); 1632 if(ret == nullptr) 1633 { 1634 string err = tools_strerror_r(errno); 1635 throw Erange("tools_localtime", 1636 tools_printf(gettext("Error met while retrieving current time: %S"), &err)); 1637 } 1638 1639 *result = *ret; 1640 #endif 1641 } 1642 tools_read_range(const string & s,S_I & min,U_I & max)1643 void tools_read_range(const string & s, S_I & min, U_I & max) 1644 { 1645 string::const_iterator it = s.begin(); 1646 1647 while(it < s.end() && *it != '-') 1648 it++; 1649 1650 try 1651 { 1652 if(it < s.end()) 1653 { 1654 min = tools_str2int(string(s.begin(), it)); 1655 max = tools_str2int(string(++it, s.end())); 1656 } 1657 else 1658 min = max = tools_str2int(s); 1659 } 1660 catch(Erange & e) 1661 { 1662 min = tools_str2signed_int(s); 1663 max = 0; 1664 } 1665 } 1666 1667 tools_printf(const char * format,...)1668 string tools_printf(const char *format, ...) 1669 { 1670 va_list ap; 1671 va_start(ap, format); 1672 string output = ""; 1673 try 1674 { 1675 output = tools_vprintf(format, ap); 1676 } 1677 catch(...) 1678 { 1679 va_end(ap); 1680 throw; 1681 } 1682 va_end(ap); 1683 return output; 1684 } 1685 tools_vprintf(const char * format,va_list ap)1686 string tools_vprintf(const char *format, va_list ap) 1687 { 1688 bool end; 1689 U_32 taille = strlen(format)+1; 1690 char *copie; 1691 string output = ""; 1692 1693 U_I test; 1694 1695 copie = new (nothrow) char[taille]; 1696 if(copie == nullptr) 1697 throw Ememory("tools_printf"); 1698 try 1699 { 1700 char *ptr = copie, *start = copie; 1701 1702 strncpy(copie, format, taille); 1703 copie[taille-1] = '\0'; 1704 1705 do 1706 { 1707 while(*ptr != '%' && *ptr != '\0') 1708 ++ptr; 1709 if(*ptr == '%') 1710 { 1711 *ptr = '\0'; 1712 end = false; 1713 } 1714 else 1715 end = true; 1716 output += start; 1717 if(!end) 1718 { 1719 ++ptr; 1720 switch(*ptr) 1721 { 1722 case '%': 1723 output += "%"; 1724 break; 1725 case 'd': 1726 output += tools_int2str(va_arg(ap, S_I)); 1727 break; 1728 case 'u': 1729 test = va_arg(ap, U_I); 1730 output += deci(test).human(); 1731 break; 1732 case 'x': 1733 test = va_arg(ap, U_I); 1734 output += tools_string_to_hexa(deci(test).human()); 1735 break; 1736 case 's': 1737 output += va_arg(ap, char *); 1738 break; 1739 case 'c': 1740 output += static_cast<char>(va_arg(ap, S_I)); 1741 break; 1742 case 'i': 1743 output += deci(*(va_arg(ap, infinint *))).human(); 1744 break; 1745 case 'S': 1746 output += *(va_arg(ap, string *)); 1747 break; 1748 default: 1749 throw Efeature(tools_printf("%%%c is not implemented in tools_printf format argument", *ptr)); 1750 } 1751 ++ptr; 1752 start = ptr; 1753 } 1754 } 1755 while(!end); 1756 } 1757 catch(...) 1758 { 1759 delete [] copie; 1760 throw; 1761 } 1762 delete [] copie; 1763 1764 return output; 1765 } 1766 tools_unlink_file_mask_regex(user_interaction & dialog,const string & c_chemin,const string & file_mask,bool info_details)1767 void tools_unlink_file_mask_regex(user_interaction & dialog, const string & c_chemin, const string & file_mask, bool info_details) 1768 { 1769 regular_mask my_mask = regular_mask(file_mask, true); 1770 1771 etage dir = etage(dialog, c_chemin.c_str(), datetime(0), datetime(0), false, false); 1772 path chemin = path(c_chemin); 1773 string entry; 1774 1775 while(dir.read(entry)) 1776 if(my_mask.is_covered(entry)) 1777 { 1778 const string c_entry = (chemin + entry).display(); 1779 if(info_details) 1780 dialog.warning(tools_printf(dar_gettext("Removing file %s"), c_entry.c_str())); 1781 if(unlink(c_entry.c_str()) != 0) 1782 { 1783 string tmp = tools_strerror_r(errno); 1784 dialog.warning(tools_printf(dar_gettext("Error removing file %s: %s"), c_entry.c_str(), tmp.c_str())); 1785 } 1786 } 1787 } 1788 tools_do_some_files_match_mask_regex(user_interaction & ui,const string & c_chemin,const string & file_mask)1789 bool tools_do_some_files_match_mask_regex(user_interaction & ui, const string & c_chemin, const string & file_mask) 1790 { 1791 regular_mask my_mask = regular_mask(file_mask, true); 1792 1793 etage dir = etage(ui, c_chemin.c_str(), datetime(0), datetime(0), false, false); 1794 string entry; 1795 bool ret = false; 1796 1797 while(!ret && dir.read(entry)) 1798 if(my_mask.is_covered(entry)) 1799 ret = true; 1800 1801 return ret; 1802 } 1803 tools_avoid_slice_overwriting_regex(user_interaction & dialog,const path & chemin,const string & basename,const string & extension,bool info_details,bool allow_overwriting,bool warn_overwriting,bool dry_run)1804 void tools_avoid_slice_overwriting_regex(user_interaction & dialog, 1805 const path & chemin, 1806 const string & basename, 1807 const string & extension, 1808 bool info_details, 1809 bool allow_overwriting, 1810 bool warn_overwriting, 1811 bool dry_run) 1812 { 1813 const string c_chemin = chemin.display(); 1814 const string file_mask = string("^") + tools_escape_chars_in_string(basename, "[].+|!*?{}()^$-,\\") + "\\.[0-9]+\\." + extension + "(\\.(md5|sha1|sha512))?$"; 1815 if(tools_do_some_files_match_mask_regex(dialog, c_chemin, file_mask)) 1816 { 1817 if(!allow_overwriting) 1818 throw Erange("tools_avoid_slice_overwriting", tools_printf(dar_gettext("Overwriting not allowed while a slice of a previous archive with the same basename has been found in the %s directory, Operation aborted"), c_chemin.c_str())); 1819 else 1820 { 1821 try 1822 { 1823 if(warn_overwriting) 1824 dialog.pause(tools_printf(dar_gettext("At least one slice of an old archive with the same name remains in the directory %s. It is advised to remove all the old archive's slices before creating an archive of same name. Can I remove these old slices?"), c_chemin.c_str())); 1825 if(!dry_run) 1826 tools_unlink_file_mask_regex(dialog, c_chemin, file_mask, info_details); 1827 } 1828 catch(Euser_abort & e) 1829 { 1830 // nothing to do, just continue 1831 } 1832 } 1833 } 1834 } 1835 tools_add_elastic_buffer(generic_file & f,U_32 max_size,U_32 modulo,U_32 offset)1836 void tools_add_elastic_buffer(generic_file & f, 1837 U_32 max_size, 1838 U_32 modulo, 1839 U_32 offset) 1840 { 1841 U_32 size = tools_pseudo_random(max_size-1) + 1; // range from 1 to max_size; 1842 1843 if(modulo > 0) 1844 { 1845 U_32 shift = modulo - (offset % modulo); 1846 size = (size/modulo)*modulo + shift; 1847 } 1848 1849 elastic tic = size; 1850 char *buffer = new (nothrow) char[tic.get_size()]; 1851 1852 if(buffer == nullptr) 1853 throw Ememory("tools_add_elastic_buffer"); 1854 try 1855 { 1856 tic.dump((unsigned char *)buffer, tic.get_size()); 1857 f.write(buffer, tic.get_size()); 1858 } 1859 catch(...) 1860 { 1861 delete [] buffer; 1862 throw; 1863 } 1864 delete [] buffer; 1865 } 1866 tools_are_on_same_filesystem(const string & file1,const string & file2)1867 bool tools_are_on_same_filesystem(const string & file1, const string & file2) 1868 { 1869 dev_t id; 1870 struct stat sstat; 1871 1872 if(stat(file1.c_str(), &sstat) < 0) 1873 { 1874 string tmp = tools_strerror_r(errno); 1875 throw Erange("tools:tools_are_on_same_filesystem", tools_printf(dar_gettext("Cannot get inode information for %s: %s"), file1.c_str(), tmp.c_str())); 1876 } 1877 id = sstat.st_dev; 1878 1879 if(stat(file2.c_str(), &sstat) < 0) 1880 { 1881 string tmp = tools_strerror_r(errno); 1882 throw Erange("tools:tools_are_on_same_filesystem", tools_printf(dar_gettext("Cannot get inode information for %s: %s"), file2.c_str(), tmp.c_str())); 1883 } 1884 1885 return id == sstat.st_dev; 1886 } 1887 tools_relative2absolute_path(const path & src,const path & cwd)1888 path tools_relative2absolute_path(const path & src, const path & cwd) 1889 { 1890 if(src.is_relative()) 1891 if(cwd.is_relative()) 1892 throw Erange("tools_relative2absolute_path", dar_gettext("Current Working Directory cannot be a relative path")); 1893 else 1894 return cwd + src; 1895 else 1896 return src; 1897 } 1898 tools_block_all_signals(sigset_t & old_mask)1899 void tools_block_all_signals(sigset_t &old_mask) 1900 { 1901 sigset_t all; 1902 1903 sigfillset(&all); 1904 #if HAVE_LIBPTHREAD 1905 if(pthread_sigmask(SIG_BLOCK, &all, &old_mask) != 0) 1906 #else 1907 if(sigprocmask(SIG_BLOCK, &all, &old_mask) != 0) 1908 #endif 1909 throw Erange("tools_block_all_signals", string(dar_gettext("Cannot block signals: "))+tools_strerror_r(errno)); 1910 } 1911 tools_set_back_blocked_signals(sigset_t old_mask)1912 void tools_set_back_blocked_signals(sigset_t old_mask) 1913 { 1914 #if HAVE_LIBPTHREAD 1915 if(pthread_sigmask(SIG_SETMASK, &old_mask, nullptr)) 1916 #else 1917 if(sigprocmask(SIG_SETMASK, &old_mask, nullptr)) 1918 #endif 1919 throw Erange("tools_set_back_block_all_signals", string(dar_gettext("Cannot unblock signals: "))+tools_strerror_r(errno)); 1920 } 1921 tools_count_in_string(const string & s,const char a)1922 U_I tools_count_in_string(const string & s, const char a) 1923 { 1924 U_I ret = 0, c, size = s.size(); 1925 1926 for(c = 0; c < size; ++c) 1927 if(s[c] == a) 1928 ++ret; 1929 return ret; 1930 } 1931 tools_get_mtime(user_interaction & dialog,const std::string & s,bool auto_zeroing,bool silent)1932 datetime tools_get_mtime(user_interaction & dialog, 1933 const std::string & s, 1934 bool auto_zeroing, 1935 bool silent) 1936 { 1937 struct stat buf; 1938 1939 if(lstat(s.c_str(), &buf) < 0) 1940 { 1941 string tmp = tools_strerror_r(errno); 1942 throw Erange("tools_get_mtime", tools_printf(dar_gettext("Cannot get last modification date: %s"), tmp.c_str())); 1943 } 1944 1945 #ifdef LIBDAR_MICROSECOND_READ_ACCURACY 1946 tools_check_negative_date(buf.st_mtim.tv_sec, 1947 dialog, 1948 s.c_str(), 1949 "mtime", 1950 auto_zeroing, 1951 silent); 1952 datetime val = datetime(buf.st_mtim.tv_sec, buf.st_mtim.tv_nsec/1000, datetime::tu_microsecond); 1953 if(val.is_null() && !auto_zeroing) // assuming an error avoids getting time that way 1954 val = datetime(buf.st_mtime, 0, datetime::tu_second); 1955 #else 1956 tools_check_negative_date(buf.st_mtime, 1957 dialog, 1958 s.c_str(), 1959 "mtime", 1960 auto_zeroing, 1961 silent); 1962 datetime val = datetime(buf.st_mtime, 0, datetime::tu_second); 1963 #endif 1964 1965 return val; 1966 } 1967 tools_get_size(const std::string & s)1968 infinint tools_get_size(const std::string & s) 1969 { 1970 struct stat buf; 1971 1972 if(lstat(s.c_str(), &buf) < 0) 1973 { 1974 string tmp = tools_strerror_r(errno); 1975 throw Erange("tools_get_size", tools_printf(dar_gettext("Cannot get last modification date: %s"), tmp.c_str())); 1976 } 1977 1978 if(!S_ISREG(buf.st_mode)) 1979 throw Erange("tools_get_size", tools_printf(dar_gettext("Cannot get size of %S: not a plain file"), &s)); 1980 1981 return buf.st_size; 1982 } 1983 1984 tools_get_ctime(const std::string & s)1985 datetime tools_get_ctime(const std::string & s) 1986 { 1987 struct stat buf; 1988 1989 if(lstat(s.c_str(), &buf) < 0) 1990 { 1991 string tmp = tools_strerror_r(errno); 1992 throw Erange("tools_get_mtime", tools_printf(dar_gettext("Cannot get mtime: %s"), tmp.c_str())); 1993 } 1994 1995 #ifdef LIBDAR_MICROSECOND_READ_ACCURACY 1996 datetime ret = datetime(buf.st_ctim.tv_sec, buf.st_ctim.tv_nsec/1000, datetime::tu_microsecond); 1997 if(ret.is_null()) // assuming an error avoids getting time that way 1998 ret = datetime(buf.st_ctime, 0, datetime::tu_second); 1999 #else 2000 datetime ret = datetime(buf.st_ctime, 0, datetime::tu_second); 2001 #endif 2002 return ret; 2003 } 2004 tools_split_in_words(generic_file & f)2005 vector<string> tools_split_in_words(generic_file & f) 2006 { 2007 vector <string> mots; 2008 vector <char> quotes; 2009 string current = ""; 2010 char a; 2011 bool loop = true; 2012 bool escaped = false; 2013 2014 2015 while(loop) 2016 { 2017 if(f.read(&a, 1) != 1) // reached end of file 2018 { 2019 loop = false; 2020 a = ' '; // to close the last word 2021 } 2022 2023 if(escaped) 2024 { 2025 current += a; // added without consideration of quoting of any sort 2026 escaped = false; 2027 continue; // continuing at beginning of the while loop 2028 } 2029 else 2030 { 2031 if(a == '\\') 2032 { 2033 escaped = true; 2034 continue; // continuing at beginning of the while loop 2035 } 2036 } 2037 2038 if(quotes.empty()) // outside a word 2039 switch(a) 2040 { 2041 case ' ': 2042 case '\t': 2043 case '\n': 2044 case '\r': 2045 break; 2046 case '"': 2047 case '\'': 2048 case '`': 2049 quotes.push_back(a); 2050 break; 2051 default: 2052 quotes.push_back(' '); // the quote space means no quote 2053 current += a; // a new argument is starting 2054 break; 2055 } 2056 else // inside a word 2057 switch(a) 2058 { 2059 case '\t': 2060 if(quotes.back() != ' ') 2061 { 2062 // this is the end of the wor(l)d ;-) 2063 // ...once again... 1000, 1999, 2012, and the next ones to come... 2064 break; 2065 } 2066 // no break ! 2067 case '\n': 2068 case '\r': 2069 a = ' '; // replace carriage return inside quoted string by a space 2070 // no break ! 2071 case ' ': 2072 case '"': 2073 case '\'': 2074 case '`': 2075 if(a == quotes.back()) // "a" is an ending quote 2076 { 2077 quotes.pop_back(); 2078 if(quotes.empty()) // reached end of word 2079 { 2080 mots.push_back(current); 2081 current = ""; 2082 } 2083 else 2084 current += a; 2085 } 2086 else // "a" is a nested starting quote 2087 { 2088 if(a != ' ') // quote ' ' does not have ending quote 2089 quotes.push_back(a); 2090 current += a; 2091 } 2092 break; 2093 default: 2094 current += a; 2095 } 2096 } 2097 if(!quotes.empty()) 2098 throw Erange("make_args_from_file", tools_printf(dar_gettext("Parse error: Unmatched `%c'"), quotes.back())); 2099 2100 return mots; 2101 } 2102 2103 tools_split_in_words(const std::string & arg)2104 std::vector<std::string> tools_split_in_words(const std::string & arg) 2105 { 2106 memory_file mem; 2107 2108 mem.write(arg.c_str(), arg.size()); 2109 mem.skip(0); 2110 return tools_split_in_words(mem); 2111 } 2112 2113 tools_find_next_char_out_of_parenthesis(const string & data,const char what,U_32 start,U_32 & found)2114 bool tools_find_next_char_out_of_parenthesis(const string & data, const char what, U_32 start, U_32 & found) 2115 { 2116 U_32 nested_parenth = 0; 2117 2118 while(start < data.size() && (nested_parenth != 0 || data[start] != what)) 2119 { 2120 if(data[start] == '(') 2121 nested_parenth++; 2122 if(data[start] == ')' && nested_parenth > 0) 2123 nested_parenth--; 2124 start++; 2125 } 2126 2127 if(start < data.size() && nested_parenth == 0 && data[start] == what) 2128 { 2129 found = start; 2130 return true; 2131 } 2132 else 2133 return false; 2134 } 2135 tools_substitute(const string & hook,const map<char,string> & corres)2136 string tools_substitute(const string & hook, 2137 const map<char, string> & corres) 2138 { 2139 string ret = ""; 2140 string::iterator it = const_cast<string &>(hook).begin(); 2141 2142 while(it != hook.end()) 2143 { 2144 if(*it == '%') 2145 { 2146 it++; 2147 if(it != hook.end()) 2148 { 2149 map<char, string>::const_iterator mptr = corres.find(*it); 2150 if(mptr == corres.end()) 2151 throw Escript("tools_substitute", string(dar_gettext("Unknown substitution string: %")) + *it); 2152 else 2153 ret += mptr->second; 2154 it++; 2155 } 2156 else // reached end of "hook" string 2157 { 2158 throw Escript("tools_hook_substitute", dar_gettext("last char of user command-line to execute is '%', (use '%%' instead to avoid this message)")); 2159 } 2160 } 2161 else 2162 { 2163 ret += *it; 2164 it++; 2165 } 2166 } 2167 2168 return ret; 2169 } 2170 tools_hook_substitute(const string & hook,const string & path,const string & basename,const string & num,const string & padded_num,const string & ext,const string & context)2171 string tools_hook_substitute(const string & hook, 2172 const string & path, 2173 const string & basename, 2174 const string & num, 2175 const string & padded_num, 2176 const string & ext, 2177 const string & context) 2178 { 2179 map<char, string> corres; 2180 2181 corres['%'] = "%"; 2182 corres['p'] = path; 2183 corres['b'] = basename; 2184 corres['n'] = num; 2185 corres['N'] = padded_num; 2186 corres['e'] = ext; 2187 corres['c'] = context; 2188 2189 return tools_substitute(hook, corres); 2190 } 2191 2192 tools_hook_execute(user_interaction & ui,const string & cmd_line)2193 void tools_hook_execute(user_interaction & ui, 2194 const string & cmd_line) 2195 { 2196 NLS_SWAP_IN; 2197 try 2198 { 2199 const char *ptr = cmd_line.c_str(); 2200 bool loop = false; 2201 do 2202 { 2203 try 2204 { 2205 S_I code = system(ptr); 2206 switch(code) 2207 { 2208 case 0: 2209 loop = false; 2210 break; // All is fine, script did not report error 2211 case 127: 2212 throw Erange("tools_hook_execute", gettext("execve() failed. (process table is full ?)")); 2213 case -1: 2214 throw Erange("tools_hook_execute", string(gettext("system() call failed: ")) + tools_strerror_r(errno)); 2215 default: 2216 throw Erange("tools_hook_execute", tools_printf(gettext("execution of [ %S ] returned error code: %d"), &cmd_line, code)); 2217 } 2218 } 2219 catch(Erange & e) 2220 { 2221 try 2222 { 2223 ui.pause(string(gettext("Error during user command line execution: ")) + e.get_message() + gettext(" . Retry command-line ?")); 2224 loop = true; 2225 } 2226 catch(Euser_abort & f) 2227 { 2228 ui.pause(gettext("Ignore previous error on user command line and continue ?")); 2229 loop = false; 2230 } 2231 } 2232 } 2233 while(loop); 2234 } 2235 catch(...) 2236 { 2237 NLS_SWAP_OUT; 2238 throw; 2239 } 2240 NLS_SWAP_OUT; 2241 } 2242 tools_hook_substitute_and_execute(user_interaction & ui,const string & hook,const string & path,const string & basename,const string & num,const string & padded_num,const string & ext,const string & context)2243 extern void tools_hook_substitute_and_execute(user_interaction & ui, 2244 const string & hook, 2245 const string & path, 2246 const string & basename, 2247 const string & num, 2248 const string & padded_num, 2249 const string & ext, 2250 const string & context) 2251 { 2252 string cmd_line; 2253 2254 cmd_line = tools_hook_substitute(hook, 2255 path, 2256 basename, 2257 num, 2258 padded_num, 2259 ext, 2260 context); 2261 2262 try 2263 { 2264 tools_hook_execute(ui, cmd_line); 2265 } 2266 catch(Euser_abort & g) 2267 { 2268 throw Escript("sar::hook_execute", string(dar_gettext("Fatal error on user command line: ")) + g.get_message()); 2269 } 2270 } 2271 2272 2273 /*************************************************************/ 2274 2275 2276 tools_build_regex_for_exclude_mask(const string & prefix,const string & relative_part)2277 string tools_build_regex_for_exclude_mask(const string & prefix, 2278 const string & relative_part) 2279 { 2280 string result = "^"; 2281 string::const_iterator it = prefix.begin(); 2282 2283 // prepending any non alpha numeric char of the root by a anti-slash 2284 2285 for( ; it != prefix.end() ; ++it) 2286 { 2287 if(isalnum(*it) || *it == '/' || *it == ' ') 2288 result += *it; 2289 else 2290 { 2291 result += '\\'; 2292 result += *it; 2293 } 2294 } 2295 2296 // adding a trailing / if necessary 2297 2298 string::reverse_iterator tr = result.rbegin(); 2299 if(tr == result.rend() || *tr != '/') 2300 result += '/'; 2301 2302 // adapting and adding the relative_part 2303 2304 it = relative_part.begin(); 2305 2306 if(it != relative_part.end() && *it == '^') 2307 it++; // skipping leading ^ 2308 else 2309 result += ".*"; // prepending wilde card sub-expression 2310 2311 for( ; it != relative_part.end() && *it != '$' ; ++it) 2312 result += *it; 2313 2314 result += "(/.+)?$"; 2315 2316 return result; 2317 } 2318 tools_output2xml(const string & src)2319 string tools_output2xml(const string & src) 2320 { 2321 string ret = ""; 2322 U_I cur = 0, size = src.size(); 2323 2324 while(cur < size) 2325 { 2326 switch(src[cur]) 2327 { 2328 case '<': 2329 ret += "<"; 2330 break; 2331 case '>': 2332 ret += ">"; 2333 break; 2334 case '&': 2335 ret += "&"; 2336 break; 2337 case '\'': 2338 ret += "'"; 2339 break; 2340 case'\"': 2341 ret += """; 2342 break; 2343 default: 2344 ret += src[cur]; 2345 } 2346 ++cur; 2347 } 2348 2349 return ret; 2350 } 2351 2352 U_I tools_octal2int(const std::string & perm) 2353 { 2354 U_I len = perm.size(); 2355 U_I ret = 0; 2356 enum { init , octal , trail, error } etat = init; 2357 2358 if(perm == "") 2359 return 0666; // permission used by default (compatible with dar's previous behavior) 2360 2361 for(U_I i = 0; i < len ; i++) 2362 switch(etat) 2363 { 2364 case init: 2365 switch(perm[i]) 2366 { 2367 case ' ': 2368 case '\t': 2369 case '\n': 2370 case '\r': 2371 break; 2372 case '0': 2373 etat = octal; 2374 break; 2375 default: 2376 etat = error; 2377 break; 2378 } 2379 break; 2380 case octal: 2381 if(perm[i] == ' ') 2382 etat = trail; 2383 else 2384 if(perm[i] >= '0' && perm[i] <= '7') 2385 ret = ret*8 + perm[i] - '0'; 2386 else 2387 etat = error; 2388 break; 2389 case trail: 2390 if(perm[i] != ' ') 2391 etat = error; 2392 break; 2393 case error: 2394 throw Erange("tools_octal2int", dar_gettext("Badly formated octal number")); 2395 default: 2396 throw SRC_BUG; 2397 } 2398 2399 if(etat == error || etat == init) 2400 throw Erange("tools_octal2int", dar_gettext("Badly formated octal number")); 2401 2402 return ret; 2403 } 2404 2405 string tools_int2octal(const U_I & perm) 2406 { 2407 vector<U_I> digits = tools_number_base_decomposition_in_big_endian(perm, (U_I)8); 2408 vector<U_I>::iterator it = digits.begin(); 2409 string ret = ""; 2410 2411 while(it != digits.end()) 2412 { 2413 string tmp; 2414 tmp += '0' + (*it); 2415 ret = tmp + ret; 2416 ++it; 2417 } 2418 2419 return string("0") + ret; // leading zero for octal format indication 2420 } 2421 2422 string tools_get_permission_string(char type, U_32 perm, bool hard) 2423 { 2424 string ret = hard ? "*" : " "; 2425 2426 if(type == 'f') // plain files 2427 type = '-'; 2428 if(type == 'o') // door "files" 2429 type = 'D'; 2430 ret += type; 2431 2432 if((perm & 0400) != 0) 2433 ret += 'r'; 2434 else 2435 ret += '-'; 2436 if((perm & 0200) != 0) 2437 ret += 'w'; 2438 else 2439 ret += '-'; 2440 if((perm & 0100) != 0) 2441 if((perm & 04000) != 0) 2442 ret += 's'; 2443 else 2444 ret += 'x'; 2445 else 2446 if((perm & 04000) != 0) 2447 ret += 'S'; 2448 else 2449 ret += '-'; 2450 if((perm & 040) != 0) 2451 ret += 'r'; 2452 else 2453 ret += '-'; 2454 if((perm & 020) != 0) 2455 ret += 'w'; 2456 else 2457 ret += '-'; 2458 if((perm & 010) != 0) 2459 if((perm & 02000) != 0) 2460 ret += 's'; 2461 else 2462 ret += 'x'; 2463 else 2464 if((perm & 02000) != 0) 2465 ret += 'S'; 2466 else 2467 ret += '-'; 2468 if((perm & 04) != 0) 2469 ret += 'r'; 2470 else 2471 ret += '-'; 2472 if((perm & 02) != 0) 2473 ret += 'w'; 2474 else 2475 ret += '-'; 2476 if((perm & 01) != 0) 2477 if((perm & 01000) != 0) 2478 ret += 't'; 2479 else 2480 ret += 'x'; 2481 else 2482 if((perm & 01000) != 0) 2483 ret += 'T'; 2484 else 2485 ret += '-'; 2486 2487 return ret; 2488 } 2489 2490 2491 U_I tools_get_permission(S_I fd) 2492 { 2493 struct stat buf; 2494 int err = fstat(fd, &buf); 2495 2496 if(err < 0) 2497 throw Erange("tools_get_permission", string(gettext("Cannot get effective permission given a file descriptor: ")) + tools_strerror_r(errno)); 2498 2499 return buf.st_mode & ~(S_IFMT); 2500 } 2501 2502 2503 void tools_set_permission(S_I fd, U_I perm) 2504 { 2505 NLS_SWAP_IN; 2506 try 2507 { 2508 if(fd < 0) 2509 throw SRC_BUG; 2510 if(fchmod(fd, (mode_t) perm) < 0) 2511 { 2512 string tmp = tools_strerror_r(errno); 2513 throw Erange("tools_set_permission", tools_printf(gettext("Error while setting file permission: %s"), tmp.c_str())); 2514 } 2515 } 2516 catch(...) 2517 { 2518 NLS_SWAP_OUT; 2519 throw; 2520 } 2521 NLS_SWAP_OUT; 2522 } 2523 2524 uid_t tools_ownership2uid(const string & user) 2525 { 2526 uid_t ret = -1; 2527 2528 NLS_SWAP_IN; 2529 try 2530 { 2531 bool direct_uid_set = false; 2532 2533 if(user.empty()) 2534 throw Erange("tools_ownership2uid", gettext("An empty string is not a valid user name")); 2535 2536 try 2537 { 2538 ret = tools_str2int(user); 2539 direct_uid_set = true; 2540 } 2541 catch(Erange & e) 2542 { 2543 // the given user is not an uid 2544 } 2545 2546 if(!direct_uid_set) 2547 { 2548 #ifdef __DYNAMIC__ 2549 const char *c_user = user.c_str(); 2550 #if HAVE_GETPWNAM_R 2551 struct passwd puser; 2552 struct passwd *result; 2553 S_I size = sysconf(_SC_GETPW_R_SIZE_MAX); 2554 char *buf = nullptr; 2555 if(size == -1) 2556 size = 16384; 2557 try 2558 { 2559 buf = new (nothrow) char[size]; 2560 if(buf == nullptr) 2561 throw Ememory("tools_ownership2uid"); 2562 2563 int val = getpwnam_r(c_user, 2564 &puser, 2565 buf, 2566 size, 2567 &result); 2568 2569 if(val != 0 2570 || result == nullptr) 2571 { 2572 string err = val == 0 ? gettext("Unknown user") : tools_strerror_r(errno); 2573 throw Erange("tools_ownership2uid", 2574 tools_printf(gettext("Error found while looking for UID of user %s: %S"), 2575 c_user, 2576 &err)); 2577 } 2578 2579 ret = result->pw_uid; 2580 } 2581 catch(...) 2582 { 2583 if(buf != nullptr) 2584 delete [] buf; 2585 throw; 2586 } 2587 if(buf != nullptr) 2588 delete [] buf; 2589 #else 2590 errno = 0; 2591 struct passwd *puser = getpwnam(c_user); 2592 if(puser == nullptr) 2593 { 2594 string err = (errno == 0) ? gettext("Unknown user") : tools_strerror_r(errno); 2595 throw Erange("tools_ownership2uid", 2596 tools_printf(gettext("Error found while looking for UID of user %s: %S"), 2597 c_user, 2598 &err)); 2599 } 2600 2601 ret = puser->pw_uid; 2602 #endif 2603 #else 2604 throw Erange("tools_ownership2uid", dar_gettext("Cannot convert username to uid in statically linked binary, either directly provide the UID or run libdar from a dynamically linked executable")); 2605 #endif 2606 } 2607 } 2608 catch(...) 2609 { 2610 NLS_SWAP_OUT; 2611 throw; 2612 } 2613 NLS_SWAP_OUT; 2614 2615 return ret; 2616 } 2617 2618 2619 gid_t tools_ownership2gid(const string & group) 2620 { 2621 gid_t ret = -1; 2622 2623 NLS_SWAP_IN; 2624 try 2625 { 2626 bool direct_gid_set = false; 2627 2628 if(group.empty()) 2629 throw Erange("tools_ownership2gid", gettext("An empty string is not a valid group name")); 2630 2631 try 2632 { 2633 ret = tools_str2int(group); 2634 direct_gid_set = true; 2635 } 2636 catch(Erange & e) 2637 { 2638 // the given group is not an gid 2639 } 2640 2641 if(!direct_gid_set) 2642 { 2643 #ifdef __DYNAMIC__ 2644 const char *c_group = group.c_str(); 2645 #if HAVE_GETGRNAM_R 2646 struct group pgroup; 2647 struct group *result; 2648 U_I size = sysconf(_SC_GETGR_R_SIZE_MAX); 2649 char *buf = nullptr; 2650 try 2651 { 2652 buf = new (nothrow) char[size]; 2653 if(buf == nullptr) 2654 throw Ememory("tools_ownsership2gid"); 2655 2656 S_I val = getgrnam_r(c_group, 2657 &pgroup, 2658 buf, 2659 size, 2660 &result); 2661 2662 if(val != 0 2663 || result == nullptr) 2664 { 2665 string err = (val == 0) ? gettext("Unknown group") : tools_strerror_r(errno); 2666 throw Erange("tools_ownership2gid", 2667 tools_printf(gettext("Error found while looking fo GID of group %s: %S"), 2668 c_group, 2669 &err)); 2670 } 2671 2672 ret = result->gr_gid; 2673 2674 } 2675 catch(...) 2676 { 2677 if(buf != nullptr) 2678 delete [] buf; 2679 throw; 2680 } 2681 if(buf != nullptr) 2682 delete [] buf; 2683 #else 2684 errno = 0; 2685 struct group *pgroup = getgrnam(c_group); 2686 if(pgroup == nullptr) 2687 { 2688 string err = (errno == 0) ? gettext("Unknown group") : tools_strerror_r(errno); 2689 throw Erange("tools_ownership2gid", 2690 tools_printf(gettext("Error found while looking for GID of group %s: %S"), 2691 c_group, 2692 &err)); 2693 } 2694 2695 ret = pgroup->gr_gid; 2696 #endif 2697 #else 2698 throw Erange("tools_ownership2gid", dar_gettext("Cannot convert username to uid in statically linked binary, either directly provide the UID or run libdar from a dynamically linked executable")); 2699 #endif 2700 } 2701 } 2702 catch(...) 2703 { 2704 NLS_SWAP_OUT; 2705 throw; 2706 } 2707 NLS_SWAP_OUT; 2708 2709 return ret; 2710 } 2711 2712 void tools_set_ownership(int filedesc, const std::string & user, const std::string & group) 2713 { 2714 uid_t uid = -1; 2715 uid_t gid = -1; 2716 2717 if(user != "") 2718 uid = tools_ownership2uid(user); 2719 if(group != "") 2720 gid = tools_ownership2gid(group); 2721 2722 if(uid != (uid_t)(-1) || gid != (gid_t)(-1)) 2723 { 2724 if(fchown(filedesc, uid, gid) < 0) 2725 { 2726 string tmp = tools_strerror_r(errno); 2727 throw Erange("tools_set_ownership", tools_printf(gettext("Error while setting file user ownership: %s"), tmp.c_str())); 2728 } 2729 } 2730 } 2731 2732 void tools_memxor(void *dest, const void *src, U_I n) 2733 { 2734 unsigned char *d = (unsigned char *) dest; 2735 const unsigned char *s = (const unsigned char *) src; 2736 2737 for(U_I i = 0; i < n; i++) 2738 *d++ ^= *s++; 2739 } 2740 2741 tlv_list tools_string2tlv_list(user_interaction & dialog, const U_16 & type, const vector<string> & data) 2742 { 2743 vector<string>::const_iterator it = data.begin(); 2744 tlv tmp; 2745 tlv_list ret; 2746 2747 tmp.set_type(type); 2748 while(it != data.end()) 2749 { 2750 tmp.reset(); 2751 tmp.write(it->c_str(), it->size()); 2752 ret.add(tmp); 2753 it++; 2754 } 2755 return ret; 2756 } 2757 2758 U_I tools_pseudo_random(U_I max) 2759 { 2760 return (U_I)(max*((float)(rand())/RAND_MAX)); 2761 } 2762 2763 void tools_read_from_pipe(user_interaction & dialog, S_I fd, tlv_list & result) 2764 { 2765 tuyau tube = tuyau(dialog, fd); 2766 result.read(tube); 2767 } 2768 2769 string tools_unsigned_char_to_hexa(unsigned char x) 2770 { 2771 string ret; 2772 vector<U_I> digit = tools_number_base_decomposition_in_big_endian(x, (U_I)(16)); 2773 vector<U_I>::reverse_iterator itr = digit.rbegin(); 2774 2775 switch(digit.size()) 2776 { 2777 case 0: 2778 ret = "00"; 2779 break; 2780 case 1: 2781 ret = "0"; 2782 break; 2783 case 2: 2784 break; 2785 default: 2786 throw SRC_BUG; 2787 } 2788 2789 while(itr != digit.rend()) 2790 { 2791 U_I t = *itr; 2792 if(t > 9) 2793 ret += ('a' + (t - 10)); 2794 else 2795 ret += ('0' + t); 2796 2797 ++itr; 2798 } 2799 2800 return ret; 2801 } 2802 2803 string tools_string_to_hexa(const string & input) 2804 { 2805 string::const_iterator it = input.begin(); 2806 string ret; 2807 2808 while(it != input.end()) 2809 { 2810 ret += tools_unsigned_char_to_hexa((unsigned char)(*it)); 2811 ++it; 2812 } 2813 2814 return ret; 2815 } 2816 2817 infinint tools_file_size_to_crc_size(const infinint & size) 2818 { 2819 const infinint ratio = tools_get_extended_size("1G",1024); 2820 infinint r; 2821 infinint crc_size; 2822 2823 if(!size.is_zero()) 2824 { 2825 euclide(size, ratio, crc_size, r); 2826 if(!r.is_zero()) 2827 ++crc_size; 2828 crc_size *= 4; // smallest value is 4 bytes, 4 bytes more per each additional 1 Gbyte of data 2829 } 2830 else 2831 crc_size = 1; // minimal value for no data to protect by checksum 2832 2833 return crc_size; 2834 } 2835 2836 string tools_get_euid() 2837 { 2838 string ret; 2839 uid_t uid = geteuid(); 2840 deci conv = infinint(uid); 2841 2842 ret += tools_name_of_uid(uid) + "("+ conv.human() + ")"; 2843 2844 return ret; 2845 } 2846 2847 string tools_get_egid() 2848 { 2849 string ret; 2850 uid_t gid = getegid(); 2851 deci conv = infinint(gid); 2852 2853 ret += tools_name_of_gid(gid) + "("+ conv.human() + ")"; 2854 2855 return ret; 2856 } 2857 2858 string tools_get_hostname() 2859 { 2860 string ret; 2861 struct utsname uts; 2862 2863 if(uname(&uts) < 0) 2864 throw Erange("tools_get_hostname", string(dar_gettext("Error while fetching hostname: ")) + tools_strerror_r(errno)); 2865 2866 ret = string(uts.nodename); 2867 2868 return ret; 2869 } 2870 2871 string tools_get_date_utc() 2872 { 2873 string ret; 2874 datetime now = datetime(::time(nullptr), 0, datetime::tu_second); 2875 2876 ret = tools_display_date(now); 2877 2878 return ret; 2879 } 2880 2881 string tools_get_compression_ratio(const infinint & storage_size, const infinint & file_size, bool compressed) 2882 { 2883 static const char * not_compressed = " "; 2884 2885 if(!compressed) 2886 return not_compressed; 2887 else 2888 if(file_size >= storage_size) 2889 if(!file_size.is_zero()) 2890 return tools_addspacebefore(deci(((file_size - storage_size)*100)/file_size).human(), 4) +"%"; 2891 else 2892 return not_compressed; 2893 else 2894 return gettext("Worse"); 2895 } 2896 2897 #define MSGSIZE 200 2898 2899 string tools_strerror_r(int errnum) 2900 { 2901 char buffer[MSGSIZE]; 2902 string ret; 2903 2904 #ifdef HAVE_STRERROR_R 2905 #ifdef HAVE_STRERROR_R_CHAR_PTR 2906 char *val = strerror_r(errnum, buffer, MSGSIZE); 2907 if(val != buffer) 2908 strncpy(buffer, val, MSGSIZE); 2909 #else 2910 // we expect the XSI-compliant strerror_r 2911 int val = strerror_r(errnum, buffer, MSGSIZE); 2912 if(val != 0) 2913 { 2914 string tmp = tools_printf(gettext("Error code %d to message conversion failed"), errnum); 2915 strncpy(buffer, tmp.c_str(), tools_min((size_t)(tmp.size()+1), (size_t)(MSGSIZE))); 2916 } 2917 #endif 2918 #else 2919 char *tmp = strerror(errnum); 2920 (void)strncpy(buffer, tmp, MSGSIZE); 2921 #endif 2922 buffer[MSGSIZE-1] = '\0'; 2923 ret = buffer; 2924 2925 return ret; 2926 } 2927 2928 #ifdef GPGME_SUPPORT 2929 string tools_gpgme_strerror_r(gpgme_error_t err) 2930 { 2931 char buffer[MSGSIZE]; 2932 string ret; 2933 2934 switch(gpgme_strerror_r(err, buffer, MSGSIZE)) 2935 { 2936 case 0: 2937 break; 2938 case ERANGE: 2939 strncpy(buffer, "Lack of memory to display gpgme error message", MSGSIZE); 2940 break; 2941 default: 2942 throw SRC_BUG; 2943 } 2944 buffer[MSGSIZE-1] = '\0'; 2945 ret = buffer; 2946 2947 return ret; 2948 } 2949 #endif 2950 2951 2952 #if HAVE_WCHAR_H 2953 wstring tools_string_to_wstring(const string & val) 2954 { 2955 wstring ret; 2956 wchar_t *dst = new (nothrow) wchar_t[val.size() + 1]; 2957 2958 if(dst == nullptr) 2959 throw Ememory("tools_string_to_wcs"); 2960 try 2961 { 2962 mbstate_t state_wc; 2963 const char *src = val.c_str(); 2964 size_t len; 2965 2966 memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure 2967 len = mbsrtowcs(dst, &src, val.size(), &state_wc); 2968 if(len == (size_t)-1) 2969 throw Erange("tools_string_to_wcs", string(gettext("Invalid wide-char found in string: ")) + tools_strerror_r(errno)); 2970 dst[len] = '\0'; 2971 2972 // converting dst to wstring 2973 2974 ret = dst; 2975 } 2976 catch(...) 2977 { 2978 if(dst != nullptr) 2979 delete [] dst; 2980 throw; 2981 } 2982 if(dst != nullptr) 2983 delete [] dst; 2984 2985 return ret; 2986 } 2987 2988 string tools_wstring_to_string(const wstring & val) 2989 { 2990 string ret; 2991 const wchar_t *src = val.c_str(); 2992 mbstate_t state_wc; 2993 size_t len; 2994 2995 memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure 2996 len = wcsrtombs(nullptr, &src, 0, &state_wc); 2997 if(len == (size_t)-1) 2998 throw SRC_BUG; // all components of wstring should be valid 2999 3000 char *dst = new (nothrow) char[len + 1]; 3001 if(dst == nullptr) 3002 throw Ememory("tools_wstring_to_string"); 3003 try 3004 { 3005 size_t len2; 3006 memset(&state_wc, '\0', sizeof(state_wc)); // initializing the shift structure 3007 src = val.c_str(); 3008 len2 = wcsrtombs(dst, &src, len, &state_wc); 3009 if(len != len2) 3010 throw SRC_BUG; 3011 if(len2 == (size_t)-1) 3012 throw SRC_BUG; // problem should have already raised above 3013 dst[len2] = '\0'; 3014 3015 // converting dst to string 3016 3017 ret = dst; 3018 } 3019 catch(...) 3020 { 3021 if(dst != nullptr) 3022 delete [] dst; 3023 throw; 3024 } 3025 if(dst != nullptr) 3026 delete [] dst; 3027 3028 return ret; 3029 } 3030 #endif 3031 3032 void tools_merge_to_vector(vector<string> & a, const vector<string> & b) 3033 { 3034 vector<string>::const_iterator ptrb = b.begin(); 3035 3036 while(ptrb != b.end()) 3037 { 3038 vector<string>::const_iterator ptra = a.begin(); 3039 3040 while(ptra != a.end() && *ptra != *ptrb) 3041 ++ptra; 3042 3043 if(ptra == a.end()) 3044 a.push_back(*ptrb); 3045 3046 ++ptrb; 3047 } 3048 } 3049 3050 vector<string> tools_substract_from_vector(const vector<string> & a, const vector<string> & b) 3051 { 3052 vector<string> ret; 3053 vector<string>::const_iterator ptra = a.begin(); 3054 3055 while(ptra != a.end()) 3056 { 3057 vector<string>::const_iterator ptrb = b.begin(); 3058 3059 while(ptrb != b.end() && *ptra != *ptrb) 3060 ++ptrb; 3061 3062 if(ptrb == b.end()) 3063 ret.push_back(*ptra); 3064 ++ptra; 3065 } 3066 3067 return ret; 3068 } 3069 3070 3071 struct dirent *tools_allocate_struct_dirent(const std::string & path_name, U_64 & max_name_length, memory_pool *pool) 3072 { 3073 struct dirent *ret; 3074 S_64 name_max = pathconf(path_name.c_str(), _PC_NAME_MAX); 3075 U_I len; 3076 3077 if(name_max == -1) 3078 name_max = NAME_MAX; 3079 if(name_max < NAME_MAX) 3080 name_max = NAME_MAX; 3081 len = offsetof(struct dirent, d_name) + name_max + 1; 3082 if(pool == nullptr) 3083 ret = (struct dirent *) new (nothrow) char[len]; 3084 else 3085 ret = (struct dirent *) new (pool) char[len]; 3086 3087 if(ret == nullptr) 3088 throw Ememory("tools_allocate_struc_dirent"); 3089 memset(ret, '\0', len); 3090 #ifdef _DIRENT_HAVE_D_RECLEN 3091 ret->d_reclen = (len / sizeof(long))*sizeof(long); 3092 #endif 3093 max_name_length = name_max; 3094 3095 return ret; 3096 } 3097 3098 void tools_release_struct_dirent(struct dirent *ptr) 3099 { 3100 if(ptr != nullptr) 3101 delete [] ((char *)(ptr)); 3102 } 3103 3104 void tools_secu_string_show(user_interaction & dialog, const string & msg, const secu_string & key) 3105 { 3106 string res = msg + tools_printf(" (size=%d) [", key.get_size()); 3107 U_I max = key.get_size() - 1; 3108 3109 for(U_I index = 0; index < max; ++index) 3110 res += tools_printf(" %d |", key[index]); 3111 3112 res += tools_printf(" %d ]", key[max]); 3113 dialog.warning(res); 3114 } 3115 3116 string tools_escape_chars_in_string(const string & val, const char *to_escape) 3117 { 3118 string ret; 3119 string::const_iterator it = val.begin(); 3120 3121 while(it != val.end()) 3122 { 3123 U_I curs = 0; 3124 while(to_escape[curs] != '\0' && to_escape[curs] != *it) 3125 ++curs; 3126 if(to_escape[curs] != '\0') 3127 ret += "\\"; 3128 ret += *it; 3129 ++it; 3130 } 3131 3132 return ret; 3133 } 3134 3135 bool tools_infinint2U_64(infinint val, U_64 & res) 3136 { 3137 res = 0; 3138 val.unstack(res); 3139 return val.is_zero(); 3140 } 3141 3142 } // end of namespace 3143