• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2021 Red Hat, Inc.
5 
6 ///@file
7 
8 // In case we have a bad fts we include this before config.h because
9 // it can't handle _FILE_OFFSET_BITS.  Everything we need here is fine
10 // if its declarations just come first.  Also, include sys/types.h
11 // before fts. On some systems fts.h is not self contained.
12 #ifdef BAD_FTS
13   #include <sys/types.h>
14   #include <fts.h>
15 #endif
16 
17 // For package configuration macros.
18 #include "config.h"
19 
20 // In case we don't have a bad fts then we need to include fts.h after
21 // config.h.
22 #ifndef BAD_FTS
23   #include <sys/types.h>
24   #include <fts.h>
25 #endif
26 
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <dirent.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <libgen.h>
36 
37 #include <algorithm>
38 #include <cstdlib>
39 #include <cstring>
40 #include <fstream>
41 #include <iostream>
42 #include <iterator>
43 #include <memory>
44 #include <sstream>
45 
46 #include "abg-dwarf-reader.h"
47 #ifdef WITH_CTF
48 #include "abg-ctf-reader.h"
49 #endif
50 #include "abg-internal.h"
51 #include "abg-regex.h"
52 
53 // <headers defining libabigail's API go under here>
54 ABG_BEGIN_EXPORT_DECLARATIONS
55 
56 #include <abg-ir.h>
57 #include "abg-config.h"
58 #include "abg-tools-utils.h"
59 
60 ABG_END_EXPORT_DECLARATIONS
61 // </headers defining libabigail's API>
62 
63 using std::string;
64 
65 namespace abigail
66 {
67 
68 using namespace abigail::suppr;
69 using namespace abigail::ini;
70 
71 /// @brief Namespace for a set of utility function used by tools based
72 /// on libabigail.
73 namespace tools_utils
74 {
75 
76 /// Get the value of $libdir variable of the autotools build
77 /// system.  This is where shared libraries are usually installed.
78 ///
79 /// @return a constant string (doesn't have to be free-ed by the
80 /// caller) that represent the value of the $libdir variable in the
81 /// autotools build system, or NULL if it's not set.
82 const char*
get_system_libdir()83 get_system_libdir()
84 {
85 #ifndef ABIGAIL_ROOT_SYSTEM_LIBDIR
86 #error the macro ABIGAIL_ROOT_SYSTEM_LIBDIR must be set at compile time
87 #endif
88 
89   static __thread const char* system_libdir(ABIGAIL_ROOT_SYSTEM_LIBDIR);
90   return system_libdir;
91 }
92 
93 /// The bitwise 'OR' operator for abidiff_status bit masks.
94 ///
95 /// @param l the left hand side operand of the OR operator.
96 ///
97 /// @param r the right hand side operand of the OR operator.
98 ///
99 /// @return the result of the OR expression.
100 abidiff_status
operator |(abidiff_status l,abidiff_status r)101 operator|(abidiff_status l, abidiff_status r)
102 {return static_cast<abidiff_status>(static_cast<unsigned>(l)
103 				     | static_cast<unsigned>(r));}
104 
105 /// The bitwise 'AND' operator for abidiff_status bit masks.
106 ///
107 /// @param l the left hand side operand of the AND operator.
108 ///
109 /// @param r the right hand side operand of the AND operator.
110 ///
111 /// @return the result of the AND expression.
112 abidiff_status
operator &(abidiff_status l,abidiff_status r)113 operator&(abidiff_status l, abidiff_status r)
114 {return static_cast<abidiff_status>(static_cast<unsigned>(l)
115 				     & static_cast<unsigned>(r));}
116 
117 /// The |= operator.
118 ///
119 /// @param l the left hand side operand of the operator.
120 ///
121 /// @param r the right hand side operand of the operator.
122 ///
123 /// @param the resulting bit mask.
124 abidiff_status&
operator |=(abidiff_status & l,abidiff_status r)125 operator|=(abidiff_status&l, abidiff_status r)
126 {
127   l = static_cast<abidiff_status>(static_cast<unsigned>(l)
128 				  | static_cast<unsigned>(r));
129   return l;
130 }
131 
132 /// Test if an instance of @param abidiff_status bits mask represents
133 /// an error.
134 ///
135 /// This functions tests if the @ref ABIDIFF_ERROR bit is set in the
136 /// given bits mask.
137 ///
138 /// @param s the bit mask to consider.
139 ///
140 /// @return true iff @p s has its ABIDIFF_ERROR bit set.
141 bool
abidiff_status_has_error(abidiff_status s)142 abidiff_status_has_error(abidiff_status s)
143 {return s & (ABIDIFF_ERROR | ABIDIFF_USAGE_ERROR);}
144 
145 /// Test if an instance of @param abidiff_status bits mask represents
146 /// an abi change.
147 ///
148 /// This functions tests if the @ref ABIDIFF_ABI_CHANGE bit is set in the
149 /// given bits mask.
150 ///
151 /// @param s the bit mask to consider.
152 ///
153 /// @return true iff @p s has its @ref ABIDIFF_ABI_CHANGE bit set.
154 bool
abidiff_status_has_abi_change(abidiff_status s)155 abidiff_status_has_abi_change(abidiff_status s)
156 {return s & ABIDIFF_ABI_CHANGE;}
157 
158 /// Test if an instance of @param abidiff_status bits mask represents
159 /// an incompatible abi change.
160 ///
161 /// This functions tests if the @ref ABIDIFF_INCOMPATIBLE_ABI_CHANGE
162 /// bit is set in the given bits mask.  Note that the this bit is set
163 /// then the bit @ref ABIDIFF_ABI_CHANGE must be set as well.
164 ///
165 /// @param s the bit mask to consider.
166 ///
167 /// @return true iff @p s has its @ref ABIDIFF_INCOMPATIBLE ABI_CHANGE
168 /// set.
169 bool
abidiff_status_has_incompatible_abi_change(abidiff_status s)170 abidiff_status_has_incompatible_abi_change(abidiff_status s)
171 {return s & ABIDIFF_ABI_INCOMPATIBLE_CHANGE;}
172 
173 #define DECLARE_STAT(st) \
174   struct stat st; \
175   memset(&st, 0, sizeof(st))
176 
177 // <class timer stuff>
178 
179 /// The private data type of the @ref timer class.
180 struct timer::priv
181 {
182   timer::kind timer_kind;
183   struct timeval begin_timeval;
184   struct timeval end_timeval;
185 
privabigail::tools_utils::timer::priv186   priv(timer::kind k)
187     : timer_kind(k),
188       begin_timeval(),
189       end_timeval()
190   {}
191 }; // end struct timer::priv
192 
193 /// Constructor of the @ref timer type.
194 ///
195 /// @param k the kind of timer to instantiate.
timer(timer::kind k)196 timer::timer(timer::kind k)
197   : priv_(new timer::priv(k))
198 {
199   if (priv_->timer_kind == START_ON_INSTANTIATION_TIMER_KIND)
200     start();
201 }
202 
203 /// Start the timer.
204 ///
205 /// To stop the timer (and record the time elapsed since the timer was
206 /// started), call the timer::stop member function.
207 ///
208 /// @return true upon successful completion.
209 bool
start()210 timer::start()
211 {
212   if (gettimeofday(&priv_->begin_timeval, 0))
213     return false;
214   return true;
215 }
216 
217 /// Stop the timer.
218 ///
219 /// This records the time elapsed since the timer was started using
220 /// the timer::start member function.
221 ///
222 /// @return true upon successful completion.
223 bool
stop()224 timer::stop()
225 {
226   if (gettimeofday(&priv_->end_timeval, 0))
227     return false;
228   return true;
229 }
230 
231 /// Get the elapsed time in seconds.
232 ///
233 /// @return the time elapsed between the invocation of the methods
234 /// timer::start() and timer::stop, in seconds.
235 time_t
value_in_seconds() const236 timer::value_in_seconds() const
237 {return priv_->end_timeval.tv_sec - priv_->begin_timeval.tv_sec;}
238 
239 /// Get the elapsed time in hour:minutes:seconds:milliseconds.
240 ///
241 /// @param hours out parameter. This is set to the number of hours elapsed.
242 ///
243 /// @param minutes out parameter.  This is set to the number of minutes
244 /// (passed the number of hours) elapsed.
245 ///
246 /// @param seconds out parameter.  This is se to the number of
247 /// seconds (passed the number of hours and minutes) elapsed.
248 ///
249 /// @param milliseconds.  This is set ot the number of milliseconds
250 /// (passed the number of hours, minutes and seconds) elapsed.
251 ///
252 /// @return true upon successful completion.
253 bool
value(time_t & hours,time_t & minutes,time_t & seconds,time_t & milliseconds) const254 timer::value(time_t&		hours,
255 	     time_t&		minutes,
256 	     time_t&		seconds,
257 	     time_t&	milliseconds) const
258 {
259   time_t elapsed_seconds =
260     priv_->end_timeval.tv_sec - priv_->begin_timeval.tv_sec;
261   suseconds_t elapsed_usecs =
262     ((priv_->end_timeval.tv_sec * 1000000) + priv_->end_timeval.tv_usec)
263     - ((priv_->begin_timeval.tv_sec * 1000000) + priv_->begin_timeval.tv_usec);
264 
265   milliseconds = 0;
266 
267   hours = elapsed_seconds / 3600;
268   minutes = (elapsed_seconds % 3600) / 60;
269   seconds = (elapsed_seconds % 3600) % 60;
270   if (elapsed_seconds == 0)
271     milliseconds = elapsed_usecs / 1000;
272 
273   return true;
274 }
275 
276 /// Get the elapsed time as a human-readable string.
277 ///
278 /// @return the elapsed time as a human-readable string.
279 string
value_as_string() const280 timer::value_as_string() const
281 {
282   time_t hours = 0, minutes = 0, seconds = 0;
283   time_t msecs = 0;
284 
285   value(hours, minutes, seconds, msecs);
286 
287   std::ostringstream o;
288 
289   if (hours)
290     o << hours << "h";
291 
292   if (minutes)
293     o << minutes << "m";
294 
295   o << seconds << "s";
296 
297   if (msecs)
298     o <<msecs <<"ms";
299 
300   return o.str();
301 }
302 
303 /// Destructor of the @ref timer type.
~timer()304 timer::~timer()
305 {
306 }
307 
308 /// Streaming operator for the @ref timer type.
309 ///
310 /// Emit a string representing the elapsed time (in a human-readable
311 /// manner) to an output stream.
312 ///
313 /// @param o the output stream to emit the elapsed time string to.
314 ///
315 /// @param t the timer to consider.
316 ///
317 /// @return the output stream considered.
318 ostream&
operator <<(ostream & o,const timer & t)319 operator<<(ostream& o, const timer& t)
320 {
321   o << t.value_as_string();
322   return o;
323 }
324 
325 /// Get the stat struct (as returned by the lstat() function of the C
326 /// library) of a file.  Note that the function uses lstat, so that
327 /// callers can detect symbolic links.
328 ///
329 /// @param path the path to the function to stat.
330 ///
331 /// @param s the resulting stat struct.
332 ///
333 /// @return true iff the stat function completed successfully.
334 static bool
get_stat(const string & path,struct stat * s)335 get_stat(const string& path,
336 	 struct stat* s)
337 {return (lstat(path.c_str(), s) == 0);}
338 
339 /// Tests whether a path exists;
340 ///
341 /// @param path the path to test for.
342 ///
343 /// @return true iff the path at @p path exist.
344 bool
file_exists(const string & path)345 file_exists(const string& path)
346 {
347   DECLARE_STAT(st);
348 
349   return get_stat(path, &st);
350 }
351 
352 /// Test that a given directory exists.
353 ///
354 /// @param path the path of the directory to consider.
355 ///
356 /// @return true iff a directory exists with the name @p path
357 bool
dir_exists(const string & path)358 dir_exists(const string &path)
359 {return file_exists(path) && is_dir(path);}
360 
361 /// Test if a given directory exists and is empty.
362 ///
363 /// @param path the path of the directory to consider
364 bool
dir_is_empty(const string & path)365 dir_is_empty(const string &path)
366 {
367   if (!dir_exists(path))
368     return false;
369 
370   DIR* dir = opendir(path.c_str());
371   if (!dir)
372     return false;
373 
374   errno = 0;
375   dirent *result = readdir(dir);
376   if (result == NULL && errno != 0)
377     return false;
378 
379   closedir(dir);
380 
381   return result == NULL;
382 }
383 
384 /// Test if path is a path to a regular file or a symbolic link to a
385 /// regular file.
386 ///
387 /// @param path the path to consider.
388 ///
389 /// @return true iff path is a regular path.
390 bool
is_regular_file(const string & path)391 is_regular_file(const string& path)
392 {
393   DECLARE_STAT(st);
394 
395   if (!get_stat(path, &st))
396     return false;
397 
398   if (S_ISREG(st.st_mode))
399     return true;
400 
401   string symlink_target_path;
402   if (maybe_get_symlink_target_file_path(path, symlink_target_path))
403     return is_regular_file(symlink_target_path);
404 
405   return false;
406 }
407 
408 /// Tests if a given path is a directory or a symbolic link to a
409 /// directory.
410 ///
411 /// @param path the path to test for.
412 ///
413 /// @return true iff @p path is a directory.
414 bool
is_dir(const string & path)415 is_dir(const string& path)
416 {
417   DECLARE_STAT(st);
418 
419   if (!get_stat(path, &st))
420     return false;
421 
422   if (S_ISDIR(st.st_mode))
423     return true;
424 
425   string symlink_target_path;
426   if (maybe_get_symlink_target_file_path(path, symlink_target_path))
427     return is_dir(symlink_target_path);
428 
429   return false;
430 }
431 
432 static const char* ANONYMOUS_STRUCT_INTERNAL_NAME = "__anonymous_struct__";
433 static const char* ANONYMOUS_UNION_INTERNAL_NAME =  "__anonymous_union__";
434 static const char* ANONYMOUS_ENUM_INTERNAL_NAME =   "__anonymous_enum__";
435 
436 static int ANONYMOUS_STRUCT_INTERNAL_NAME_LEN =
437   strlen(ANONYMOUS_STRUCT_INTERNAL_NAME);
438 
439 static int ANONYMOUS_UNION_INTERNAL_NAME_LEN =
440   strlen(ANONYMOUS_UNION_INTERNAL_NAME);
441 
442 static int ANONYMOUS_ENUM_INTERNAL_NAME_LEN =
443   strlen(ANONYMOUS_ENUM_INTERNAL_NAME);
444 
445 /// Getter of the prefix for the name of anonymous structs.
446 ///
447 /// @reaturn the prefix for the name of anonymous structs.
448 const char*
get_anonymous_struct_internal_name_prefix()449 get_anonymous_struct_internal_name_prefix()
450 {return ANONYMOUS_STRUCT_INTERNAL_NAME;}
451 
452 /// Getter of the prefix for the name of anonymous unions.
453 ///
454 /// @reaturn the prefix for the name of anonymous unions.
455 const char*
get_anonymous_union_internal_name_prefix()456 get_anonymous_union_internal_name_prefix()
457 {return ANONYMOUS_UNION_INTERNAL_NAME;}
458 
459 /// Getter of the prefix for the name of anonymous enums.
460 ///
461 /// @reaturn the prefix for the name of anonymous enums.
462 const char*
get_anonymous_enum_internal_name_prefix()463 get_anonymous_enum_internal_name_prefix()
464 {return ANONYMOUS_ENUM_INTERNAL_NAME;}
465 
466 /// Compare two fully qualified decl names by taking into account that
467 /// they might have compontents that are anonymous types/namespace names.
468 ///
469 /// For instance:
470 ///
471 /// __anonymous_struct__1::foo and __anonymous_struct__2::foo are
472 /// considered being equivalent qualified names because both are data
473 /// members that belong to anonymous structs.  The anonymous structs
474 /// are numbered so that we can tell them appart (and look them up)
475 /// where there are several of them in the same scope.  But during
476 /// comparison, for various purposes, we want to consider them as
477 /// equivalent.
478 ///
479 /// Similarly, __anonymous_struct__1::foo::__anonymous_struct__2::bar
480 /// and __anonymous_struct__10::foo::__anonymous_struct__11::bar are
481 /// equivalent.
482 ///
483 /// But __anonymous_struct__1::foo::__anonymous_struct__2::bar and
484 /// __anonymous_struct__10::foo::__anonymous_union__11::bar are not
485 /// equivalent because the former designates a member of an anonymous
486 /// struct and the latter designates a member of an anonymous union.
487 ///
488 /// So this function handles those cases.
489 ///
490 /// @param l the name of the first (left hand side) decl to consider.
491 ///
492 /// @param r the name of the second (right hand side) decl to consider.
493 ///
494 /// @return true iff @p l is equivalent to @p r when taking into
495 /// account the anonymous scopes that both might have and if they
496 /// might be anonymous themselves.
497 bool
decl_names_equal(const string & l,const string & r)498 decl_names_equal(const string& l, const string& r)
499 {
500   string::size_type l_pos1 = 0, r_pos1 = 0;
501   const string::size_type l_length = l.length(), r_length = r.length();
502 
503   while (l_pos1 < l_length && r_pos1 < r_length)
504     {
505       string::size_type l_pos2 = l.find("::", l_pos1);
506       string::size_type r_pos2 = r.find("::", r_pos1);
507       if (l_pos2 == string::npos)
508 	l_pos2 = l_length;
509       if (r_pos2 == string::npos)
510 	r_pos2 = r_length;
511 
512       if (l.compare(l_pos1, l_pos2 - l_pos1, r,
513 		    r_pos1, r_pos2 - r_pos1)
514 	  && (l.compare(l_pos1,
515 			ANONYMOUS_STRUCT_INTERNAL_NAME_LEN,
516 			ANONYMOUS_STRUCT_INTERNAL_NAME)
517 	      || r.compare(r_pos1,
518 			   ANONYMOUS_STRUCT_INTERNAL_NAME_LEN,
519 			   ANONYMOUS_STRUCT_INTERNAL_NAME))
520 	  && (l.compare(l_pos1,
521 			ANONYMOUS_UNION_INTERNAL_NAME_LEN,
522 			ANONYMOUS_UNION_INTERNAL_NAME)
523 	      || r.compare(r_pos1,
524 			   ANONYMOUS_UNION_INTERNAL_NAME_LEN,
525 			   ANONYMOUS_UNION_INTERNAL_NAME))
526 	  && (l.compare(l_pos1,
527 			ANONYMOUS_ENUM_INTERNAL_NAME_LEN,
528 			ANONYMOUS_ENUM_INTERNAL_NAME)
529 	      || r.compare(r_pos1,
530 			   ANONYMOUS_ENUM_INTERNAL_NAME_LEN,
531 			   ANONYMOUS_ENUM_INTERNAL_NAME)))
532 	return false;
533 
534       l_pos1 = l_pos2 == l_length ? l_pos2 : l_pos2 + 2;
535       r_pos1 = r_pos2 == r_length ? r_pos2 : r_pos2 + 2;
536     }
537 
538   return (l_pos1 == l_length) == (r_pos1 == r_length);
539 }
540 
541 /// If a given file is a symbolic link, get the canonicalized absolute
542 /// path to the target file.
543 ///
544 /// @param file_path the path to the file to consider.
545 ///
546 /// @param target_path this parameter is set by the function to the
547 /// canonicalized path to the target file, if @p file_path is a
548 /// symbolic link.  In that case, the function returns true.
549 ///
550 /// @return true iff @p file_path is a symbolic link.  In that case,
551 /// the function sets @p target_path to the canonicalized absolute
552 /// path of the target file.
553 bool
maybe_get_symlink_target_file_path(const string & file_path,string & target_path)554 maybe_get_symlink_target_file_path(const string& file_path,
555 				   string& target_path)
556 {
557   DECLARE_STAT(st);
558 
559   if (!get_stat(file_path, &st))
560     return false;
561 
562   if (!S_ISLNK(st.st_mode))
563     return false;
564 
565   char *link_target_path = realpath(file_path.c_str(), NULL);
566   if (!link_target_path)
567     return false;
568 
569   target_path = link_target_path;
570   free(link_target_path);
571   return true;
572 }
573 
574 /// Return the directory part of a file path.
575 ///
576 /// @param path the file path to consider
577 ///
578 /// @param dirnam the resulting directory part, or "." if the couldn't
579 /// figure out anything better (for now; maybe we should do something
580 /// better than this later ...).
581 ///
582 /// @param keep_separator_at_end if true, then keep the separator at
583 /// the end of the resulting dir name.
584 ///
585 /// @return true upon successful completion, false otherwise (okay,
586 /// for now it always return true, but that might change in the future).
587 bool
dir_name(string const & path,string & dir_name,bool keep_separator_at_end)588 dir_name(string const& path,
589 	 string& dir_name,
590 	 bool keep_separator_at_end)
591 {
592   if (path.empty())
593     {
594       dir_name = ".";
595       return true;
596     }
597 
598   char *p = strdup(path.c_str());
599   char *r = ::dirname(p);
600   dir_name = r;
601   free(p);
602   if (keep_separator_at_end
603       && dir_name.length() < path.length())
604     dir_name += "/";
605   return true;
606 }
607 
608 /// Return the file name part of a file part.
609 ///
610 /// @param path the file path to consider.
611 ///
612 /// @param file_name the name part of the file to consider.
613 ///
614 ///@return true upon successful completion, false otherwise (okay it
615 ///always return true for now, but that might change in the future).
616 bool
base_name(string const & path,string & file_name)617 base_name(string const &path,
618 	 string& file_name)
619 {
620   if (path.empty())
621     {
622       file_name = ".";
623       return true;
624     }
625 
626   char *p = strdup(path.c_str());
627   char *f = ::basename(p);
628   file_name = f;
629   free(p);
630   return true;
631 }
632 
633 /// Return the real path of a given path.
634 ///
635 /// The real path of path 'foo_path' is the same path as foo_path, but
636 /// with symlinks and relative paths resolved.
637 ///
638 /// @param path the path to consider.
639 ///
640 /// @param result the computed real_path;
641 void
real_path(const string & path,string & result)642 real_path(const string&path, string& result)
643 {
644   if (path.empty())
645     {
646       result.clear();
647       return;
648     }
649 
650   char *realp = realpath(path.c_str(), NULL);
651   if (realp)
652     {
653       result = realp;
654       free(realp);
655     }
656 }
657 
658 /// Ensures #dir_path is a directory and is created.  If #dir_path is
659 /// not created, this function creates it.
660 ///
661 /// \return true if #dir_path is a directory that is already present,
662 /// of if the function has successfuly created it.
663 bool
ensure_dir_path_created(const string & dir_path)664 ensure_dir_path_created(const string& dir_path)
665 {
666   struct stat st;
667   memset(&st, 0, sizeof (st));
668 
669   int stat_result = 0;
670 
671   stat_result = stat(dir_path.c_str(), &st);
672   if (stat_result == 0)
673     {
674       // A file or directory already exists with the same name.
675       if (!S_ISDIR (st.st_mode))
676 	return false;
677       return true;
678     }
679 
680   string cmd;
681   cmd = "mkdir -p " + dir_path;
682 
683   if (system(cmd.c_str()))
684     return false;
685 
686   return true;
687 }
688 
689 /// Ensures that the parent directory of #path is created.
690 ///
691 /// \return true if the parent directory of #path is already present,
692 /// or if this function has successfuly created it.
693 bool
ensure_parent_dir_created(const string & path)694 ensure_parent_dir_created(const string& path)
695 {
696   bool is_ok = false;
697 
698   if (path.empty())
699     return is_ok;
700 
701   string parent;
702   if (dir_name(path, parent))
703     is_ok = ensure_dir_path_created(parent);
704 
705   return is_ok;
706 }
707 
708 /// Emit a prefix made of the name of the program which is emitting a
709 /// message to an output stream.
710 ///
711 /// The prefix is a string which looks like:
712 ///
713 ///   "<program-name> : "
714 ///
715 /// @param prog_name the name of the program to use in the prefix.
716 /// @param out the output stream where to emit the prefix.
717 ///
718 /// @return the output stream where the prefix was emitted.
719 ostream&
emit_prefix(const string & prog_name,ostream & out)720 emit_prefix(const string& prog_name, ostream& out)
721 {
722   if (!prog_name.empty())
723     out << prog_name << ": ";
724   return out;
725 }
726 
727 /// Check if a given path exists and is readable.
728 ///
729 /// @param path the path to consider.
730 ///
731 /// @param out the out stream to report errors to.
732 ///
733 /// @return true iff path exists and is readable.
734 bool
check_file(const string & path,ostream & out,const string & prog_name)735 check_file(const string& path, ostream& out, const string& prog_name)
736 {
737   if (!file_exists(path))
738     {
739       emit_prefix(prog_name, out) << "file " << path << " does not exist\n";
740       return false;
741     }
742 
743   if (!is_regular_file(path))
744     {
745       emit_prefix(prog_name, out) << path << " is not a regular file\n";
746       return false;
747     }
748 
749   return true;
750 }
751 
752 /// Check if a given path exists, is readable and is a directory.
753 ///
754 /// @param path the path to consider.
755 ///
756 /// @param out the out stream to report errors to.
757 ///
758 /// @param prog_name the program name on behalf of which to report the
759 /// error, if any.
760 ///
761 /// @return true iff @p path exists and is for a directory.
762 bool
check_dir(const string & path,ostream & out,const string & prog_name)763 check_dir(const string& path, ostream& out, const string& prog_name)
764 {
765   if (!file_exists(path))
766     {
767       emit_prefix(prog_name, out) << "path " << path << " does not exist\n";
768       return false;
769     }
770 
771   if (!is_dir(path))
772     {
773       emit_prefix(prog_name, out) << path << " is not a directory\n";
774       return false;
775     }
776 
777   return true;
778 }
779 
780 /// Test if a given string ends with a particular suffix.
781 ///
782 /// @param str the string to consider.
783 ///
784 /// @param suffix the suffix to test for.
785 ///
786 /// @return true iff string @p str ends with suffix @p suffix.
787 bool
string_ends_with(const string & str,const string & suffix)788 string_ends_with(const string& str, const string& suffix)
789 {
790   string::size_type str_len = str.length(), suffix_len = suffix.length();
791 
792   if (str_len < suffix_len)
793     return false;
794   return str.compare(str_len - suffix_len, suffix_len, suffix) == 0;
795 }
796 
797 /// Test if a given string begins with a particular prefix.
798 ///
799 /// @param str the string consider.
800 ///
801 /// @param prefix the prefix to look for.
802 ///
803 /// @return true iff string @p str begins with prefix @p prefix.
804 bool
string_begins_with(const string & str,const string & prefix)805 string_begins_with(const string& str, const string& prefix)
806 {
807   if (str.empty())
808     return false;
809 
810   if (prefix.empty())
811     return true;
812 
813   string::size_type prefix_len = prefix.length();
814   if (prefix_len > str.length())
815     return false;
816 
817   return str.compare(0, prefix.length(), prefix) == 0;
818 }
819 
820 /// Test if a string is made of ascii characters.
821 ///
822 /// @param str the string to consider.
823 ///
824 /// @return true iff @p str is made of ascii characters.
825 bool
string_is_ascii(const string & str)826 string_is_ascii(const string& str)
827 {
828   for (string::const_iterator i = str.begin(); i != str.end(); ++i)
829     if (!isascii(*i))
830       return false;
831 
832   return true;
833 }
834 
835 /// Test if a string is made of ascii characters which are identifiers
836 /// acceptable in C or C++ programs.
837 ///
838 ///
839 /// In the C++ spec, [lex.charset]/2, we can read:
840 ///
841 /// "if the hexadecimal value for a universal-character-name [...]  or
842 ///  string literal corresponds to a control character (in either of
843 ///  the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) [...] the
844 ///  program is ill-formed."
845 ///
846 /// @param str the string to consider.
847 ///
848 /// @return true iff @p str is made of ascii characters, and is an
849 /// identifier.
850 bool
string_is_ascii_identifier(const string & str)851 string_is_ascii_identifier(const string& str)
852 {
853   for (string::const_iterator i = str.begin(); i != str.end(); ++i)
854     {
855       unsigned char c = *i;
856     if (!isascii(c)
857 	|| (c <= 0x1F) // Rule out control characters
858 	|| (c >= 0x7F && c <= 0x9F)) // Rule out special extended
859 				     // ascii characters.
860       return false;
861     }
862 
863   return true;
864 }
865 
866 /// Split a given string into substrings, given some delimiters.
867 ///
868 /// @param input_string the input string to split.
869 ///
870 /// @param delims a string containing the delimiters to consider.
871 ///
872 /// @param result a vector of strings containing the splitted result.
873 ///
874 /// @return true iff the function found delimiters in the input string
875 /// and did split it as a result.  Note that if no delimiter was found
876 /// in the input string, then the input string is added at the end of
877 /// the output vector of strings.
878 bool
split_string(const string & input_string,const string & delims,vector<string> & result)879 split_string(const string& input_string,
880 	     const string& delims,
881 	     vector<string>& result)
882 {
883   size_t current = 0, next;
884   bool did_split = false;
885 
886   do
887     {
888       // Trim leading white spaces
889       while (current < input_string.size() && isspace(input_string[current]))
890 	++current;
891 
892       if (current >= input_string.size())
893 	break;
894 
895       next = input_string.find_first_of(delims, current);
896       if (next == string::npos)
897 	{
898 	  string s = input_string.substr(current);
899 	  if (!s.empty())
900 	    result.push_back(input_string.substr(current));
901 	  did_split = (current != 0);
902 	  break;
903 	}
904       string s = input_string.substr(current, next - current);
905       if (!s.empty())
906 	{
907 	  result.push_back(input_string.substr(current, next - current));
908 	  did_split = true;
909 	}
910       current = next + 1;
911     }
912   while (next != string::npos);
913 
914   return did_split;
915 }
916 
917 /// Get the suffix of a string, given a prefix to consider.
918 ///
919 /// @param input_string the input string to consider.
920 ///
921 /// @param prefix the prefix of the input string to consider.
922 ///
923 /// @param suffix output parameter. This is set by the function to the
924 /// the computed suffix iff a suffix was found for prefix @p prefix.
925 ///
926 /// @return true iff the function could find a prefix for the suffix
927 /// @p suffix in the input string @p input_string.
928 bool
string_suffix(const string & input_string,const string & prefix,string & suffix)929 string_suffix(const string& input_string,
930 	      const string& prefix,
931 	      string& suffix)
932 {
933   // Some basic sanity check before we start hostilities.
934   if (prefix.length() >= input_string.length())
935     return false;
936 
937   if (input_string.compare(0, prefix.length(), prefix) != 0)
938     // The input string does not start with the string contained in
939     // the prefix parameter.
940     return false;
941 
942   suffix = input_string.substr(prefix.length());
943   return true;
944 }
945 
946 /// Return the prefix that is common to two strings.
947 ///
948 /// @param s1 the first input string to consider.
949 ///
950 /// @param s2 the second input string to consider.
951 ///
952 /// @param result output parameter.  The resulting common prefix found
953 /// between @p s1 and @p s2.  This is set iff the function returns
954 /// true.
955 ///
956 /// @return true iff @p result was set by this function with the
957 /// common prefix of @p s1 and @p s2.
958 static bool
common_prefix(const string & s1,const string & s2,string & result)959 common_prefix(const string& s1, const string& s2, string &result)
960 {
961   if (s1.length() == 0 || s2.length() == 0)
962     return false;
963 
964   result.clear();
965   for (size_t i = 0; i < s1.length() && i< s2.length(); ++i)
966     if (s1[i] == s2[i])
967       result += s1[i];
968     else
969       break;
970 
971   return !result.empty();
972 }
973 
974 /// Find the prefix common to a *SORTED* vector of strings.
975 ///
976 /// @param input_strings a lexycographically sorted vector of
977 /// strings.  Please note that this vector absolutely needs to be
978 /// sorted for the function to work correctly.  Otherwise the results
979 /// are going to be wrong.
980 ///
981 /// @param prefix output parameter.  This is set by this function with
982 /// the prefix common to the strings found in @p input_strings, iff
983 /// the function returns true.
984 ///
985 /// @return true iff the function could find a common prefix to the
986 /// strings in @p input_strings.
987 bool
sorted_strings_common_prefix(vector<string> & input_strings,string & prefix)988 sorted_strings_common_prefix(vector<string>& input_strings, string& prefix)
989 {
990   string prefix_candidate;
991   bool found_prefix = false;
992 
993   if (input_strings.size() == 1)
994     {
995       if (dir_name(input_strings.front(), prefix,
996 		   /*keep_separator_at_end=*/true))
997 	return true;
998       return false;
999     }
1000 
1001   string cur_str;
1002   for (vector<string>::const_iterator i = input_strings.begin();
1003        i != input_strings.end();
1004        ++i)
1005     {
1006       dir_name(*i, cur_str, /*keep_separator_at_end=*/true);
1007       if (prefix_candidate.empty())
1008 	{
1009 	  prefix_candidate = cur_str;
1010 	  continue;
1011 	}
1012 
1013       string s;
1014       if (common_prefix(prefix_candidate, cur_str, s))
1015 	{
1016 	  ABG_ASSERT(!s.empty());
1017 	  prefix_candidate = s;
1018 	  found_prefix = true;
1019 	}
1020     }
1021 
1022   if (found_prefix)
1023     {
1024       prefix = prefix_candidate;
1025       return true;
1026     }
1027 
1028   return false;
1029 }
1030 
1031 /// Return the version string of the library.
1032 ///
1033 /// @return the version string of the library.
1034 string
get_library_version_string()1035 get_library_version_string()
1036 {
1037   string major, minor, revision, version_string, suffix;
1038   abigail::abigail_get_library_version(major, minor, revision, suffix);
1039   version_string = major + "." + minor + "." + revision + suffix;
1040   return version_string;
1041 }
1042 
1043 /// Return the version string for the ABIXML format.
1044 ///
1045 /// @return the version string of the ABIXML format.
1046 string
get_abixml_version_string()1047 get_abixml_version_string()
1048 {
1049   string major, minor, version_string;
1050   abigail::abigail_get_abixml_version(major, minor);
1051   version_string = major + "." + minor;
1052   return version_string;
1053 }
1054 
1055 /// Execute a shell command and returns its output.
1056 ///
1057 /// @param cmd the shell command to execute.
1058 ///
1059 /// @param lines output parameter.  This is set with the lines that
1060 /// constitute the output of the process that executed the command @p
1061 /// cmd.
1062 ///
1063 /// @return true iff the command was executed properly and no error
1064 /// was encountered.
1065 bool
execute_command_and_get_output(const string & cmd,vector<string> & lines)1066 execute_command_and_get_output(const string& cmd, vector<string>& lines)
1067 {
1068   if (cmd.empty())
1069     return false;
1070 
1071   FILE *stream=
1072     popen(cmd.c_str(),
1073 	  /*open 'stream' in
1074 	    read-only mode: type=*/"r");
1075 
1076   if (stream == NULL)
1077     return false;
1078 
1079   string result;
1080 
1081 #define TMP_BUF_LEN 1024 + 1
1082   char tmp_buf[TMP_BUF_LEN];
1083   memset(tmp_buf, 0, TMP_BUF_LEN);
1084 
1085   while (fgets(tmp_buf, TMP_BUF_LEN, stream))
1086     {
1087       lines.push_back(tmp_buf);
1088       memset(tmp_buf, 0, TMP_BUF_LEN);
1089     }
1090 
1091   if (pclose(stream) == -1)
1092     return false;
1093 
1094   return true;
1095 }
1096 
1097 /// Get the SONAMEs of the DSOs advertised as being "provided" by a
1098 /// given RPM.  That set can be considered as being the set of
1099 /// "public" DSOs of the RPM.
1100 ///
1101 /// This runs the command "rpm -qp --provides <rpm> | grep .so" and
1102 /// filters its result.
1103 ///
1104 /// @param rpm_path the path to the RPM to consider.
1105 ///
1106 /// @param provided_dsos output parameter.  This is set to the set of
1107 /// SONAMEs of the DSOs advertised as being provided by the RPM
1108 /// designated by @p rpm_path.
1109 ///
1110 /// @return true iff we could successfully query the RPM to see what
1111 /// DSOs it provides.
1112 bool
get_dsos_provided_by_rpm(const string & rpm_path,set<string> & provided_dsos)1113 get_dsos_provided_by_rpm(const string& rpm_path, set<string>& provided_dsos)
1114 {
1115   vector<string> query_output;
1116   // We don't check the return value of this command because on some
1117   // system, the command can issue errors but still emit a valid
1118   // output.  We'll rather rely on the fact that the command emits a
1119   // valid output or not.
1120   execute_command_and_get_output("rpm -qp --provides "
1121 				 + rpm_path + " 2> /dev/null | grep .so",
1122 				 query_output);
1123 
1124   for (vector<string>::const_iterator line = query_output.begin();
1125        line != query_output.end();
1126        ++line)
1127     {
1128       string dso = line->substr(0, line->find('('));
1129       dso = trim_white_space(dso);
1130       if (!dso.empty())
1131 	provided_dsos.insert(dso);
1132     }
1133   return true;
1134 }
1135 
1136 /// Remove spaces at the beginning and at the end of a given string.
1137 ///
1138 /// @param str the input string to consider.
1139 ///
1140 /// @return the @p str string with leading and trailing white spaces removed.
1141 string
trim_white_space(const string & str)1142 trim_white_space(const string& str)
1143 {
1144   if (str.empty())
1145     return "";
1146 
1147   string result;
1148   string::size_type start, end;
1149   for (start = 0; start < str.length(); ++start)
1150     if (!isspace(str[start]))
1151       break;
1152 
1153   for (end = str.length() - 1; end > 0; --end)
1154     if (!isspace(str[end]))
1155       break;
1156 
1157   result = str.substr(start, end - start + 1);
1158   return result;
1159 }
1160 
1161 /// Remove a string of pattern in front of a given string.
1162 ///
1163 /// For instance, consider this string:
1164 ///    "../../../foo"
1165 ///
1166 /// The pattern "../" is repeated three times in front of the
1167 /// sub-string "foo".  Thus, the call:
1168 ///    trim_leading_string("../../../foo", "../")
1169 /// will return the string "foo".
1170 ///
1171 /// @param from the string to trim the leading repetition of pattern from.
1172 ///
1173 /// @param to_trim the pattern to consider (and to trim).
1174 ///
1175 /// @return the resulting string where the leading patter @p to_trim
1176 /// has been removed from.
1177 string
trim_leading_string(const string & from,const string & to_trim)1178 trim_leading_string(const string& from, const string& to_trim)
1179 {
1180   string str = from;
1181 
1182   while (string_begins_with(str, to_trim))
1183     string_suffix(str, to_trim, str);
1184   return str;
1185 }
1186 
1187 /// Convert a vector<char*> into a vector<char**>.
1188 ///
1189 /// @param char_stars the input vector.
1190 ///
1191 /// @param char_star_stars the output vector.
1192 void
convert_char_stars_to_char_star_stars(const vector<char * > & char_stars,vector<char ** > & char_star_stars)1193 convert_char_stars_to_char_star_stars(const vector<char*> &char_stars,
1194 				      vector<char**>& char_star_stars)
1195 {
1196   for (vector<char*>::const_iterator i = char_stars.begin();
1197        i != char_stars.end();
1198        ++i)
1199     char_star_stars.push_back(const_cast<char**>(&*i));
1200 }
1201 
1202 /// The private data of the @ref temp_file type.
1203 struct temp_file::priv
1204 {
1205   char*			   path_template_;
1206   int			   fd_;
1207   shared_ptr<std::fstream> fstream_;
1208 
privabigail::tools_utils::temp_file::priv1209   priv()
1210   {
1211     const char* templat = "/tmp/libabigail-tmp-file-XXXXXX";
1212     int s = strlen(templat);
1213     path_template_ = new char[s + 1];
1214     memset(path_template_, 0, s + 1);
1215     memcpy(path_template_, templat, s);
1216 
1217     fd_ = mkstemp(path_template_);
1218     if (fd_ == -1)
1219       return;
1220 
1221     fstream_.reset(new std::fstream(path_template_,
1222 				    std::ios::trunc
1223 				    | std::ios::in
1224 				    | std::ios::out));
1225   }
1226 
~privabigail::tools_utils::temp_file::priv1227   ~priv()
1228   {
1229     if (fd_ && fd_ != -1)
1230       {
1231 	fstream_.reset();
1232 	close(fd_);
1233 	remove(path_template_);
1234       }
1235     delete [] path_template_;
1236   }
1237 };
1238 
1239 /// Default constructor of @ref temp_file.
1240 ///
1241 /// It actually creates the temporary file.
temp_file()1242 temp_file::temp_file()
1243   : priv_(new priv)
1244 {}
1245 
1246 /// Test if the temporary file has been created and is usable.
1247 ///
1248 /// @return true iff the temporary file has been created and is
1249 /// useable.
1250 bool
is_good() const1251 temp_file::is_good() const
1252 {return priv_->fstream_->good();}
1253 
1254 /// Return the path to the temporary file.
1255 ///
1256 /// @return the path to the temporary file if it's usable, otherwise
1257 /// return nil.
1258 const char*
get_path() const1259 temp_file::get_path() const
1260 {
1261   if (is_good())
1262     return priv_->path_template_;
1263 
1264   return 0;
1265 }
1266 
1267 /// Get the fstream to the temporary file.
1268 ///
1269 /// Note that the current process is aborted if this member function
1270 /// is invoked on an instance of @ref temp_file that is not usable.
1271 /// So please test that the instance is usable by invoking the
1272 /// temp_file::is_good() member function on it first.
1273 ///
1274 /// @return the fstream to the temporary file.
1275 std::fstream&
get_stream()1276 temp_file::get_stream()
1277 {
1278   ABG_ASSERT(is_good());
1279   return *priv_->fstream_;
1280 }
1281 
1282 /// Create the temporary file and return it if it's usable.
1283 ///
1284 /// @return the newly created temporary file if it's usable, nil
1285 /// otherwise.
1286 temp_file_sptr
create()1287 temp_file::create()
1288 {
1289   temp_file_sptr result(new temp_file);
1290   if (result->is_good())
1291     return result;
1292 
1293   return temp_file_sptr();
1294 }
1295 
1296 /// Get a pseudo random number.
1297 ///
1298 /// @return a pseudo random number.
1299 size_t
get_random_number()1300 get_random_number()
1301 {
1302   static __thread bool initialized = false;
1303 
1304   if (!initialized)
1305     {
1306       srand(time(NULL));
1307       initialized = true;
1308     }
1309 
1310   return rand();
1311 }
1312 
1313 /// Get a pseudo random number as string.
1314 ///
1315 /// @return a pseudo random number as string.
1316 string
get_random_number_as_string()1317 get_random_number_as_string()
1318 {
1319   std::ostringstream o;
1320   o << get_random_number();
1321 
1322   return o.str();
1323 }
1324 
1325 ostream&
operator <<(ostream & output,file_type r)1326 operator<<(ostream& output,
1327 	   file_type r)
1328 {
1329   string repr;
1330 
1331   switch(r)
1332     {
1333     case FILE_TYPE_UNKNOWN:
1334       repr = "unknown file type";
1335       break;
1336     case FILE_TYPE_NATIVE_BI:
1337       repr = "native binary instrumentation file type";
1338       break;
1339     case FILE_TYPE_ELF:
1340       repr = "ELF file type";
1341       break;
1342     case FILE_TYPE_AR:
1343       repr = "archive file type";
1344       break;
1345     case FILE_TYPE_XML_CORPUS:
1346       repr = "native XML corpus file type";
1347       break;
1348     case FILE_TYPE_XML_CORPUS_GROUP:
1349       repr = "native XML corpus group file type";
1350       break;
1351     case FILE_TYPE_RPM:
1352       repr = "RPM file type";
1353       break;
1354     case FILE_TYPE_SRPM:
1355       repr = "SRPM file type";
1356       break;
1357     case FILE_TYPE_DEB:
1358       repr = "Debian binary file type";
1359       break;
1360     case FILE_TYPE_DIR:
1361       repr = "Directory type";
1362       break;
1363     case FILE_TYPE_TAR:
1364       repr = "GNU tar archive type";
1365       break;
1366     }
1367 
1368   output << repr;
1369   return output;
1370 }
1371 
1372 /// Guess the type of the content of an input stream.
1373 ///
1374 /// @param in the input stream to guess the content type for.
1375 ///
1376 /// @return the type of content guessed.
1377 file_type
guess_file_type(istream & in)1378 guess_file_type(istream& in)
1379 {
1380   const unsigned BUF_LEN = 264;
1381   const unsigned NB_BYTES_TO_READ = 263;
1382 
1383   char buf[BUF_LEN];
1384   memset(buf, 0, BUF_LEN);
1385 
1386   std::streampos initial_pos = in.tellg();
1387   in.read(buf, NB_BYTES_TO_READ);
1388   in.seekg(initial_pos);
1389 
1390   if (in.gcount() < 4 || in.bad())
1391     return FILE_TYPE_UNKNOWN;
1392 
1393   if (buf[0] == 0x7f
1394       && buf[1] == 'E'
1395       && buf[2] == 'L'
1396       && buf[3] == 'F')
1397     return FILE_TYPE_ELF;
1398 
1399   if (buf[0] == '!'
1400       && buf[1] == '<'
1401       && buf[2] == 'a'
1402       && buf[3] == 'r'
1403       && buf[4] == 'c'
1404       && buf[5] == 'h'
1405       && buf[6] == '>')
1406     {
1407       if (strstr(buf, "debian-binary"))
1408 	return FILE_TYPE_DEB;
1409       else
1410 	return FILE_TYPE_AR;
1411     }
1412 
1413   if (buf[0] == '<'
1414       && buf[1] == 'a'
1415       && buf[2] == 'b'
1416       && buf[3] == 'i'
1417       && buf[4] == '-'
1418       && buf[5] == 'i'
1419       && buf[6] == 'n'
1420       && buf[7] == 's'
1421       && buf[8] == 't'
1422       && buf[9] == 'r'
1423       && buf[10] == ' ')
1424     return FILE_TYPE_NATIVE_BI;
1425 
1426   if (buf[0]     == '<'
1427       && buf[1]  == 'a'
1428       && buf[2]  == 'b'
1429       && buf[3]  == 'i'
1430       && buf[4]  == '-'
1431       && buf[5]  == 'c'
1432       && buf[6]  == 'o'
1433       && buf[7]  == 'r'
1434       && buf[8]  == 'p'
1435       && buf[9]  == 'u'
1436       && buf[10] == 's'
1437       && buf[11] == '-'
1438       && buf[12] == 'g'
1439       && buf[13] == 'r'
1440       && buf[14] == 'o'
1441       && buf[15] == 'u'
1442       && buf[16] == 'p'
1443       && buf[17] == ' ')
1444     return FILE_TYPE_XML_CORPUS_GROUP;
1445 
1446   if (buf[0]     == '<'
1447       && buf[1]  == 'a'
1448       && buf[2]  == 'b'
1449       && buf[3]  == 'i'
1450       && buf[4]  == '-'
1451       && buf[5]  == 'c'
1452       && buf[6]  == 'o'
1453       && buf[7]  == 'r'
1454       && buf[8]  == 'p'
1455       && buf[9]  == 'u'
1456       && buf[10] == 's'
1457       && buf[11] == ' ')
1458     return FILE_TYPE_XML_CORPUS;
1459 
1460   if ((unsigned char) buf[0]    == 0xed
1461       && (unsigned char) buf[1] == 0xab
1462       && (unsigned char) buf[2] == 0xee
1463       && (unsigned char) buf[3] == 0xdb)
1464     {
1465         if (buf[7] == 0x00)
1466           return FILE_TYPE_RPM;
1467         else if (buf[7] == 0x01)
1468           return FILE_TYPE_SRPM;
1469         else
1470           return FILE_TYPE_UNKNOWN;
1471     }
1472 
1473   if (buf[257]    == 'u'
1474       && buf[258] == 's'
1475       && buf[259] == 't'
1476       && buf[260] == 'a'
1477       && buf[261] == 'r')
1478     return FILE_TYPE_TAR;
1479 
1480   return FILE_TYPE_UNKNOWN;
1481 }
1482 
1483 /// Guess the type of the content of an file.
1484 ///
1485 /// @param file_path the path to the file to consider.
1486 ///
1487 /// @return the type of content guessed.
1488 file_type
guess_file_type(const string & file_path)1489 guess_file_type(const string& file_path)
1490 {
1491   if (is_dir(file_path))
1492     return FILE_TYPE_DIR;
1493 
1494   if (string_ends_with(file_path, ".tar")
1495       || string_ends_with(file_path, ".tar.gz")
1496       || string_ends_with(file_path, ".tgz")
1497       || string_ends_with(file_path, ".tar.bz2")
1498       || string_ends_with(file_path, ".tbz2")
1499       || string_ends_with(file_path, ".tbz")
1500       || string_ends_with(file_path, ".tb2")
1501       || string_ends_with(file_path, ".tar.xz")
1502       || string_ends_with(file_path, ".txz")
1503       || string_ends_with(file_path, ".tar.lzma")
1504       || string_ends_with(file_path, ".tar.lz")
1505       || string_ends_with(file_path, ".tlz")
1506       || string_ends_with(file_path, ".tar.Z")
1507       || string_ends_with(file_path, ".taz")
1508       || string_ends_with(file_path, ".tz"))
1509     return FILE_TYPE_TAR;
1510 
1511   ifstream in(file_path.c_str(), ifstream::binary);
1512   file_type r = guess_file_type(in);
1513   in.close();
1514   return r;
1515 }
1516 
1517 /// Get the package name of a .deb package.
1518 ///
1519 /// @param str the string containing the .deb NVR.
1520 ///
1521 /// @param name output parameter.  This is set with the package name
1522 /// of the .deb package iff the function returns true.
1523 ///
1524 /// @return true iff the function successfully finds the .deb package
1525 /// name.
1526 bool
get_deb_name(const string & str,string & name)1527 get_deb_name(const string& str, string& name)
1528 {
1529   if (str.empty() || str[0] == '_')
1530     return false;
1531 
1532   string::size_type str_len = str.length(), i = 0 ;
1533 
1534   for (; i < str_len; ++i)
1535     {
1536       if (str[i] == '_')
1537 	break;
1538     }
1539 
1540   if (i == str_len)
1541     return false;
1542 
1543   name = str.substr(0, i);
1544   return true;
1545 }
1546 
1547 /// Get the package name of an rpm package.
1548 ///
1549 /// @param str the string containing the NVR of the rpm.
1550 ///
1551 /// @param name output parameter.  This is set with the package name
1552 /// of the rpm package iff the function returns true.
1553 ///
1554 /// @return true iff the function successfully finds the rpm package
1555 /// name.
1556 bool
get_rpm_name(const string & str,string & name)1557 get_rpm_name(const string& str, string& name)
1558 {
1559   if (str.empty() || str[0] == '-')
1560     return false;
1561 
1562   string::size_type str_len = str.length(), i = 0;
1563   string::value_type c;
1564 
1565   for (; i < str_len; ++i)
1566     {
1567       c = str[i];
1568       string::size_type next_index = i + 1;
1569       if ((next_index < str_len) && c == '-' && isdigit(str[next_index]))
1570 	break;
1571     }
1572 
1573   if (i == str_len)
1574     return false;
1575 
1576   name = str.substr(0, i);
1577 
1578   return true;
1579 }
1580 
1581 /// Get the architecture string from the NVR of an rpm.
1582 ///
1583 /// @param str the NVR to consider.
1584 ///
1585 /// @param arch output parameter.  Is set to the resulting
1586 /// archirecture string iff the function returns true.
1587 ///
1588 /// @return true iff the function could find the architecture string
1589 /// from the NVR.
1590 bool
get_rpm_arch(const string & str,string & arch)1591 get_rpm_arch(const string& str, string& arch)
1592 {
1593   if (str.empty())
1594     return false;
1595 
1596   if (!string_ends_with(str, ".rpm"))
1597     return false;
1598 
1599   string::size_type str_len = str.length(), i = 0;
1600   string::value_type c;
1601   string::size_type last_dot_index = 0, dot_before_last_index = 0;
1602 
1603   for (i = str_len - 1; i > 0; --i)
1604     {
1605       c = str[i];
1606       if (c == '.')
1607 	{
1608 	  last_dot_index = i;
1609 	  break;
1610 	}
1611     }
1612 
1613   if (i == 0)
1614     return false;
1615 
1616   for(--i; i > 0; --i)
1617     {
1618       c = str[i];
1619       if (c == '.')
1620 	{
1621 	  dot_before_last_index = i;
1622 	  break;
1623 	}
1624     }
1625 
1626   if (i == 0)
1627     return false;
1628 
1629   arch = str.substr(dot_before_last_index + 1,
1630 		    last_dot_index - dot_before_last_index - 1);
1631 
1632   return true;
1633 }
1634 
1635 /// Tests if a given file name designates a kernel package.
1636 ///
1637 /// @param file_name the file name to consider.
1638 ///
1639 /// @param file_type the type of the file @p file_name.
1640 ///
1641 /// @return true iff @p file_name of kind @p file_type designates a
1642 /// kernel package.
1643 bool
file_is_kernel_package(const string & file_name,file_type file_type)1644 file_is_kernel_package(const string& file_name, file_type file_type)
1645 {
1646   bool result = false;
1647   string package_name;
1648 
1649   if (file_type == FILE_TYPE_RPM)
1650     {
1651       if (!get_rpm_name(file_name, package_name))
1652 	return false;
1653       result = (package_name == "kernel");
1654     }
1655   else if (file_type == FILE_TYPE_DEB)
1656     {
1657       if (!get_deb_name(file_name, package_name))
1658 	return false;
1659       result = (string_begins_with(package_name, "linux-image"));
1660     }
1661 
1662   return result;
1663 }
1664 
1665 /// Tests if a given file name designates a kernel debuginfo package.
1666 ///
1667 /// @param file_name the file name to consider.
1668 ///
1669 /// @param file_type the type of the file @p file_name.
1670 ///
1671 /// @return true iff @p file_name of kind @p file_type designates a
1672 /// kernel debuginfo package.
1673 bool
file_is_kernel_debuginfo_package(const string & file_name,file_type file_type)1674 file_is_kernel_debuginfo_package(const string& file_name, file_type file_type)
1675 {
1676   bool result = false;
1677   string package_name;
1678 
1679   if (file_type == FILE_TYPE_RPM)
1680     {
1681       if (!get_rpm_name(file_name, package_name))
1682 	return false;
1683       result = (package_name == "kernel-debuginfo");
1684     }
1685   else if (file_type == FILE_TYPE_DEB)
1686     {
1687       if (!get_deb_name(file_name, package_name))
1688 	return false;
1689       result = (string_begins_with(package_name, "linux-image")
1690 		&& (string_ends_with(package_name, "-dbg")
1691 		    || string_ends_with(package_name, "-dbgsyms")));
1692     }
1693 
1694   return result;
1695 }
1696 
1697 /// The delete functor of a char buffer that has been created using
1698 /// malloc.
1699 struct malloced_char_star_deleter
1700 {
1701   void
operator ()abigail::tools_utils::malloced_char_star_deleter1702   operator()(char* ptr)
1703   {free(ptr);}
1704 };
1705 
1706 /// Return a copy of the path given in argument, turning it into an
1707 /// absolute path by prefixing it with the concatenation of the result
1708 /// of get_current_dir_name() and the '/' character.
1709 ///
1710 /// The result being an shared_ptr to char*, it should manage its
1711 /// memory by itself and the user shouldn't need to wory too much for
1712 /// that.
1713 ///
1714 /// @param p the path to turn into an absolute path.
1715 ///
1716 /// @return a shared pointer to the resulting absolute path.
1717 std::shared_ptr<char>
make_path_absolute(const char * p)1718 make_path_absolute(const char*p)
1719 {
1720   using std::shared_ptr;
1721 
1722   shared_ptr<char> result;
1723 
1724   if (p && p[0] != '/')
1725     {
1726       shared_ptr<char> pwd(get_current_dir_name(),
1727 			   malloced_char_star_deleter());
1728       string s = string(pwd.get()) + "/" + p;
1729       result.reset(strdup(s.c_str()), malloced_char_star_deleter());
1730     }
1731   else
1732     result.reset(strdup(p), malloced_char_star_deleter());
1733 
1734   return result;
1735 }
1736 
1737 /// Return a copy of the path given in argument, turning it into an
1738 /// absolute path by prefixing it with the concatenation of the result
1739 /// of get_current_dir_name() and the '/' character.
1740 ///
1741 /// The result being a pointer to an allocated memory region, it must
1742 /// be freed by the caller.
1743 ///
1744 /// @param p the path to turn into an absolute path.
1745 ///
1746 /// @return a pointer to the resulting absolute path.  It must be
1747 /// freed by the caller.
1748 char*
make_path_absolute_to_be_freed(const char * p)1749 make_path_absolute_to_be_freed(const char*p)
1750 {
1751     char* result = 0;
1752 
1753   if (p && p[0] != '/')
1754     {
1755       char* pwd = get_current_dir_name();
1756       string s = string(pwd) + "/" + p;
1757       free(pwd);
1758       result = strdup(s.c_str());
1759     }
1760   else
1761     result = strdup(p);
1762 
1763   return result;
1764 }
1765 
1766 /// This is a sub-routine of gen_suppr_spec_from_headers and
1767 /// handle_fts_entry.
1768 ///
1769 /// It setups a type suppression which is meant to keep types defined
1770 /// in a given file and suppress all other types.
1771 ///
1772 /// @param file_path the path to the file that defines types that are
1773 /// meant to be kept by the type suppression.  All other types defined
1774 /// in other files are to be suppressed.  Note that this file path is
1775 /// added to the vector returned by
1776 /// type_suppression::get_source_locations_to_keep()
1777 ///
1778 /// @param suppr the type suppression to setup.  If this smart pointer
1779 /// is nil then a new instance @ref type_suppression is created and
1780 /// this variable is made to point to it.
1781 static void
handle_file_entry(const string & file_path,type_suppression_sptr & suppr)1782 handle_file_entry(const string& file_path,
1783 		  type_suppression_sptr& suppr)
1784 {
1785   if (!suppr)
1786     {
1787       suppr.reset(new type_suppression(get_private_types_suppr_spec_label(),
1788 				       /*type_name_regexp=*/"",
1789 				       /*type_name=*/""));
1790 
1791       // Types that are defined in system headers are usually
1792       // OK to be considered as public types.
1793       suppr->set_source_location_to_keep_regex_str("^/usr/include/");
1794       suppr->set_is_artificial(true);
1795     }
1796 
1797   // And types that are defined in header files that are under
1798   // the header directory file we are looking are to be
1799   // considered public types too.
1800   suppr->get_source_locations_to_keep().insert(file_path);
1801 }
1802 
1803 /// This is a sub-routine of gen_suppr_spec_from_headers.
1804 ///
1805 /// @param entry if this file represents a regular (or symlink) file,
1806 /// then its file name is going to be added to the vector returned by
1807 /// type_suppression::get_source_locations_to_keep().
1808 ///
1809 /// @param if @p entry represents a file, then its file name is going
1810 /// to be added to the vector returned by the method
1811 /// type_suppression::get_source_locations_to_keep of this instance.
1812 /// If this smart pointer is nil then a new instance @ref
1813 /// type_suppression is created and this variable is made to point to
1814 /// it.
1815 static void
handle_fts_entry(const FTSENT * entry,type_suppression_sptr & suppr)1816 handle_fts_entry(const FTSENT *entry,
1817 		 type_suppression_sptr& suppr)
1818 {
1819   if (entry == NULL
1820       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
1821       || entry->fts_info == FTS_ERR
1822       || entry->fts_info == FTS_NS)
1823     return;
1824 
1825   string fname = entry->fts_name;
1826   if (!fname.empty())
1827     {
1828       if (string_ends_with(fname, ".h")
1829 	  || string_ends_with(fname, ".hpp")
1830 	  || string_ends_with(fname, ".hxx"))
1831 	handle_file_entry (fname, suppr);
1832     }
1833 }
1834 
1835 /// Populate a type_supression from header files found in a given
1836 /// directory tree.
1837 ///
1838 /// The suppression suppresses types defined in source files that are
1839 /// *NOT* found in the directory tree.
1840 ///
1841 /// This is a subroutine for gen_suppr_spect_from_headers.
1842 ///
1843 /// @param headers_root_dir the directory tree to consider for header
1844 /// files.
1845 ///
1846 /// @param result the type_supression to populate from the content of
1847 /// @p headers_root_dir.
1848 static void
gen_suppr_spec_from_headers_root_dir(const string & headers_root_dir,type_suppression_sptr & result)1849 gen_suppr_spec_from_headers_root_dir(const string& headers_root_dir,
1850 				     type_suppression_sptr &result)
1851 {
1852   if (!headers_root_dir.empty())
1853     {
1854       char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
1855 
1856       if (FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL))
1857 	{
1858 	  FTSENT *entry;
1859 	  while ((entry = fts_read(file_hierarchy)))
1860 	    handle_fts_entry(entry, result);
1861 	  fts_close(file_hierarchy);
1862 	}
1863     }
1864 }
1865 
1866 /// Generate a type suppression specification that suppresses ABI
1867 /// changes for types defined in source files that are neither in a
1868 /// given set of header root directories nor in a set of header
1869 /// files.
1870 ///
1871 /// @param headers_root_dirs ABI changes in types defined in files
1872 /// *NOT* found in these directory trees are going be suppressed.
1873 ///
1874 /// @param header_files a set of additional header files that define
1875 /// types that are to be kept (not supressed) by the returned type
1876 /// suppression.
1877 ///
1878 /// @return the resulting type suppression generated, if any file was
1879 /// found in the directory tree @p headers_root_dir.
1880 type_suppression_sptr
gen_suppr_spec_from_headers(const vector<string> & headers_root_dirs,const vector<string> & header_files)1881 gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
1882 			    const vector<string>& header_files)
1883 {
1884   type_suppression_sptr result;
1885 
1886   for (vector<string>::const_iterator root_dir = headers_root_dirs.begin();
1887        root_dir != headers_root_dirs.end();
1888        ++root_dir)
1889     gen_suppr_spec_from_headers_root_dir(*root_dir, result);
1890 
1891   for (vector<string>::const_iterator file = header_files.begin();
1892        file != header_files.end();
1893        ++file)
1894     handle_file_entry(*file, result);
1895 
1896   return result;
1897 }
1898 
1899 /// Generate a type suppression specification that suppresses ABI
1900 /// changes for types defined in source files that are neither in a
1901 /// given header root dir, not in a set of header files.
1902 ///
1903 /// @param headers_root_dir ABI changes in types defined in files
1904 /// *NOT* found in this directory tree are going be suppressed.
1905 ///
1906 /// @param header_files a set of additional header files that define
1907 /// types that are to be kept (not supressed) by the returned type
1908 /// suppression.
1909 ///
1910 /// @return the resulting type suppression generated, if any file was
1911 /// found in the directory tree @p headers_root_dir.
1912 type_suppression_sptr
gen_suppr_spec_from_headers(const string & headers_root_dir,const vector<string> & header_files)1913 gen_suppr_spec_from_headers(const string& headers_root_dir,
1914 			    const vector<string>& header_files)
1915 {
1916   type_suppression_sptr result;
1917   vector<string> root_dirs;
1918 
1919   if (!headers_root_dir.empty())
1920     root_dirs.push_back(headers_root_dir);
1921 
1922   return gen_suppr_spec_from_headers(root_dirs, header_files);
1923 }
1924 
1925 /// Generate a type suppression specification that suppresses ABI
1926 /// changes for types defined in source files that are not in a given
1927 /// header root dir.
1928 ///
1929 /// @param headers_root_dir ABI changes in types defined in files
1930 /// *NOT* found in this directory tree are going be suppressed.
1931 ///
1932 /// @return the resulting type suppression generated, if any file was
1933 /// found in the directory tree @p headers_root_dir.
1934 type_suppression_sptr
gen_suppr_spec_from_headers(const string & headers_root_dir)1935 gen_suppr_spec_from_headers(const string& headers_root_dir)
1936 {
1937   // We don't list individual files, just look under the headers_path.
1938   vector<string> header_files;
1939   return gen_suppr_spec_from_headers(headers_root_dir, header_files);
1940 }
1941 
1942 /// Generate a suppression specification from kernel abi whitelist
1943 /// files.
1944 ///
1945 /// A kernel ABI whitelist file is an INI file that usually has only
1946 /// one section.  The name of the section is a string that ends up
1947 /// with the sub-string "symbol_list" or "whitelist".  For instance
1948 /// RHEL7_x86_64_symbol_list.
1949 ///
1950 /// Then the content of the section is a set of function or variable
1951 /// names, one name per line.  Each function or variable name is the
1952 /// name of a function or a variable whose changes are to be keept.
1953 ///
1954 /// A whitelist file can have multiple sections (adhering to the naming
1955 /// conventions and multiple files can be passed. The suppression that
1956 /// is created takes all whitelist sections from all files into account.
1957 /// Symbols (or expression of such) are deduplicated in the final
1958 /// suppression expression.
1959 ///
1960 /// This function reads the white lists and generates a
1961 /// function_suppression_sptr and variable_suppression_sptr and returns
1962 /// a vector containing those.
1963 ///
1964 /// @param abi_whitelist_paths a vector of KMI whitelist paths
1965 ///
1966 /// @return a vector or suppressions
1967 suppressions_type
gen_suppr_spec_from_kernel_abi_whitelists(const std::vector<std::string> & abi_whitelist_paths)1968 gen_suppr_spec_from_kernel_abi_whitelists
1969    (const std::vector<std::string>& abi_whitelist_paths)
1970 {
1971 
1972   std::vector<std::string> whitelisted_names;
1973   for (std::vector<std::string>::const_iterator
1974 	   path_iter = abi_whitelist_paths.begin(),
1975 	   path_end = abi_whitelist_paths.end();
1976        path_iter != path_end;
1977        ++path_iter)
1978     {
1979 
1980       abigail::ini::config whitelist;
1981       if (!read_config(*path_iter, whitelist))
1982 	continue;
1983 
1984       const ini::config::sections_type& whitelist_sections =
1985 	  whitelist.get_sections();
1986 
1987       for (ini::config::sections_type::const_iterator
1988 	       section_iter = whitelist_sections.begin(),
1989 	       section_end = whitelist_sections.end();
1990 	   section_iter != section_end;
1991 	   ++section_iter)
1992 	{
1993 	  std::string section_name = (*section_iter)->get_name();
1994 	  if (!string_ends_with(section_name, "symbol_list")
1995 	      && !string_ends_with(section_name, "whitelist"))
1996 	    continue;
1997 	  for (ini::config::properties_type::const_iterator
1998 		   prop_iter = (*section_iter)->get_properties().begin(),
1999 		   prop_end = (*section_iter)->get_properties().end();
2000 	       prop_iter != prop_end;
2001 	       ++prop_iter)
2002 	    {
2003 	      if (const simple_property_sptr& prop =
2004 		      is_simple_property(*prop_iter))
2005 		if (prop->has_empty_value())
2006 		  {
2007 		    const std::string& name = prop->get_name();
2008 		    if (!name.empty())
2009 		      whitelisted_names.push_back(name);
2010 		  }
2011 	    }
2012 	}
2013     }
2014 
2015   suppressions_type result;
2016   if (!whitelisted_names.empty())
2017     {
2018       // Drop duplicates to simplify the regex we are generating
2019       std::sort(whitelisted_names.begin(), whitelisted_names.end());
2020       whitelisted_names.erase(std::unique(whitelisted_names.begin(),
2021 					  whitelisted_names.end()),
2022 			      whitelisted_names.end());
2023 
2024       // Build a regular expression representing the union of all
2025       // the function and variable names expressed in the white list.
2026       const std::string regex = regex::generate_from_strings(whitelisted_names);
2027 
2028       // Build a suppression specification which *keeps* functions
2029       // whose ELF symbols match the regular expression contained
2030       // in function_names_regexp.  This will also keep the ELF
2031       // symbols (not designated by any debug info) whose names
2032       // match this regexp.
2033       function_suppression_sptr fn_suppr(new function_suppression);
2034       fn_suppr->set_label("whitelist");
2035       fn_suppr->set_symbol_name_not_regex_str(regex);
2036       fn_suppr->set_drops_artifact_from_ir(true);
2037       result.push_back(fn_suppr);
2038 
2039       // Build a suppression specification which *keeps* variables
2040       // whose ELF symbols match the regular expression contained
2041       // in function_names_regexp.  This will also keep the ELF
2042       // symbols (not designated by any debug info) whose names
2043       // match this regexp.
2044       variable_suppression_sptr var_suppr(new variable_suppression);
2045       var_suppr->set_label("whitelist");
2046       var_suppr->set_symbol_name_not_regex_str(regex);
2047       var_suppr->set_drops_artifact_from_ir(true);
2048       result.push_back(var_suppr);
2049     }
2050   return result;
2051 }
2052 
2053 /// Get the path to the default system suppression file.
2054 ///
2055 /// @return a copy of the default system suppression file.
2056 string
get_default_system_suppression_file_path()2057 get_default_system_suppression_file_path()
2058 {
2059   string default_system_suppr_path;
2060 
2061   const char *s = getenv("LIBABIGAIL_DEFAULT_SYSTEM_SUPPRESSION_FILE");
2062   if (s)
2063     default_system_suppr_path = s;
2064 
2065   if (default_system_suppr_path.empty())
2066     default_system_suppr_path =
2067       get_system_libdir() + string("/libabigail/default.abignore");
2068 
2069   return default_system_suppr_path;
2070 }
2071 
2072 /// Get the path to the default user suppression file.
2073 ///
2074 /// @return a copy of the default user suppression file.
2075 string
get_default_user_suppression_file_path()2076 get_default_user_suppression_file_path()
2077 {
2078   string default_user_suppr_path;
2079   const char *s = getenv("LIBABIGAIL_DEFAULT_USER_SUPPRESSION_FILE");
2080 
2081   if (s == NULL)
2082     {
2083       s = getenv("HOME");
2084       if (s == NULL)
2085 	return "";
2086       default_user_suppr_path  = s;
2087       if (default_user_suppr_path.empty())
2088 	default_user_suppr_path = "~";
2089       default_user_suppr_path += "/.abignore";
2090     }
2091   else
2092     default_user_suppr_path = s;
2093 
2094   return default_user_suppr_path;
2095 }
2096 
2097 /// Load the default system suppression specification file and
2098 /// populate a vector of @ref suppression_sptr with its content.
2099 ///
2100 /// The default system suppression file is located at
2101 /// $libdir/libabigail/default-libabigail.abignore.
2102 ///
2103 /// @param supprs the vector to add the suppression specifications
2104 /// read from the file to.
2105 void
load_default_system_suppressions(suppr::suppressions_type & supprs)2106 load_default_system_suppressions(suppr::suppressions_type& supprs)
2107 {
2108   string default_system_suppr_path =
2109     get_default_system_suppression_file_path();
2110 
2111   read_suppressions(default_system_suppr_path, supprs);
2112 }
2113 
2114 /// Load the default user suppression specification file and populate
2115 /// a vector of @ref suppression_sptr with its content.
2116 ///
2117 /// The default user suppression file is located at $HOME~/.abignore.
2118 ///
2119 /// @param supprs the vector to add the suppression specifications
2120 /// read from the file to.
2121 void
load_default_user_suppressions(suppr::suppressions_type & supprs)2122 load_default_user_suppressions(suppr::suppressions_type& supprs)
2123 {
2124   string default_user_suppr_path =
2125     get_default_user_suppression_file_path();
2126 
2127   read_suppressions(default_user_suppr_path, supprs);
2128 }
2129 
2130 /// Test if a given FTSENT* denotes a file with a given name.
2131 ///
2132 /// @param entry the FTSENT* to consider.
2133 ///
2134 /// @param fname the file name (or end of path) to consider.
2135 ///
2136 /// @return true iff @p entry denotes a file which path ends with @p
2137 /// fname.
2138 static bool
entry_of_file_with_name(const FTSENT * entry,const string & fname)2139 entry_of_file_with_name(const FTSENT *entry,
2140 			const string& fname)
2141 {
2142   if (entry == NULL
2143       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2144       || entry->fts_info == FTS_ERR
2145       || entry->fts_info == FTS_NS)
2146     return false;
2147 
2148   string fpath = ::basename(entry->fts_path);
2149   if (fpath == fname)
2150     return true;
2151   return false;
2152 }
2153 
2154 /// Find a given file under a root directory and return its absolute
2155 /// path.
2156 ///
2157 /// @param root_dir the root directory under which to look for.
2158 ///
2159 /// @param file_path_to_look_for the file to look for under the
2160 /// directory @p root_dir.
2161 ///
2162 /// @param result the resulting path to @p file_path_to_look_for.
2163 /// This is set iff the file has been found.
2164 bool
find_file_under_dir(const string & root_dir,const string & file_path_to_look_for,string & result)2165 find_file_under_dir(const string& root_dir,
2166 		    const string& file_path_to_look_for,
2167 		    string& result)
2168 {
2169   char* paths[] = {const_cast<char*>(root_dir.c_str()), 0};
2170 
2171   FTS *file_hierarchy = fts_open(paths,
2172 				 FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2173   if (!file_hierarchy)
2174     return false;
2175 
2176   FTSENT *entry;
2177   while ((entry = fts_read(file_hierarchy)))
2178     {
2179       // Skip descendents of symbolic links.
2180       if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2181 	{
2182 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2183 	  continue;
2184 	}
2185       if (entry_of_file_with_name(entry, file_path_to_look_for))
2186 	{
2187 	  result = entry->fts_path;
2188 	  return true;
2189 	}
2190     }
2191 
2192   fts_close(file_hierarchy);
2193   return false;
2194 }
2195 /// If we were given suppression specification files or kabi whitelist
2196 /// files, this function parses those, come up with suppression
2197 /// specifications as a result, and set them to the read context.
2198 ///
2199 /// @param read_ctxt the read context to consider.
2200 ///
2201 /// @param suppr_paths paths to suppression specification files that
2202 /// we were given.  If empty, it means we were not given any
2203 /// suppression specification path.
2204 ///
2205 /// @param kabi_whitelist_paths paths to kabi whitelist files that we
2206 /// were given.  If empty, it means we were not given any kabi
2207 /// whitelist.
2208 ///
2209 /// @param supprs the suppressions specifications resulting from
2210 /// parsing the suppression specification files at @p suppr_paths and
2211 /// the kabi whitelist at @p kabi_whitelist_paths.
2212 ///
2213 /// @param opts the options to consider.
2214 static void
load_generate_apply_suppressions(dwarf_reader::read_context & read_ctxt,vector<string> & suppr_paths,vector<string> & kabi_whitelist_paths,suppressions_type & supprs)2215 load_generate_apply_suppressions(dwarf_reader::read_context &read_ctxt,
2216 				 vector<string>& suppr_paths,
2217 				 vector<string>& kabi_whitelist_paths,
2218 				 suppressions_type& supprs)
2219 {
2220   if (supprs.empty())
2221     {
2222       for (vector<string>::const_iterator i = suppr_paths.begin();
2223 	   i != suppr_paths.end();
2224 	   ++i)
2225 	read_suppressions(*i, supprs);
2226 
2227       const suppressions_type& wl_suppr =
2228 	  gen_suppr_spec_from_kernel_abi_whitelists(kabi_whitelist_paths);
2229 
2230       supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
2231     }
2232 
2233   abigail::dwarf_reader::add_read_context_suppressions(read_ctxt, supprs);
2234 }
2235 
2236 /// Test if an FTSENT pointer (resulting from fts_read) represents the
2237 /// vmlinux binary.
2238 ///
2239 /// @param entry the FTSENT to consider.
2240 ///
2241 /// @return true iff @p entry is for a vmlinux binary.
2242 static bool
is_vmlinux(const FTSENT * entry)2243 is_vmlinux(const FTSENT *entry)
2244 {
2245   if (entry == NULL
2246       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2247       || entry->fts_info == FTS_ERR
2248       || entry->fts_info == FTS_NS)
2249     return false;
2250 
2251   string fname = entry->fts_name;
2252 
2253   if (fname == "vmlinux")
2254     {
2255       string dirname;
2256       dir_name(entry->fts_path, dirname);
2257       if (string_ends_with(dirname, "compressed"))
2258 	return false;
2259 
2260       return true;
2261     }
2262 
2263   return false;
2264 }
2265 
2266 /// Test if an FTSENT pointer (resulting from fts_read) represents a a
2267 /// linux kernel module binary.
2268 ///
2269 /// @param entry the FTSENT to consider.
2270 ///
2271 /// @return true iff @p entry is for a linux kernel module binary.
2272 static bool
is_kernel_module(const FTSENT * entry)2273 is_kernel_module(const FTSENT *entry)
2274 {
2275     if (entry == NULL
2276       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2277       || entry->fts_info == FTS_ERR
2278       || entry->fts_info == FTS_NS)
2279     return false;
2280 
2281   string fname = entry->fts_name;
2282   if (string_ends_with(fname, ".ko")
2283       || string_ends_with(fname, ".ko.xz")
2284       || string_ends_with(fname, ".ko.gz"))
2285     return true;
2286 
2287   return false;
2288 }
2289 
2290 /// Find a vmlinux and its kernel modules in a given directory tree.
2291 ///
2292 /// @param from the directory tree to start looking from.
2293 ///
2294 /// @param vmlinux_path output parameter.  This is set to the path
2295 /// where the vmlinux binary is found.  This is set iff the returns
2296 /// true and if this argument was empty to begin with.
2297 ///
2298 /// @param module_paths output parameter.  This is set to the paths of
2299 /// the linux kernel module binaries.
2300 ///
2301 /// @return true iff at least the vmlinux binary was found.
2302 static bool
find_vmlinux_and_module_paths(const string & from,string & vmlinux_path,vector<string> & module_paths)2303 find_vmlinux_and_module_paths(const string&	from,
2304 			      string		&vmlinux_path,
2305 			      vector<string>	&module_paths)
2306 {
2307   char* path[] = {const_cast<char*>(from.c_str()), 0};
2308 
2309   FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2310   if (!file_hierarchy)
2311     return false;
2312 
2313   bool found_vmlinux = !vmlinux_path.empty();
2314   FTSENT *entry;
2315   while ((entry = fts_read(file_hierarchy)))
2316     {
2317       // Skip descendents of dead symbolic links.
2318       if (entry->fts_info == FTS_SLNONE)
2319 	{
2320 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2321 	  continue;
2322 	}
2323 
2324       if (!found_vmlinux && is_vmlinux(entry))
2325 	{
2326 	  vmlinux_path = entry->fts_path;
2327 	  found_vmlinux = true;
2328 	}
2329       else if (is_kernel_module(entry))
2330 	module_paths.push_back(entry->fts_path);
2331     }
2332 
2333   fts_close(file_hierarchy);
2334 
2335   return found_vmlinux;
2336 }
2337 
2338 /// Find a vmlinux binary in a given directory tree.
2339 ///
2340 /// @param from the directory tree to start looking from.
2341 ///
2342 /// @param vmlinux_path output parameter
2343 ///
2344 /// return true iff the vmlinux binary was found
2345 static bool
find_vmlinux_path(const string & from,string & vmlinux_path)2346 find_vmlinux_path(const string&	from,
2347 		  string		&vmlinux_path)
2348 {
2349   char* path[] = {const_cast<char*>(from.c_str()), 0};
2350 
2351   FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2352   if (!file_hierarchy)
2353     return false;
2354 
2355   bool found_vmlinux = false;
2356   FTSENT *entry;
2357   while ((entry = fts_read(file_hierarchy)))
2358     {
2359       // Skip descendents of symbolic links.
2360       if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2361 	{
2362 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2363 	  continue;
2364 	}
2365 
2366       if (!found_vmlinux && is_vmlinux(entry))
2367 	{
2368 	  vmlinux_path = entry->fts_path;
2369 	  found_vmlinux = true;
2370 	  break;
2371 	}
2372     }
2373 
2374   fts_close(file_hierarchy);
2375 
2376   return found_vmlinux;
2377 }
2378 
2379 /// Get the paths of the vmlinux and kernel module binaries under
2380 /// given directory.
2381 ///
2382 /// @param dist_root the directory under which to look for.
2383 ///
2384 /// @param debug_info_root_path the path to the directory under which
2385 /// debug info is going to be found for binaries under @p dist_root.
2386 ///
2387 /// @param vmlinux_path output parameter.  The path of the vmlinux
2388 /// binary that was found.
2389 ///
2390 /// @param module_paths output parameter.  The paths of the kernel
2391 /// module binaries that were found, sorted to impose a deterministic
2392 /// ordering.
2393 ///
2394 /// @return true if at least the path to the vmlinux binary was found.
2395 bool
get_binary_paths_from_kernel_dist(const string & dist_root,const string & debug_info_root_path,string & vmlinux_path,vector<string> & module_paths)2396 get_binary_paths_from_kernel_dist(const string&	dist_root,
2397 				  const string&	debug_info_root_path,
2398 				  string&		vmlinux_path,
2399 				  vector<string>&	module_paths)
2400 {
2401   if (!dir_exists(dist_root))
2402     return false;
2403 
2404   // For now, we assume either an Enterprise Linux or a Fedora kernel
2405   // distribution directory.
2406   //
2407   // We also take into account split debug info package for these.  In
2408   // this case, the content split debug info package is installed
2409   // under the 'debug_info_root_path' directory and its content is
2410   // accessible from <debug_info_root_path>/usr/lib/debug directory.
2411 
2412   string kernel_modules_root;
2413   string debug_info_root;
2414   if (dir_exists(dist_root + "/lib/modules"))
2415     {
2416       dist_root + "/lib/modules";
2417       debug_info_root = debug_info_root_path.empty()
2418 	? dist_root
2419 	: debug_info_root_path;
2420       debug_info_root += "/usr/lib/debug";
2421     }
2422 
2423   if (dir_is_empty(debug_info_root))
2424     debug_info_root.clear();
2425 
2426   bool found = false;
2427   string from = dist_root;
2428   if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
2429     found = true;
2430 
2431   std::sort(module_paths.begin(), module_paths.end());
2432 
2433   return found;
2434 }
2435 
2436 /// Get the path of the vmlinux binary under the given directory, that
2437 /// must have been generated either from extracting a package.
2438 ///
2439 /// @param from the directory under which to look for.
2440 ///
2441 /// @param vmlinux_path output parameter.  The path of the vmlinux
2442 /// binary that was found.
2443 ///
2444 /// @return true if the path to the vmlinux binary was found.
2445 bool
get_vmlinux_path_from_kernel_dist(const string & from,string & vmlinux_path)2446 get_vmlinux_path_from_kernel_dist(const string&	from,
2447 				  string&		vmlinux_path)
2448 {
2449     if (!dir_exists(from))
2450     return false;
2451 
2452   // For now, we assume the possibility of having either an Enterprise
2453   // Linux or a Fedora kernel distribution directory.  In those cases,
2454   // the vmlinux binary is located under the /lib/modules
2455   // sub-directory.  So we might as well save some time by picking it
2456   // from there directly.
2457 
2458     string dist_root = from;
2459   if (dir_exists(dist_root + "/lib/modules"))
2460     dist_root += "/lib/modules";
2461 
2462   bool found = false;
2463   if (find_vmlinux_path(dist_root, vmlinux_path))
2464     found = true;
2465 
2466   return found;
2467 }
2468 
2469 /// Get the paths of the vmlinux and kernel module binaries under
2470 /// given directory.
2471 ///
2472 /// @param dist_root the directory under which to look for.
2473 ///
2474 /// @param vmlinux_path output parameter.  The path of the vmlinux
2475 /// binary that was found.
2476 ///
2477 /// @param module_paths output parameter.  The paths of the kernel
2478 /// module binaries that were found.
2479 ///
2480 /// @return true if at least the path to the vmlinux binary was found.
2481 bool
get_binary_paths_from_kernel_dist(const string & dist_root,string & vmlinux_path,vector<string> & module_paths)2482 get_binary_paths_from_kernel_dist(const string&	dist_root,
2483 				  string&		vmlinux_path,
2484 				  vector<string>&	module_paths)
2485 {
2486   string debug_info_root_path;
2487   return get_binary_paths_from_kernel_dist(dist_root,
2488 					   debug_info_root_path,
2489 					   vmlinux_path,
2490 					   module_paths);
2491 }
2492 
2493 /// If the @ref origin is DWARF_ORIGIN it build a @ref corpus_group
2494 /// made of vmlinux kernel file and the linux kernel modules found
2495 /// under @p root directory and under its sub-directories, recursively.
2496 ///
2497 /// @param origin the debug type information in vmlinux kernel and
2498 /// the linux kernel modules to be used to build the corpora @p group.
2499 ///
2500 /// @param the group @ref corpus_group to be built.
2501 ///
2502 /// @param vmlinux the path to the vmlinux binary.
2503 ///
2504 /// @param modules a vector with the paths to the linux kernel
2505 /// modules.
2506 ///
2507 /// @param root the path of the directory under which the kernel
2508 /// kernel modules were found.
2509 ///
2510 /// @param di_root the directory in aboslute path which debug
2511 /// info is to be found for binaries under director @p root
2512 ///
2513 /// @param suppr_paths the paths to the suppression specifications to
2514 /// apply while loading the binaries.
2515 ///
2516 /// @param kabi_wl_path the paths to the kabi whitelist files to take
2517 /// into account while loading the binaries.
2518 ///
2519 /// @param supprs the suppressions resulting from parsing the
2520 /// suppression specifications at @p suppr_paths.  This is set by this
2521 /// function.
2522 ///
2523 /// @param verbose true if the function has to emit some verbose
2524 /// messages.
2525 ///
2526 /// @param t time to trace time spent in each step.
2527 ///
2528 /// @param env the environment to create the corpus_group in.
2529 static void
maybe_load_vmlinux_dwarf_corpus(corpus::origin origin,corpus_group_sptr & group,const string & vmlinux,vector<string> & modules,const string & root,vector<char ** > & di_roots,vector<string> & suppr_paths,vector<string> & kabi_wl_paths,suppressions_type & supprs,bool verbose,timer & t,environment_sptr & env)2530 maybe_load_vmlinux_dwarf_corpus(corpus::origin      origin,
2531                                 corpus_group_sptr&  group,
2532                                 const string&       vmlinux,
2533                                 vector<string>&     modules,
2534                                 const string&       root,
2535                                 vector<char**>&     di_roots,
2536                                 vector<string>&     suppr_paths,
2537                                 vector<string>&     kabi_wl_paths,
2538                                 suppressions_type&  supprs,
2539                                 bool                verbose,
2540                                 timer&              t,
2541                                 environment_sptr&   env)
2542 {
2543   if (!(origin & corpus::DWARF_ORIGIN))
2544     return;
2545 
2546   abigail::elf_reader::status status = abigail::elf_reader::STATUS_OK;
2547   dwarf_reader::read_context_sptr ctxt;
2548   ctxt =
2549    dwarf_reader::create_read_context(vmlinux, di_roots, env.get(),
2550                                      /*read_all_types=*/false,
2551                                      /*linux_kernel_mode=*/true);
2552   dwarf_reader::set_do_log(*ctxt, verbose);
2553 
2554   t.start();
2555   load_generate_apply_suppressions(*ctxt, suppr_paths,
2556                                    kabi_wl_paths, supprs);
2557   t.stop();
2558 
2559   if (verbose)
2560     std::cerr << "loaded white list and generated suppr spec in: "
2561      << t
2562      << "\n";
2563 
2564   group.reset(new corpus_group(env.get(), root));
2565 
2566   set_read_context_corpus_group(*ctxt, group);
2567 
2568   if (verbose)
2569     std::cerr << "reading kernel binary '"
2570      << vmlinux << "' ...\n" << std::flush;
2571 
2572   // Read the vmlinux corpus and add it to the group.
2573   t.start();
2574   read_and_add_corpus_to_group_from_elf(*ctxt, *group, status);
2575   t.stop();
2576 
2577   if (verbose)
2578     std::cerr << vmlinux
2579      << " reading DONE:"
2580      << t << "\n";
2581 
2582   if (group->is_empty())
2583     return;
2584 
2585   // Now add the corpora of the modules to the corpus group.
2586   int total_nb_modules = modules.size();
2587   int cur_module_index = 1;
2588   for (vector<string>::const_iterator m = modules.begin();
2589        m != modules.end();
2590        ++m, ++cur_module_index)
2591     {
2592       if (verbose)
2593         std::cerr << "reading module '"
2594          << *m << "' ("
2595          << cur_module_index
2596          << "/" << total_nb_modules
2597          << ") ... " << std::flush;
2598 
2599       reset_read_context(ctxt, *m, di_roots, env.get(),
2600                          /*read_all_types=*/false,
2601                          /*linux_kernel_mode=*/true);
2602 
2603       load_generate_apply_suppressions(*ctxt, suppr_paths,
2604                                        kabi_wl_paths, supprs);
2605 
2606       set_read_context_corpus_group(*ctxt, group);
2607 
2608       t.start();
2609       read_and_add_corpus_to_group_from_elf(*ctxt,
2610                                             *group, status);
2611       t.stop();
2612       if (verbose)
2613         std::cerr << "module '"
2614          << *m
2615          << "' reading DONE: "
2616          << t << "\n";
2617     }
2618 }
2619 
2620 /// If the @ref origin is CTF_ORIGIN it build a @ref corpus_group
2621 /// made of vmlinux kernel file and the linux kernel modules found
2622 /// under @p root directory and under its sub-directories, recursively.
2623 ///
2624 /// @param origin the debug type information in vmlinux kernel and
2625 /// the linux kernel modules to be used to build the corpora @p group.
2626 ///
2627 /// @param group the @ref corpus_group to be built.
2628 ///
2629 /// @param vmlinux the path to the vmlinux binary.
2630 ///
2631 /// @param modules a vector with the paths to the linux kernel
2632 /// modules.
2633 ///
2634 /// @param root the path of the directory under which the kernel
2635 /// kernel modules were found.
2636 ///
2637 /// @param di_root the directory in aboslute path which debug
2638 /// info is to be found for binaries under director @p root
2639 ///
2640 /// @param verbose true if the function has to emit some verbose
2641 /// messages.
2642 ///
2643 /// @param t time to trace time spent in each step.
2644 ///
2645 /// @param env the environment to create the corpus_group in.
2646 #ifdef WITH_CTF
2647 static void
maybe_load_vmlinux_ctf_corpus(corpus::origin origin,corpus_group_sptr & group,const string & vmlinux,vector<string> & modules,const string & root,vector<char ** > & di_roots,bool verbose,timer & t,environment_sptr & env)2648 maybe_load_vmlinux_ctf_corpus(corpus::origin	  origin,
2649                               corpus_group_sptr&  group,
2650                               const string&       vmlinux,
2651                               vector<string>&     modules,
2652                               const string&       root,
2653                               vector<char**>&     di_roots,
2654                               bool                verbose,
2655                               timer&              t,
2656                               environment_sptr&   env)
2657 {
2658   if (!(origin & corpus::CTF_ORIGIN))
2659     return;
2660 
2661   abigail::elf_reader::status status = abigail::elf_reader::STATUS_OK;
2662   ctf_reader::read_context_sptr ctxt;
2663   ctxt = ctf_reader::create_read_context(vmlinux, di_roots, env.get());
2664   group.reset(new corpus_group(env.get(), root));
2665   set_read_context_corpus_group(*ctxt, group);
2666 
2667   if (verbose)
2668     std::cerr << "reading kernel binary '"
2669      << vmlinux << "' ...\n" << std::flush;
2670 
2671   // Read the vmlinux corpus and add it to the group.
2672   t.start();
2673   read_and_add_corpus_to_group_from_elf(ctxt.get(), *group, status);
2674   t.stop();
2675 
2676   if (verbose)
2677     std::cerr << vmlinux
2678      << " reading DONE:"
2679      << t << "\n";
2680 
2681   if (group->is_empty())
2682     return;
2683 
2684   // Now add the corpora of the modules to the corpus group.
2685   int total_nb_modules = modules.size();
2686   int cur_module_index = 1;
2687   for (vector<string>::const_iterator m = modules.begin();
2688        m != modules.end();
2689        ++m, ++cur_module_index)
2690     {
2691       if (verbose)
2692         std::cerr << "reading module '"
2693          << *m << "' ("
2694          << cur_module_index
2695          << "/" << total_nb_modules
2696          << ") ... " << std::flush;
2697 
2698       reset_read_context(ctxt, *m, di_roots, env.get());
2699       set_read_context_corpus_group(*ctxt, group);
2700 
2701       t.start();
2702       read_and_add_corpus_to_group_from_elf(ctxt.get(),
2703                                             *group, status);
2704       t.stop();
2705       if (verbose)
2706         std::cerr << "module '"
2707          << *m
2708          << "' reading DONE: "
2709          << t << "\n";
2710     }
2711 }
2712 #endif
2713 
2714 /// Walk a given directory and build an instance of @ref corpus_group
2715 /// from the vmlinux kernel binary and the linux kernel modules found
2716 /// under that directory and under its sub-directories, recursively.
2717 ///
2718 /// The main corpus of the @ref corpus_group is made of the vmlinux
2719 /// binary.  The other corpora are made of the linux kernel binaries.
2720 ///
2721 /// Depending of the @ref origin it delegates the corpus build @p group
2722 /// to:
2723 ///     @ref maybe_load_vmlinux_dwarf_corpus
2724 ///     @ref maybe_load_vmlinux_ctf_corpus
2725 ///
2726 /// @param root the path of the directory under which the kernel
2727 /// kernel modules are to be found.  The vmlinux can also be found
2728 /// somewhere under that directory, but if it's not in there, its path
2729 /// can be set to the @p vmlinux_path parameter.
2730 ///
2731 /// @param debug_info_root the directory under which debug info is to
2732 /// be found for binaries under director @p root.
2733 ///
2734 /// @param vmlinux_path the path to the vmlinux binary, if that binary
2735 /// is not under the @p root directory.  If this is empty, then it
2736 /// means the vmlinux binary is to be found under the @p root
2737 /// directory.
2738 ///
2739 /// @param suppr_paths the paths to the suppression specifications to
2740 /// apply while loading the binaries.
2741 ///
2742 /// @param kabi_wl_path the paths to the kabi whitelist files to take
2743 /// into account while loading the binaries.
2744 ///
2745 /// @param supprs the suppressions resulting from parsing the
2746 /// suppression specifications at @p suppr_paths.  This is set by this
2747 /// function.
2748 ///
2749 /// @param verbose true if the function has to emit some verbose
2750 /// messages.
2751 ///
2752 /// @param env the environment to create the corpus_group in.
2753 corpus_group_sptr
build_corpus_group_from_kernel_dist_under(const string & root,const string debug_info_root,const string & vmlinux_path,vector<string> & suppr_paths,vector<string> & kabi_wl_paths,suppressions_type & supprs,bool verbose,environment_sptr & env,corpus::origin origin)2754 build_corpus_group_from_kernel_dist_under(const string&	root,
2755 					  const string		debug_info_root,
2756 					  const string&	vmlinux_path,
2757 					  vector<string>&	suppr_paths,
2758 					  vector<string>&	kabi_wl_paths,
2759 					  suppressions_type&	supprs,
2760 					  bool			verbose,
2761 					  environment_sptr&	env,
2762 					  corpus::origin	origin)
2763 {
2764   string vmlinux = vmlinux_path;
2765   corpus_group_sptr group;
2766   vector<string> modules;
2767 
2768   if (verbose)
2769     std::cerr << "Analysing kernel dist root '"
2770 	      << root << "' ... " << std::flush;
2771 
2772   timer t;
2773 
2774   t.start();
2775   bool got_binary_paths =
2776     get_binary_paths_from_kernel_dist(root, debug_info_root, vmlinux, modules);
2777   t.stop();
2778 
2779   if (verbose)
2780     std::cerr << "DONE: " << t << "\n";
2781 
2782   if (got_binary_paths)
2783     {
2784       shared_ptr<char> di_root =
2785 	make_path_absolute(debug_info_root.c_str());
2786       char *di_root_ptr = di_root.get();
2787       vector<char**> di_roots;
2788       di_roots.push_back(&di_root_ptr);
2789 
2790       maybe_load_vmlinux_dwarf_corpus(origin, group, vmlinux,
2791                                       modules, root, di_roots,
2792                                       suppr_paths, kabi_wl_paths,
2793                                       supprs, verbose, t, env);
2794 #ifdef WITH_CTF
2795       maybe_load_vmlinux_ctf_corpus(origin, group, vmlinux,
2796                                     modules, root, di_roots,
2797                                     verbose, t, env);
2798 #endif
2799     }
2800 
2801   return group;
2802 }
2803 
2804 }//end namespace tools_utils
2805 
2806 using abigail::ir::function_decl;
2807 
2808 /// Dump (to the standard error stream) two sequences of strings where
2809 /// each string represent one of the functions in the two sequences of
2810 /// functions given in argument to this function.
2811 ///
2812 /// @param a_begin the begin iterator for the first input sequence of
2813 /// functions.
2814 ///
2815 /// @parm a_end the end iterator for the first input sequence of
2816 /// functions.
2817 ///
2818 /// @param b_begin the begin iterator for the second input sequence of
2819 /// functions.
2820 ///
2821 /// @param b_end the end iterator for the second input sequence of functions.
2822 void
dump_functions_as_string(std::vector<function_decl * >::const_iterator a_begin,std::vector<function_decl * >::const_iterator a_end,std::vector<function_decl * >::const_iterator b_begin,std::vector<function_decl * >::const_iterator b_end)2823 dump_functions_as_string(std::vector<function_decl*>::const_iterator a_begin,
2824 			 std::vector<function_decl*>::const_iterator a_end,
2825 			 std::vector<function_decl*>::const_iterator b_begin,
2826 			 std::vector<function_decl*>::const_iterator b_end)
2827 {abigail::fns_to_str(a_begin, a_end, b_begin, b_end, std::cerr);}
2828 
2829 /// Dump (to the standard error output stream) a pretty representation
2830 /// of the signatures of two sequences of functions.
2831 ///
2832 /// @param a_begin the start iterator of the first input sequence of functions.
2833 ///
2834 /// @param a_end the end iterator of the first input sequence of functions.
2835 ///
2836 /// @param b_begin the start iterator of the second input sequence of functions.
2837 ///
2838 /// @param b_end the end iterator of the second input sequence of functions.
2839 void
dump_function_names(std::vector<function_decl * >::const_iterator a_begin,std::vector<function_decl * >::const_iterator a_end,std::vector<function_decl * >::const_iterator b_begin,std::vector<function_decl * >::const_iterator b_end)2840 dump_function_names(std::vector<function_decl*>::const_iterator a_begin,
2841 		    std::vector<function_decl*>::const_iterator a_end,
2842 		    std::vector<function_decl*>::const_iterator b_begin,
2843 		    std::vector<function_decl*>::const_iterator b_end)
2844 {
2845   std::vector<function_decl*>::const_iterator i;
2846   std::ostream& o = std::cerr;
2847   for (i = a_begin; i != a_end; ++i)
2848     o << (*i)->get_pretty_representation() << "\n";
2849 
2850   o << "  ->|<-  \n";
2851   for (i = b_begin; i != b_end; ++i)
2852     o << (*i)->get_pretty_representation() << "\n";
2853   o << "\n";
2854 }
2855 
2856 /// Compare two functions that are in a vector of functions.
2857 ///
2858 /// @param an iterator to the beginning of the the sequence of functions.
2859 ///
2860 /// @param f1_index the index of the first function to compare.
2861 ///
2862 /// @param f2_inde the index of the second function to compare
2863 bool
compare_functions(vector<function_decl * >::const_iterator base,unsigned f1_index,unsigned f2_index)2864 compare_functions(vector<function_decl*>::const_iterator base,
2865 		  unsigned f1_index, unsigned f2_index)
2866 {
2867   function_decl* fn1 = base[f1_index];
2868   function_decl* fn2 = base[f2_index];
2869 
2870   return *fn1 == *fn2;
2871 }
2872 
2873 }//end namespace abigail
2874