- Implemented a copy_dir_content (variant of copy_dir)
[platform/upstream/libzypp.git] / zypp / PathInfo.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PathInfo.cc
10  *
11 */
12
13 #include <iostream>
14 #include <fstream>
15 #include <iomanip>
16
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/String.h"
19 #include "zypp/base/IOStream.h"
20
21 #include "zypp/ExternalProgram.h"
22 #include "zypp/PathInfo.h"
23 #include "zypp/Digest.h"
24
25 #include <sys/types.h> // for ::minor, ::major macros
26
27 using std::string;
28
29 ///////////////////////////////////////////////////////////////////
30 namespace zypp
31 { /////////////////////////////////////////////////////////////////
32   ///////////////////////////////////////////////////////////////////
33   namespace filesystem
34   { /////////////////////////////////////////////////////////////////
35
36     /******************************************************************
37      **
38      ** FUNCTION NAME : operator<<
39      ** FUNCTION TYPE : std::ostream &
40     */
41     std::ostream & operator<<( std::ostream & str, FileType obj )
42     {
43       switch ( obj ) {
44 #define EMUMOUT(T) case T: return str << #T; break
45         EMUMOUT( FT_NOT_AVAIL );
46         EMUMOUT( FT_NOT_EXIST );
47         EMUMOUT( FT_FILE );
48         EMUMOUT( FT_DIR );
49         EMUMOUT( FT_CHARDEV );
50         EMUMOUT( FT_BLOCKDEV );
51         EMUMOUT( FT_FIFO );
52         EMUMOUT( FT_LINK );
53         EMUMOUT( FT_SOCKET );
54 #undef EMUMOUT
55       }
56       return str;
57     }
58
59     ///////////////////////////////////////////////////////////////////
60     //
61     //  METHOD NAME : StatMode::fileType
62     //  METHOD TYPE : FileType
63     //
64     FileType StatMode::fileType() const
65     {
66       if ( isFile() )
67         return FT_FILE;
68       if ( isDir() )
69         return FT_DIR;
70       if ( isLink() )
71         return FT_LINK;
72       if ( isChr() )
73         return FT_CHARDEV;
74       if ( isBlk() )
75         return FT_BLOCKDEV;
76       if ( isFifo() )
77         return FT_FIFO;
78       if ( isSock() )
79         return FT_SOCKET ;
80
81       return FT_NOT_AVAIL;
82     }
83
84     /******************************************************************
85      **
86      ** FUNCTION NAME : operator<<
87      ** FUNCTION TYPE : std::ostream &
88     */
89     std::ostream & operator<<( std::ostream & str, const StatMode & obj )
90     {
91       iostr::IosFmtFlagsSaver autoResoreState( str );
92
93       char t = '?';
94       if ( obj.isFile() )
95         t = '-';
96       else if ( obj.isDir() )
97         t = 'd';
98       else if ( obj.isLink() )
99         t = 'l';
100       else if ( obj.isChr() )
101         t = 'c';
102       else if ( obj.isBlk() )
103         t = 'b';
104       else if ( obj.isFifo() )
105         t = 'p';
106       else if ( obj.isSock() )
107         t = 's';
108
109       str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
110       return str;
111     }
112
113     ///////////////////////////////////////////////////////////////////
114     //
115     //  Class : PathInfo
116     //
117     ///////////////////////////////////////////////////////////////////
118
119     ///////////////////////////////////////////////////////////////////
120     //
121     //  METHOD NAME : PathInfo::PathInfo
122     //  METHOD TYPE : Constructor
123     //
124     PathInfo::PathInfo()
125     : mode_e( STAT )
126     , error_i( -1 )
127     {}
128
129     ///////////////////////////////////////////////////////////////////
130     //
131     //  METHOD NAME : PathInfo::PathInfo
132     //  METHOD TYPE : Constructor
133     //
134     PathInfo::PathInfo( const Pathname & path, Mode initial )
135     : path_t( path )
136     , mode_e( initial )
137     , error_i( -1 )
138     {
139       operator()();
140     }
141
142     ///////////////////////////////////////////////////////////////////
143     //
144     //  METHOD NAME : PathInfo::PathInfo
145     //  METHOD TYPE : Constructor
146     //
147     PathInfo::PathInfo( const std::string & path, Mode initial )
148     : path_t( path )
149     , mode_e( initial )
150     , error_i( -1 )
151     {
152       operator()();
153     }
154
155     ///////////////////////////////////////////////////////////////////
156     //
157     //  METHOD NAME : PathInfo::PathInfo
158     //  METHOD TYPE : Constructor
159     //
160     PathInfo::PathInfo( const char * path, Mode initial )
161     : path_t( path )
162     , mode_e( initial )
163     , error_i( -1 )
164     {
165       operator()();
166     }
167
168     ///////////////////////////////////////////////////////////////////
169     //
170     //  METHOD NAME : PathInfo::~PathInfo
171     //  METHOD TYPE : Destructor
172     //
173     PathInfo::~PathInfo()
174     {
175     }
176
177     ///////////////////////////////////////////////////////////////////
178     //
179     //  METHOD NAME : PathInfo::operator()
180     //  METHOD TYPE : bool
181     //
182     bool PathInfo::operator()()
183     {
184       if ( path_t.empty() ) {
185         error_i = -1;
186       } else {
187         switch ( mode_e ) {
188         case STAT:
189           error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
190           break;
191         case LSTAT:
192           error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
193           break;
194         }
195         if ( error_i == -1 )
196           error_i = errno;
197       }
198       return !error_i;
199     }
200
201     ///////////////////////////////////////////////////////////////////
202     //
203     //  METHOD NAME : PathInfo::fileType
204     //  METHOD TYPE : File_type
205     //
206     FileType PathInfo::fileType() const
207     {
208       if ( isExist() )
209         return asStatMode().fileType();
210       return FT_NOT_EXIST;
211     }
212
213     ///////////////////////////////////////////////////////////////////
214     //
215     //  METHOD NAME : PathInfo::userMay
216     //  METHOD TYPE : mode_t
217     //
218     mode_t PathInfo::userMay() const
219     {
220       if ( !isExist() )
221         return 0;
222       if ( owner() == getuid() ) {
223         return( uperm()/0100 );
224       } else if ( group() == getgid() ) {
225         return( gperm()/010 );
226       }
227       return operm();
228     }
229
230     /******************************************************************
231      **
232      ** FUNCTION NAME : PathInfo::major
233      ** FUNCTION TYPE : unsigned int
234      */
235     unsigned int PathInfo::major() const
236     {
237       return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
238     }
239
240     /******************************************************************
241      **
242      ** FUNCTION NAME : PathInfo::minor
243      ** FUNCTION TYPE : unsigned int
244      */
245     unsigned int PathInfo::minor() const
246     {
247       return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
248     }
249
250     /******************************************************************
251      **
252      ** FUNCTION NAME : operator<<
253      ** FUNCTION TYPE :  std::ostream &
254     */
255     std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
256     {
257       iostr::IosFmtFlagsSaver autoResoreState( str );
258
259       str << obj.asString() << "{";
260       if ( !obj.isExist() ) {
261         str << "does not exist}";
262       } else {
263         str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
264
265         if ( obj.isFile() )
266           str << " size " << obj.size();
267
268         str << "}";
269       }
270
271       return str;
272     }
273
274     ///////////////////////////////////////////////////////////////////
275     //
276     //  filesystem utilities
277     //
278     ///////////////////////////////////////////////////////////////////
279
280     /******************************************************************
281      **
282      ** FUNCTION NAME : _Log_Result
283      ** FUNCTION TYPE : int
284      **
285      ** DESCRIPTION : Helper function to log return values.
286     */
287     inline int _Log_Result( const int res, const char * rclass = "errno" )
288     {
289       if ( res )
290         DBG << " FAILED: " << rclass << " " << res;
291       DBG << std::endl;
292       return res;
293     }
294
295     ///////////////////////////////////////////////////////////////////
296     //
297     //  METHOD NAME : PathInfo::mkdir
298     //  METHOD TYPE : int
299     //
300     int mkdir( const Pathname & path, unsigned mode )
301     {
302       DBG << "mkdir " << path << ' ' << str::octstring( mode );
303       if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
304         return _Log_Result( errno );
305       }
306       return _Log_Result( 0 );
307     }
308
309     ///////////////////////////////////////////////////////////////////
310     //
311     //  METHOD NAME : assert_dir()
312     //  METHOD TYPE : int
313     //
314     int assert_dir( const Pathname & path, unsigned mode )
315     {
316       string::size_type pos, lastpos = 0;
317       string spath = path.asString()+"/";
318       int ret = 0;
319
320       if(path.empty())
321         return ENOENT;
322
323       // skip ./
324       if(path.relative())
325         lastpos=2;
326       // skip /
327       else
328         lastpos=1;
329
330       //    DBG << "about to create " << spath << endl;
331       while((pos = spath.find('/',lastpos)) != string::npos )
332         {
333           string dir = spath.substr(0,pos);
334           ret = ::mkdir(dir.c_str(), mode);
335           if(ret == -1)
336             {
337               // ignore errors about already existing directorys
338               if(errno == EEXIST)
339                 ret=0;
340               else
341                 ret=errno;
342             }
343           //    DBG << "creating directory " << dir << (ret?" failed":" succeeded") << endl;
344           lastpos = pos+1;
345         }
346       return ret;
347     }
348
349     ///////////////////////////////////////////////////////////////////
350     //
351     //  METHOD NAME : rmdir
352     //  METHOD TYPE : int
353     //
354     int rmdir( const Pathname & path )
355     {
356       DBG << "rmdir " << path;
357       if ( ::rmdir( path.asString().c_str() ) == -1 ) {
358         return _Log_Result( errno );
359       }
360       return _Log_Result( 0 );
361     }
362
363     ///////////////////////////////////////////////////////////////////
364     //
365     //  METHOD NAME : recursive_rmdir
366     //  METHOD TYPE : int
367     //
368     int recursive_rmdir( const Pathname & path )
369     {
370       DBG << "recursive_rmdir " << path << ' ';
371       PathInfo p( path );
372
373       if ( !p.isExist() ) {
374         return _Log_Result( 0 );
375       }
376
377       if ( !p.isDir() ) {
378         return _Log_Result( ENOTDIR );
379       }
380
381       const char *const argv[] = {
382         "/bin/rm",
383         "-rf",
384         "--preserve-root",
385         "--",
386         path.asString().c_str(),
387         NULL
388       };
389
390       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
391       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
392         DBG << "  " << output;
393       }
394       int ret = prog.close();
395       return _Log_Result( ret, "returned" );
396     }
397
398     ///////////////////////////////////////////////////////////////////
399     //
400     //  METHOD NAME : clean_dir
401     //  METHOD TYPE : int
402     //
403     int clean_dir( const Pathname & path )
404     {
405       DBG << "clean_dir " << path << ' ';
406       PathInfo p( path );
407
408       if ( !p.isExist() ) {
409         return _Log_Result( 0 );
410       }
411
412       if ( !p.isDir() ) {
413         return _Log_Result( ENOTDIR );
414       }
415
416       string cmd( str::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
417       ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
418       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
419         DBG << "  " << output;
420       }
421       int ret = prog.close();
422       return _Log_Result( ret, "returned" );
423     }
424
425     ///////////////////////////////////////////////////////////////////
426     //
427     //  METHOD NAME : copy_dir
428     //  METHOD TYPE : int
429     //
430     int copy_dir( const Pathname & srcpath, const Pathname & destpath )
431     {
432       DBG << "copy_dir " << srcpath << " -> " << destpath << ' ';
433
434       PathInfo sp( srcpath );
435       if ( !sp.isDir() ) {
436         return _Log_Result( ENOTDIR );
437       }
438
439       PathInfo dp( destpath );
440       if ( !dp.isDir() ) {
441         return _Log_Result( ENOTDIR );
442       }
443
444       PathInfo tp( destpath + srcpath.basename() );
445       if ( tp.isExist() ) {
446         return _Log_Result( EEXIST );
447       }
448
449
450       const char *const argv[] = {
451         "/bin/cp",
452         "-dR",
453         "--",
454         srcpath.asString().c_str(),
455         destpath.asString().c_str(),
456         NULL
457       };
458       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
459       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
460         DBG << "  " << output;
461       }
462       int ret = prog.close();
463       return _Log_Result( ret, "returned" );
464     }
465
466     ///////////////////////////////////////////////////////////////////
467     //
468     //  METHOD NAME : copy_dir_content
469     //  METHOD TYPE : int
470     //
471     int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
472     {
473       DBG << "copy_dir " << srcpath << " -> " << destpath << ' ';
474
475       PathInfo sp( srcpath );
476       if ( !sp.isDir() ) {
477         return _Log_Result( ENOTDIR );
478       }
479
480       PathInfo dp( destpath );
481       if ( !dp.isDir() ) {
482         return _Log_Result( ENOTDIR );
483       }
484
485       if ( srcpath == destpath ) {
486         return _Log_Result( EEXIST );
487       }
488
489       std::string src( srcpath.asString());
490       src += "/.";
491       const char *const argv[] = {
492         "/bin/cp",
493         "-dR",
494         "--",
495         src.c_str(),
496         destpath.asString().c_str(),
497         NULL
498       };
499       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
500       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
501         DBG << "  " << output;
502       }
503       int ret = prog.close();
504       return _Log_Result( ret, "returned" );
505     }
506
507     ///////////////////////////////////////////////////////////////////
508     //
509     //  METHOD NAME : readdir
510     //  METHOD TYPE : int
511     //
512     int readdir( std::list<std::string> & retlist,
513                  const Pathname & path, bool dots )
514     {
515       retlist.clear();
516
517       DBG << "readdir " << path << ' ';
518
519       DIR * dir = ::opendir( path.asString().c_str() );
520       if ( ! dir ) {
521         return _Log_Result( errno );
522       }
523
524       struct dirent *entry;
525       while ( (entry = ::readdir( dir )) != 0 ) {
526
527         if ( entry->d_name[0] == '.' ) {
528           if ( !dots )
529             continue;
530           if ( entry->d_name[1] == '\0'
531                || (    entry->d_name[1] == '.'
532                     && entry->d_name[2] == '\0' ) )
533             continue;
534         }
535         retlist.push_back( entry->d_name );
536       }
537
538       ::closedir( dir );
539
540       return _Log_Result( 0 );
541     }
542
543
544     ///////////////////////////////////////////////////////////////////
545     //
546     //  METHOD NAME : readdir
547     //  METHOD TYPE : int
548     //
549     int readdir( std::list<Pathname> & retlist,
550                  const Pathname & path, bool dots )
551     {
552       retlist.clear();
553
554       std::list<string> content;
555       int res = readdir( content, path, dots );
556
557       if ( !res ) {
558         for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
559           retlist.push_back( path + *it );
560         }
561       }
562
563       return res;
564     }
565
566     ///////////////////////////////////////////////////////////////////
567     //
568     //  METHOD NAME : readdir
569     //  METHOD TYPE : int
570     //
571     int readdir( DirContent & retlist, const Pathname & path,
572                  bool dots, PathInfo::Mode statmode )
573     {
574       retlist.clear();
575
576       std::list<string> content;
577       int res = readdir( content, path, dots );
578
579       if ( !res ) {
580         for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
581           PathInfo p( path + *it, statmode );
582           retlist.push_back( DirEntry( *it, p.fileType() ) );
583         }
584       }
585
586       return res;
587     }
588
589     ///////////////////////////////////////////////////////////////////
590     //
591     //  METHOD NAME : is_empty_dir
592     //  METHOD TYPE : int
593     //
594     int is_empty_dir(const Pathname & path)
595     {
596       DIR * dir = ::opendir( path.asString().c_str() );
597       if ( ! dir ) {
598         return _Log_Result( errno );
599       }
600
601       struct dirent *entry;
602       while ( (entry = ::readdir( dir )) != NULL )
603       {
604         std::string name(entry->d_name);
605
606         if ( name == "." || name == "..")
607           continue;
608
609         break;
610       }
611       ::closedir( dir );
612
613       return entry != NULL ? -1 : 0;
614     }
615
616     ///////////////////////////////////////////////////////////////////
617     //
618     //  METHOD NAME : unlink
619     //  METHOD TYPE : int
620     //
621     int unlink( const Pathname & path )
622     {
623       DBG << "unlink " << path;
624       if ( ::unlink( path.asString().c_str() ) == -1 ) {
625         return _Log_Result( errno );
626       }
627       return _Log_Result( 0 );
628     }
629
630     ///////////////////////////////////////////////////////////////////
631     //
632     //  METHOD NAME : rename
633     //  METHOD TYPE : int
634     //
635     int rename( const Pathname & oldpath, const Pathname & newpath )
636     {
637       DBG << "rename " << oldpath << " -> " << newpath;
638       if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
639         return _Log_Result( errno );
640       }
641       return _Log_Result( 0 );
642     }
643
644     ///////////////////////////////////////////////////////////////////
645     //
646     //  METHOD NAME : copy
647     //  METHOD TYPE : int
648     //
649     int copy( const Pathname & file, const Pathname & dest )
650     {
651       DBG << "copy " << file << " -> " << dest << ' ';
652
653       PathInfo sp( file );
654       if ( !sp.isFile() ) {
655         return _Log_Result( EINVAL );
656       }
657
658       PathInfo dp( dest );
659       if ( dp.isDir() ) {
660         return _Log_Result( EISDIR );
661       }
662
663       const char *const argv[] = {
664         "/bin/cp",
665         "--",
666         file.asString().c_str(),
667         dest.asString().c_str(),
668         NULL
669       };
670       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
671       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
672         DBG << "  " << output;
673       }
674       int ret = prog.close();
675       return _Log_Result( ret, "returned" );
676     }
677
678     ///////////////////////////////////////////////////////////////////
679     //
680     //  METHOD NAME : symlink
681     //  METHOD TYPE : int
682     //
683     int symlink( const Pathname & oldpath, const Pathname & newpath )
684     {
685       DBG << "symlink " << newpath << " -> " << oldpath;
686       if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
687         return _Log_Result( errno );
688       }
689       return _Log_Result( 0 );
690     }
691
692     ///////////////////////////////////////////////////////////////////
693     //
694     //  METHOD NAME : hardlink
695     //  METHOD TYPE : int
696     //
697     int hardlink( const Pathname & oldpath, const Pathname & newpath )
698     {
699       DBG << "hardlink " << newpath << " -> " << oldpath;
700       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
701         return _Log_Result( errno );
702       }
703       return _Log_Result( 0 );
704     }
705
706     ///////////////////////////////////////////////////////////////////
707     //
708     //  METHOD NAME : copy_file2dir
709     //  METHOD TYPE : int
710     //
711     int copy_file2dir( const Pathname & file, const Pathname & dest )
712     {
713       DBG << "copy_file2dir " << file << " -> " << dest << ' ';
714
715       PathInfo sp( file );
716       if ( !sp.isFile() ) {
717         return _Log_Result( EINVAL );
718       }
719
720       PathInfo dp( dest );
721       if ( !dp.isDir() ) {
722         return _Log_Result( ENOTDIR );
723       }
724
725       const char *const argv[] = {
726         "/bin/cp",
727         "--",
728         file.asString().c_str(),
729         dest.asString().c_str(),
730         NULL
731       };
732       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
733       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
734         DBG << "  " << output;
735       }
736       int ret = prog.close();
737       return _Log_Result( ret, "returned" );
738     }
739
740     ///////////////////////////////////////////////////////////////////
741     //
742     //  METHOD NAME : md5sum
743     //  METHOD TYPE : std::string
744     //
745     std::string md5sum( const Pathname & file )
746     {
747       if ( ! PathInfo( file ).isFile() ) {
748         return string();
749       }
750       std::ifstream istr( file.asString().c_str() );
751       if ( ! istr ) {
752         return string();
753       }
754       return Digest::digest( "MD5", istr );
755     }
756
757     ///////////////////////////////////////////////////////////////////
758     //
759     //  METHOD NAME : sha1sum
760     //  METHOD TYPE : std::string
761     //
762     std::string sha1sum( const Pathname & file )
763     {
764       if ( ! PathInfo( file ).isFile() ) {
765         return string();
766       }
767       std::ifstream istr( file.asString().c_str() );
768       if ( ! istr ) {
769         return string();
770       }
771       return Digest::digest( "SHA1", istr );
772     }
773
774     ///////////////////////////////////////////////////////////////////
775     //
776     //  METHOD NAME : erase
777     //  METHOD TYPE : int
778     //
779     int erase( const Pathname & path )
780     {
781       int res = 0;
782       PathInfo p( path, PathInfo::LSTAT );
783       if ( p.isExist() )
784         {
785           if ( p.isDir() )
786             res = recursive_rmdir( path );
787           else
788             res = unlink( path );
789         }
790       return res;
791     }
792
793     ///////////////////////////////////////////////////////////////////
794     //
795     //  METHOD NAME : chmod
796     //  METHOD TYPE : int
797     //
798     int chmod( const Pathname & path, mode_t mode )
799     {
800       DBG << "chmod " << path << ' ' << str::octstring( mode );
801       if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
802         return _Log_Result( errno );
803       }
804       return _Log_Result( 0 );
805     }
806
807     ///////////////////////////////////////////////////////////////////
808     //
809     //  METHOD NAME : zipType
810     //  METHOD TYPE : ZIP_TYPE
811     //
812     ZIP_TYPE zipType( const Pathname & file )
813     {
814       ZIP_TYPE ret = ZT_NONE;
815
816       int fd = open( file.asString().c_str(), O_RDONLY );
817
818       if ( fd != -1 ) {
819         const int magicSize = 3;
820         unsigned char magic[magicSize];
821         memset( magic, 0, magicSize );
822         if ( read( fd, magic, magicSize ) == magicSize ) {
823           if ( magic[0] == 0037 && magic[1] == 0213 ) {
824             ret = ZT_GZ;
825           } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
826             ret = ZT_BZ2;
827           }
828         }
829         close( fd );
830       }
831
832       return ret;
833     }
834
835     /////////////////////////////////////////////////////////////////
836   } // namespace filesystem
837   ///////////////////////////////////////////////////////////////////
838   /////////////////////////////////////////////////////////////////
839 } // namespace zypp
840 ///////////////////////////////////////////////////////////////////