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 += "&lt;";
2330                 break;
2331             case '>':
2332                 ret += "&gt;";
2333                 break;
2334             case '&':
2335                 ret += "&amp;";
2336                 break;
2337             case '\'':
2338                 ret += "&apos;";
2339                 break;
2340             case'\"':
2341                 ret += "&quot;";
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