summaryrefslogtreecommitdiff
path: root/src/prtget.cpp
blob: fef2afeed6ea64f1c6aa96604f3a4b0b2377375c (plain)
    1 ////////////////////////////////////////////////////////////////////////
    2 // FILE:        prtget.cpp
    3 // AUTHOR:      Johannes Winkelmann, jw@tks6.net
    4 // COPYRIGHT:   (c) 2002 by Johannes Winkelmann
    5 // ---------------------------------------------------------------------
    6 //  This program is free software; you can redistribute it and/or modify
    7 //  it under the terms of the GNU General Public License as published by
    8 //  the Free Software Foundation; either version 2 of the License, or
    9 //  (at your option) any later version.
   10 ////////////////////////////////////////////////////////////////////////
   11 
   12 #ifdef HAVE_CONFIG_H
   13 #include <config.h>
   14 #endif
   15 
   16 #include <iostream>
   17 #include <algorithm>
   18 #include <set>
   19 #include <iomanip>
   20 #include <cstdio>
   21 #include <cassert>
   22 using namespace std;
   23 
   24 #include <sys/types.h>
   25 #include <sys/stat.h>
   26 #include <dirent.h>
   27 #include <unistd.h>
   28 
   29 #include "prtget.h"
   30 #include "repository.h"
   31 #include "argparser.h"
   32 #include "installtransaction.h"
   33 #include "configuration.h"
   34 
   35 #include "stringhelper.h"
   36 #include "versioncomparator.h"
   37 #include "file.h"
   38 #include "process.h"
   39 #include "datafileparser.h"
   40 using namespace StringHelper;
   41 
   42 
   43 const string PrtGet::CONF_FILE = SYSCONFDIR"/prt-get.conf";
   44 const string PrtGet::DEFAULT_CACHE_FILE = LOCALSTATEDIR"/lib/pkg/prt-get.cache";
   45 
   46 /*!
   47   Create a PrtGet object
   48   \param parser the argument parser to be used
   49 */
   50 PrtGet::PrtGet( const ArgParser* parser )
   51     : m_repo( 0 ),
   52       m_config( 0 ),
   53       m_parser( parser ),
   54       m_cacheFile( DEFAULT_CACHE_FILE ),
   55       m_returnValue( PG_OK ),
   56       m_currentTransaction( 0 )
   57 {
   58     if ( m_parser->wasCalledAsPrtCached() ) {
   59         m_appName = "prt-cache";
   60     } else {
   61         m_appName = "prt-get";
   62     }
   63 
   64 
   65     m_pkgDB = new PkgDB(m_parser->installRoot());
   66     readConfig();
   67 
   68     m_useRegex = m_config->useRegex() || m_parser->useRegex();
   69 }
   70 
   71 /*! destruct PrtGet object */
   72 PrtGet::~PrtGet()
   73 {
   74     if ( m_config ) {
   75         delete m_config;
   76     }
   77     if ( m_repo ) {
   78         delete m_repo;
   79     }
   80 
   81     delete m_pkgDB;
   82 }
   83 
   84 
   85 /*! print version and exit */
   86 void PrtGet::printVersion()
   87 {
   88     cout << m_appName << " " << VERSION
   89          << " by Johannes Winkelmann, jw@tks6.net" << endl;
   90 }
   91 
   92 /*! print version, usage and exit */
   93 void PrtGet::printUsage()
   94 {
   95     printVersion();
   96     cout << "Usage: " << m_appName << " <command> [options]" << endl;
   97 
   98     cout << "where commands are:" << endl;
   99 
  100     cout << "\nINFORMATION" << endl;
  101     cout << "  help                       show this help" << endl;
  102     cout << "  version                    show the current version" << endl;
  103     cout << "  list     [<filter>]        show a list of available ports"
  104          << endl;
  105     cout << "  printf   <format>          print formatted list of available"
  106          << " ports"
  107          << endl;
  108     cout << "  listinst [<filter>]        show a list of installed ports"
  109          << endl;
  110     cout << "  listorphans                list of ports with no "
  111          << "packages depending on them" << endl;
  112     cout << "  info     <port>            show info about a port" << endl;
  113     cout << "  path     <port>            show path of a port" << endl;
  114     cout << "  readme   <port>            show a port's readme file "
  115          << "(if it exists)" << endl;
  116     cout << "  dup                        Find duplicate ports" << endl;
  117     cout << "  isinst   <port1 port2...>  print whether ports are installed"
  118          << endl;
  119     cout << "  current  <port>            print installed version of port"
  120          << endl;
  121 
  122     cout << "\nDIFFERENCES / CHECK FOR UPDATES" << endl;
  123     cout << "  diff     <port1 port2...>  list outdated packages (or check "
  124          << "args for change)" << endl;
  125     cout << "  quickdiff                  same as diff but simple format"
  126          << endl;
  127     cout << "          where opt can be:" << endl;
  128     cout << "    --all            display locked ports too"
  129          << endl;
  130     cout << "    --prefer-higher  prefer higher installed "
  131          << "versions over lower ports"
  132          << endl;
  133     cout << "    --strict-diff    override prefer higher "
  134          << "configuration setting"
  135          << endl;
  136 
  137     cout << "\nDEPENDENCIES" << endl;
  138     cout << "  depends   <port1 port2...>  show dependencies for these ports"
  139          << endl;
  140     cout << "  quickdep  <port1 port2...>  same as 'depends' but simple format"
  141          << endl;
  142     cout << "  deptree   <port>            show dependencies tree for <port>"
  143          << endl;
  144     cout << "  dependent [opt] <port>      show installed packages which "
  145          << "depend on 'port'"
  146          << endl;
  147     cout << "          where opt can be:" << endl;
  148     cout << "                --all    list all dependent packages, not "
  149          << "only installed" << endl;
  150     cout << "                --recursive    print recursive listing" << endl;
  151     cout << "                --tree         print recursive tree listing" 
  152          << endl;
  153 
  154     cout << "\nSEARCHING" << endl;
  155     cout << "  search  <expr>     show port names containing 'expr'" << endl;
  156     cout << "  dsearch <expr>     show ports containing 'expr' in the "
  157          << "name or description" << endl;
  158     cout << "  fsearch <pattern>  show file names in footprints matching "
  159          << "'pattern'" << endl;
  160 
  161     cout << "\nINSTALL, UPDATE and REMOVAL" << endl;
  162     cout << "  install [opt] <port1 port2...>    install ports" << endl;
  163     cout << "  update  [opt] <port1 port2...>    update ports" << endl;
  164     cout << "  grpinst [opt] <port1 port2...>    install ports, stop on error"
  165          << endl;
  166     cout << "  depinst [opt] <port1 port2...>    install ports and their dependencies"
  167          << endl;
  168     cout << "  remove [opt] <port1 port2...>     remove ports"
  169          << endl;
  170     cout << "          where opt can be:" << endl;
  171     cout << "                -f, -fi             force installation" << endl;
  172     cout << "                -fr                 force rebuild" << endl;
  173     cout << "                -uf                 update footprint" << endl;
  174     cout << "                -if                 ignore footprint" << endl;
  175     cout << "                -um                 update md5sum" << endl;
  176     cout << "                -im                 ignore md5sum" << endl;
  177     cout << "                --margs=<string>    pass 'string' to pkgmk"
  178          << endl;
  179     cout << "                --aargs=<string>    pass 'string' to pkgadd"
  180          << endl;
  181     cout << "                --rargs=<string>    pass 'string' to pkgrm"
  182          << endl;
  183     cout << "                --test              test mode" << endl;
  184     cout << "                --log               write log file"<< endl;
  185     cout << "                --ignore=<package1,package2,...>" << endl
  186          << "                                    Don't install/update those packages"<< endl;
  187     cout << "                --pre-install       execute pre-install script"
  188          << endl;
  189     cout << "                --post-install      execute post-install script"
  190          << endl;
  191     cout << "                --install-scripts   execute "
  192          << "pre-install and post-install script"
  193          << endl;
  194 
  195     cout << "\nSYSTEM UPDATE " << endl;
  196     cout << "  sysup [opt]                       update all outdated ports"
  197          << endl;
  198     cout << "          where opt can be:" << endl;
  199     cout << "                --nodeps            don't sort by dependencies"
  200          << endl;
  201     cout << "                --test              test mode" << endl;
  202     cout << "                --log               write log file"<< endl;
  203     cout << "                --prefer-higher     prefer higher installed "
  204          << "versions over lower ones in ports tree"
  205          << endl;
  206     cout << "                --strict-diff       override prefer higher "
  207          << "configuration setting"
  208          << endl;
  209 
  210     cout << "  lock <port1 port2...>             lock current version "
  211          << "of packages"
  212          << endl;
  213     cout << "  unlock <port1 port2...>           unlock packages"
  214          << endl;
  215     cout << "  listlocked                        list locked packages"
  216          << endl;
  217 
  218     cout << "\nFILE OPERATIONS " << endl;
  219 
  220     cout << "  ls <port>                         print a listing of the port's"
  221          << " directory" << endl;
  222     cout << "  cat <port> <file>                 print out 'port/file'"
  223          << endl;
  224     cout << "  edit <port> <file>                edit 'port/file'" << endl;
  225 
  226     cout << "\nGENERAL OPTIONS" << endl;
  227     cout << "                -v                 Show version in listing"
  228          << endl;
  229     cout << "                -vv                Show version and decription "          << "in listing\n" << endl;
  230     cout << "                --path             Print path to port if appropriate (search, list, depends)\n" << endl;
  231     cout << "                --cache             Use a cache file" << endl;
  232     cout << "                --config=<file>     Use alternative "
  233          << "configuration file" << endl;
  234     cout << "                --install-root=..   Use alternative "
  235          << "install root directory" << endl;
  236 
  237 
  238 
  239 
  240     cout << "                --no-std-config     Don't parse "
  241          << "default configuration file" << endl;
  242     cout << "                --config-prepend=.. Prepend '..' to"
  243          << " configuration" << endl;
  244     cout << "                --config-append=..  Append '..' "
  245          << "to configuration" << endl;
  246     cout << "                --config-set=..     Set configuration "
  247          << "data '..',\n"
  248          << "                                       overriding config file"
  249          << endl;
  250 
  251 }
  252 
  253 
  254 /*! print list of duplicate packages in the repository */
  255 void PrtGet::listShadowed()
  256 {
  257     if ( m_parser->useCache() ) {
  258         cout << m_appName << ": command 'dup' can't work on a cache" << endl;
  259         m_returnValue = PG_GENERAL_ERROR;
  260         return;
  261     }
  262 
  263     initRepo( true );
  264     cout << "Hidden packages:" << endl;
  265     map<string, pair<string, string> >::const_iterator it =
  266         m_repo->shadowedPackages().begin();
  267     for ( ; it != m_repo->shadowedPackages().end(); ++it ) {
  268         string name = it->first;
  269         cout << "* " << name << endl;
  270         cout << "  " << it->second.second << " preceeds over" << endl;
  271         cout << "  " << it->second.first << endl;
  272     }
  273 }
  274 
  275 /*!
  276   find ports matching a pattern in repository
  277 
  278   \sa Repository::getMatchingPackages()
  279 */
  280 void PrtGet::listPackages()
  281 {
  282     string arg = "*";
  283     assertMaxArgCount(1);
  284 
  285     if ( m_parser->otherArgs().size() == 1 ) {
  286         arg = *(m_parser->otherArgs().begin());
  287     }
  288 
  289     initRepo();
  290     list<Package*> packages;
  291     m_repo->getMatchingPackages( arg, packages );
  292     if ( packages.size() ) {
  293         list<Package*>::iterator it = packages.begin();
  294         for ( ; it != packages.end(); ++it ) {
  295             if ( m_parser->printPath() ) {
  296                 cout << (*it)->path() << "/";
  297             }
  298             cout << (*it)->name();
  299             if ( m_parser->verbose() > 0 ) {
  300                 cout << " " << (*it)->version() << "-" << (*it)->release();
  301             }
  302             if ( m_parser->verbose() > 1 && !(*it)->description().empty() ) {
  303                 cout << ": " << (*it)->description();
  304             }
  305 
  306             cout << endl;
  307         }
  308     } else {
  309         cout << "No matching packages found"  << endl;
  310     }
  311 }
  312 
  313 /*!
  314   search repository for a certain pattern (which is read by the argument
  315   parser.
  316 
  317   \sa Repository::searchMatchingPackages()
  318 */
  319 void PrtGet::searchPackages( bool searchDesc )
  320 {
  321     assertExactArgCount(1);
  322 
  323     initRepo();
  324     string arg = *(m_parser->otherArgs().begin());
  325     list<Package*> packages;
  326     m_repo->searchMatchingPackages( arg, packages, searchDesc );
  327     if ( packages.size() ) {
  328         list<Package*>::iterator it = packages.begin();
  329         for ( ; it != packages.end(); ++it ) {
  330             if ( m_parser->printPath()) {
  331                 cout << (*it)->path() << "/";
  332             }
  333             cout << (*it)->name();
  334 
  335             if ( m_parser->verbose() > 0 ) {
  336                 cout << " " << (*it)->version() << "-" << (*it)->release();
  337             }
  338             if ( m_parser->verbose() > 1 && !(*it)->description().empty() ) {
  339                 cout << ": " << (*it)->description();
  340             }
  341 
  342 
  343             cout << endl;
  344         }
  345     } else {
  346         m_returnValue = PG_GENERAL_ERROR;
  347         cout << "No matching packages found"  << endl;
  348     }
  349 }
  350 
  351 /*! print info for a package */
  352 void PrtGet::printInfo()
  353 {
  354     assertExactArgCount(1);
  355 
  356     initRepo();
  357     string arg = *(m_parser->otherArgs().begin());
  358     const Package* p = m_repo->getPackage( arg );
  359     if ( p ) {
  360         cout << "Name:         " << p->name() << "\n"
  361              << "Path:         " << p->path() << "\n"
  362              << "Version:      " << p->version() << "\n"
  363              << "Release:      " << p->release() << endl;
  364 
  365         if ( !p->description().empty() ) {
  366             cout << "Description:  " << p->description() << endl;
  367         }
  368         if ( !p->url().empty() ) {
  369             cout << "URL:          " << p->url() << endl;
  370         }
  371         if ( !p->packager().empty() ) {
  372             cout << "Packager:     " << p->packager() << endl;
  373         }
  374         if ( !p->maintainer().empty() ) {
  375             cout << "Maintainer:   " << p->maintainer() << endl;
  376         }
  377 
  378         if ( !p->dependencies().empty() ) {
  379             cout << "Dependencies: " << p->dependencies() << endl;
  380         }
  381 
  382         // TODO: don't hardcode file names
  383         string filesString = "";
  384         if ( p->hasReadme() ) {
  385             filesString += "README ";
  386         }
  387         if ( p->hasPreInstall() ) {
  388             filesString += "pre-install ";
  389         }
  390         if ( p->hasPostInstall() ) {
  391             filesString += "post-install ";
  392         }
  393 
  394         if ( filesString.length() > 0 ) {
  395             filesString = StringHelper::stripWhiteSpace( filesString );
  396             StringHelper::replaceAll( filesString, " ", "," );
  397             cout << "Files:        " << filesString << endl;
  398         }
  399 
  400         if ( m_parser->verbose() > 0 && p->hasReadme()) {
  401             cout << "\n-- README ------" << endl;
  402             readme();
  403         }
  404 
  405     } else {
  406         cerr << "Package '" << arg << "' not found" << endl;
  407         m_returnValue = PG_GENERAL_ERROR;
  408         return;
  409     }
  410 }
  411 
  412 
  413 /*!
  414   initialize repository
  415   \sa Repository::initFromCache()
  416   \sa Repository::initFromFS()
  417  */
  418 void PrtGet::initRepo( bool listDuplicate )
  419 {
  420     if ( !m_repo ) {
  421         m_repo = new Repository(m_useRegex);
  422 
  423         if ( m_parser->useCache() ) {
  424             if (m_config->cacheFile() != "") {
  425                 m_cacheFile = m_config->cacheFile();
  426             }
  427 
  428             Repository::CacheReadResult result =
  429                 m_repo->initFromCache( m_cacheFile );
  430             if ( result == Repository::ACCESS_ERR  ) {
  431                 cerr << "Can't open cache file: " << m_cacheFile << endl;
  432                 m_returnValue = PG_GENERAL_ERROR;
  433                 return;
  434             } else if ( result == Repository::FORMAT_ERR ) {
  435                 cerr << "warning: your cache file "
  436                      << m_cacheFile << " was made with an "
  437                      << "older version "
  438                      << "of prt-get."
  439                      << "\nPlease regenerate it using"
  440                      << "\n  prt-get cache" << endl;
  441                 m_returnValue = PG_GENERAL_ERROR;
  442                 return;
  443             }
  444 
  445             struct stat cacheStat;
  446             struct stat confStat;
  447             stat( m_cacheFile.c_str(), &cacheStat );
  448             stat( CONF_FILE.c_str(), &confStat );
  449             if ( confStat.st_ctime > cacheStat.st_ctime ) {
  450                 cerr << "Error: "
  451                      << "Configuration changed after generating cache"
  452                      << endl;
  453                 cerr << "regenerate cache using 'prt-get cache'" << endl;
  454                 m_returnValue = PG_GENERAL_ERROR;
  455                 return;
  456             }
  457 
  458             if ( !m_parser->wasCalledAsPrtCached() ) {
  459                 cout << m_appName << ": using cache" << endl;
  460             }
  461 
  462         } else {
  463             m_repo->initFromFS( m_config->rootList(), listDuplicate );
  464         }
  465     }
  466 }
  467 
  468 /*! print whether a package is installed or not */
  469 void PrtGet::isInstalled()
  470 {
  471     assertMinArgCount(1);
  472 
  473     const list<char*>& l = m_parser->otherArgs();
  474     list<char*>::const_iterator it = l.begin();
  475     for ( ; it != l.end(); ++it ) {
  476         bool isAlias = false;
  477         string aliasName;
  478 
  479         if ( m_pkgDB->isInstalled( *it, true, &isAlias, &aliasName  ) ) {
  480             if (isAlias) {
  481                 cout << *it << " is provided by package "
  482                      << aliasName
  483                      << endl;
  484             } else {
  485                 cout << "package " << *it << " is installed" << endl;
  486             }
  487         } else {
  488             cout << "package " << *it << " is not installed" << endl;
  489             m_returnValue = PG_GENERAL_ERROR;
  490         }
  491     }
  492 }
  493 
  494 
  495 /*! list installed packages */
  496 void PrtGet::listInstalled()
  497 {
  498     assertMaxArgCount(1);
  499 
  500     string arg = "*";
  501     if ( m_parser->otherArgs().size() == 1 ) {
  502         arg = *(m_parser->otherArgs().begin());
  503     }
  504 
  505     map<string, string> l;
  506     m_pkgDB->getMatchingPackages( arg, l, m_useRegex );
  507     map<string, string>::iterator it = l.begin();
  508 
  509     if ( l.empty() && m_parser->otherArgs().size() > 0 ) {
  510         cerr << m_appName << ": No matching packages found" << endl;
  511         m_returnValue = PG_GENERAL_ERROR;
  512         return;
  513     }
  514 
  515     if ( m_parser->verbose() > 1 ) {
  516         // warning: will slow down the process...
  517         initRepo();
  518     }
  519 
  520     for ( ; it != l.end(); ++it ) {
  521         cout <<  it->first.c_str();
  522         if ( m_parser->verbose() > 0 ) {
  523             cout << " " << it->second.c_str();
  524         }
  525         if ( m_parser->verbose() > 1 ) {
  526             const Package* p = m_repo->getPackage( it->first );
  527             if ( p ) {
  528                 cout << " " << p->description();
  529             }
  530         }
  531 
  532         cout << endl;
  533     }
  534 }
  535 
  536 /*!
  537    install package
  538    \param update whether this is an update operation
  539    \param group whether it's a group install (stop on error)
  540 
  541 */
  542 void PrtGet::install( bool update, bool group, bool dependencies )
  543 {
  544     assertMinArgCount(1);
  545 
  546     // this can be done without initRepo()
  547     const list<char*>& args = m_parser->otherArgs();
  548     list<char*>::const_iterator it = args.begin();
  549 
  550     if ( args.size() == 1 ) {
  551         for ( ; it != args.end(); ++it ) {
  552             string s = *it;
  553             if ( !update && m_pkgDB->isInstalled( s ) ) {
  554                 cout << "package " << s << " is installed" << endl;
  555                 m_returnValue = PG_GENERAL_ERROR;
  556                 return;
  557             } else if ( update && !m_pkgDB->isInstalled( s ) ) {
  558                 // can't upgrade
  559                 cout << "package " << s << " is not installed" << endl;
  560                 m_returnValue = PG_GENERAL_ERROR;
  561                 return;
  562             }
  563         }
  564     }
  565 
  566     initRepo();
  567 
  568     if (dependencies) {
  569         // calc dependencies
  570         InstallTransaction depTransaction( m_parser->otherArgs(),
  571                                            m_repo, m_pkgDB, m_config );
  572         InstallTransaction::InstallResult result =
  573             depTransaction.calcDependencies();
  574 
  575         // TODO: code duplication with printDepends!
  576         if ( result == InstallTransaction::CYCLIC_DEPEND ) {
  577             cerr << "prt-get: cyclic dependencies found" << endl;
  578             m_returnValue = PG_GENERAL_ERROR;
  579             return;
  580         } else if ( result == InstallTransaction::PACKAGE_NOT_FOUND ) {
  581             warnPackageNotFound(depTransaction);
  582             m_returnValue = PG_GENERAL_ERROR;
  583             return;
  584         }
  585         const list<string>& depRef = depTransaction.dependencies();
  586         list<string>::const_iterator it = depRef.begin();
  587 
  588         list<string> deps;
  589         for (; it != depRef.end(); ++it) {
  590             if (!m_pkgDB->isInstalled(*it)) {
  591                 deps.push_back(*it);
  592             }
  593         }
  594 
  595         InstallTransaction transaction( deps, m_repo, m_pkgDB, m_config );
  596         executeTransaction( transaction, update, group );
  597     } else {
  598         InstallTransaction transaction( m_parser->otherArgs(),
  599                                         m_repo, m_pkgDB, m_config );
  600         executeTransaction( transaction, update, group );
  601     }
  602 }
  603 
  604 void PrtGet::executeTransaction( InstallTransaction& transaction,
  605                                  bool update, bool group )
  606 {
  607     m_currentTransaction = &transaction;
  608 
  609     string command[] = { "install", "installed" };
  610     if ( update ) {
  611         command[0] = "update";
  612         command[1] = "updated";
  613     }
  614 
  615     if ( m_parser->isTest() ) {
  616         cout << "*** " << m_appName << ": test mode" << endl;
  617     }
  618 
  619     InstallTransaction::InstallResult result =
  620         transaction.install( m_parser, update, group );
  621     bool failed = false;
  622     // TODO: use switch
  623     if ( result == InstallTransaction::PACKAGE_NOT_FOUND ) {
  624         cout << m_appName << ": package(s) not found" << endl;
  625     } else if ( result == InstallTransaction::PKGMK_EXEC_ERROR ) {
  626         cout << m_appName << " couldn't excecute pkgmk "
  627              << "(or alternative command). "
  628              << "Make sure it's installed properly" << endl;
  629     } else if ( result == InstallTransaction::PKGMK_FAILURE ) {
  630         cout << m_appName << ": error while " << command[0] << endl;
  631     } else if ( result == InstallTransaction::NO_PACKAGE_GIVEN ) {
  632         cout << m_appName << ": no package specified for "
  633              << command[0] << endl;
  634     } else if ( result == InstallTransaction::PKGADD_EXEC_ERROR ) {
  635         cout << m_appName << " couldn't excecute pkgadd. "
  636              << "Make sure it's installed properly" << endl;
  637     } else if ( result == InstallTransaction::PKGDEST_ERROR ) {
  638         cout << m_appName << ": error changing to directory PKGDEST " << endl;
  639         failed = true;
  640     } else if ( result == InstallTransaction::PKGADD_FAILURE ) {
  641         cout << m_appName << ": error while pkgadding " << endl;
  642     } else if ( result == InstallTransaction::LOG_DIR_FAILURE ) {
  643         cout << m_appName << ": can't create log file directory " << endl;
  644     } else if ( result == InstallTransaction::LOG_FILE_FAILURE ) {
  645         cout << m_appName << ": can't create log file" << endl;
  646         failed = true;
  647     } else if ( result == InstallTransaction::NO_LOG_FILE ) {
  648         cout << m_appName << ": no log file specified, but logging enabled"
  649              << endl;
  650         failed = true;
  651     } else if ( result == InstallTransaction::CANT_LOCK_LOG_FILE ) {
  652         cout << m_appName << ": can't create lock file for the log file. "
  653              << "\nMaybe there's another instance of prt-get using the same "
  654              << "file."
  655              << "\nIf this is a stale not, please remove "
  656             // TODO: file name of lock file
  657              << endl;
  658         failed = true;
  659     } else if ( result != InstallTransaction::SUCCESS ) {
  660         cout << m_appName << ": Unknown error " << result << endl;
  661         failed = true;
  662     }
  663 
  664     if ( !failed ) {
  665         evaluateResult( transaction, update );
  666         if ( m_parser->isTest() ) {
  667             cout << "\n*** " << m_appName << ": test mode end" << endl;
  668         }
  669     } else {
  670         m_returnValue = PG_INSTALL_ERROR;
  671     }
  672 
  673     m_currentTransaction = 0;
  674 }
  675 
  676 /*!
  677   print dependency listing
  678   \param simpleListing Whether it should be in a simple format
  679 */
  680 void PrtGet::printDepends( bool simpleListing )
  681 {
  682     assertMinArgCount(1);
  683 
  684     initRepo();
  685 
  686     InstallTransaction transaction( m_parser->otherArgs(),
  687                                     m_repo, m_pkgDB, m_config );
  688     InstallTransaction::InstallResult result = transaction.calcDependencies();
  689     if ( result == InstallTransaction::CYCLIC_DEPEND ) {
  690         cerr << "prt-get: cyclic dependencies found" << endl;
  691         m_returnValue = PG_GENERAL_ERROR;
  692         return;
  693     } else if ( result == InstallTransaction::PACKAGE_NOT_FOUND ) {
  694         warnPackageNotFound(transaction);
  695         m_returnValue = PG_GENERAL_ERROR;
  696         return;
  697     }
  698 
  699     const list<string>& deps = transaction.dependencies();
  700     if ( simpleListing ) {
  701         if ( deps.size() > 0 ) {
  702             list<string>::const_iterator it = deps.begin();
  703             for ( ; it != deps.end(); ++it ) {
  704                 cout << *it << " ";
  705             }
  706             cout << endl;
  707         }
  708     } else {
  709         if ( deps.size() > 0 ) {
  710             cout << "-- dependencies ([i] = installed)" << endl;
  711             list<string>::const_iterator it = deps.begin();
  712 
  713             bool isAlias;
  714             string provider;
  715             for ( ; it != deps.end(); ++it ) {
  716                 isAlias = false;
  717                 if ( m_pkgDB->isInstalled( *it, true, &isAlias, &provider ) ) {
  718                     cout << "[i] ";
  719                 } else {
  720                     cout << "[ ] ";
  721                 }
  722                 if (m_parser->printPath() > 0) {
  723                     cout << m_repo->getPackage(*it)->path() << "/";
  724                 }
  725                 cout << *it;
  726 
  727                 if (isAlias) {
  728                     cout << " (provided by " << provider << ")";
  729                 }
  730                 cout << endl;
  731             }
  732         } else {
  733             cout << "No dependencies found" << endl;
  734         }
  735 
  736         const list< pair<string, string> >& missing = transaction.missing();
  737         if ( missing.size() ) {
  738             list< pair<string, string> >::const_iterator mit = missing.begin();
  739             cout << endl << "-- missing packages" << endl;
  740             for ( ; mit != missing.end(); ++mit ) {
  741                 cout << mit->first;
  742                 if ( !mit->second.empty() ) {
  743                     cout << " from " << mit->second;
  744                 }
  745                 cout << endl;
  746             }
  747         }
  748     }
  749 }
  750 
  751 /*! read the config file */
  752 void PrtGet::readConfig()
  753 {
  754     string fName = CONF_FILE;
  755     if ( m_parser->isAlternateConfigGiven() ) {
  756         fName = m_parser->alternateConfigFile();
  757     }
  758 
  759     if ( m_config ) {
  760         return; // don't initialize twice
  761     }
  762     m_config = new Configuration( fName, m_parser );
  763 
  764     if (!m_parser->noStdConfig()) {
  765         if ( !m_config->parse() ) {
  766             cerr << "Can't read config file " << fName
  767                  << ". Exiting" << endl;
  768             m_returnValue = PG_GENERAL_ERROR;
  769             return;
  770         }
  771     }
  772 
  773     const list< pair<char*, ArgParser::ConfigArgType> >& configData =
  774         m_parser->configData();
  775     list< pair<char*, ArgParser::ConfigArgType> >::const_iterator it =
  776         configData.begin();
  777     for (; it != configData.end(); ++it) {
  778         m_config->addConfig(it->first,
  779                             it->second == ArgParser::CONFIG_SET,
  780                             it->second == ArgParser::CONFIG_PREPEND);
  781     }
  782 }
  783 
  784 /*!
  785   print a simple list of port which are installed in a different version
  786   than they are in the repository
  787 */
  788 void PrtGet::printQuickDiff()
  789 {
  790     initRepo();
  791 
  792     const map<string, string>& installed = m_pkgDB->installedPackages();
  793     map<string, string>::const_iterator it = installed.begin();
  794     const Package* p = 0;
  795     for ( ; it != installed.end(); ++it ) {
  796         if ( !m_locker.isLocked( it->first ) ) {
  797             p = m_repo->getPackage( it->first );
  798             if ( p ) {
  799                 if (greaterThan(p->version() + "-" + p->release(),
  800                                 it->second)) {
  801                     cout <<  it->first.c_str() << " ";
  802                 }
  803             }
  804         }
  805     }
  806     cout << endl;
  807 }
  808 
  809 
  810 /*!
  811   print an overview of port which are installed in a different version
  812   than they are in the repository
  813 */
  814 void PrtGet::printDiff()
  815 {
  816     initRepo();
  817     map< string, string > l;
  818     if ( m_parser->otherArgs().size() > 0 ) {
  819         expandWildcardsPkgDB( m_parser->otherArgs(), l );
  820     }
  821     if ( l.size() < m_parser->otherArgs().size() ) {
  822         cerr << "prt-get: no matching installed packages found" << endl;
  823         m_returnValue = PG_GENERAL_ERROR;
  824         return;
  825     }
  826 
  827 #if 0
  828     // const list<char*>& l = m_parser->otherArgs();
  829     // list<char*>::const_iterator checkIt = l.begin();
  830 
  831     // check whether ports to be checked are installed
  832     list< string >::iterator checkIt = l.begin();
  833     for ( ; checkIt != l.end(); ++checkIt ) {
  834         if ( ! m_pkgDB->isInstalled( *checkIt )  ) {
  835             cerr << "Port not installed: " << *checkIt << endl;
  836             m_returnValue = PG_GENERAL_ERROR;
  837             return;
  838         }
  839     }
  840 #endif
  841 
  842     const map<string, string>& installed = m_pkgDB->installedPackages();
  843     map<string, string>::const_iterator it = installed.begin();
  844     const Package* p = 0;
  845     int count = 0;
  846     for ( ; it != installed.end(); ++it ) {
  847 
  848         p = m_repo->getPackage( it->first );
  849         if ( p ) {
  850             if ( l.size() && l.find( it->first ) == l.end() ) {
  851                 continue;
  852             }
  853 
  854             if ( greaterThan( p->version() + "-" + p->release(),
  855                               it->second ) ) {
  856                 if ( !m_locker.isLocked( it->first )  ||
  857                      m_parser->otherArgs().size() > 0 ||
  858                      m_parser->all() ) {
  859 
  860 
  861                     ++count;
  862                     if ( count == 1 ) {
  863                         cout << "Differences between installed packages "
  864                              << "and ports tree:\n" << endl;
  865                         cout.setf( ios::left, ios::adjustfield );
  866                         cout.width( 20 );
  867                         cout.fill( ' ' );
  868                         cout << "Ports";
  869                         cout.width( 20 );
  870                         cout.fill( ' ' );
  871                         cout << "Installed";
  872                         cout.width( 20 );
  873                         cout.fill( ' ' );
  874                         cout << "Available in the ports tree" << endl << endl;
  875                     }
  876                     cout.setf( ios::left, ios::adjustfield );
  877                     cout.width( 20 );
  878                     cout.fill( ' ' );
  879                     cout <<  it->first.c_str();
  880 
  881                     cout.width( 20 );
  882                     cout.fill( ' ' );
  883                     cout << it->second.c_str();
  884 
  885                     string locked = "";
  886                     if ( m_locker.isLocked( it->first ) ) {
  887                         locked = "locked";
  888                     }
  889                     cout.width( 20 );
  890                     cout.fill( ' ' );
  891                     cout << (p->version()+"-"+p->release()).c_str()
  892                          << locked << endl;
  893                 }
  894             }
  895         }
  896     }
  897 
  898     if ( count == 0 ) {
  899         cout << "No differences found" << endl;
  900     }
  901 }
  902 
  903 /*! print path to a port */
  904 void PrtGet::printPath()
  905 {
  906     assertExactArgCount(1);
  907 
  908     initRepo();
  909     string arg = *(m_parser->otherArgs().begin());
  910     const Package* p = m_repo->getPackage( arg );
  911     if ( p ) {
  912         cout << p->path() << "/" << p->name() << endl;
  913     } else {
  914         cerr << "Package '" << arg << "' not found" << endl;
  915         m_returnValue = PG_GENERAL_ERROR;
  916         return;
  917     }
  918 }
  919 
  920 
  921 /*! helper method to print the result of an InstallTransaction */
  922 void PrtGet::evaluateResult( InstallTransaction& transaction,
  923                           bool update,
  924                           bool interrupted )
  925 {
  926     int errors = 0;
  927 
  928     // TODO: this is a duplicate, it's in install() as well
  929     string command[] = { "install", "installed" };
  930     if ( update ) {
  931         command[0] = "update";
  932         command[1] = "updated";
  933     }
  934 
  935     const list<string>& ignored = transaction.ignoredPackages();
  936     if ( ignored.size() ) {
  937         cout << endl << "-- Packages ignored" << endl;
  938         list<string>::const_iterator iit = ignored.begin();
  939 
  940         for ( ; iit != ignored.end(); ++iit ) {
  941             cout << *iit << endl;
  942         }
  943     }
  944 
  945 
  946     const list< pair<string, string> >& missing = transaction.missing();
  947     if ( missing.size() ) {
  948         ++errors;
  949         cout << endl << "-- Packages not found" << endl;
  950         list< pair<string, string> >::const_iterator mit = missing.begin();
  951 
  952         for ( ; mit != missing.end(); ++mit ) {
  953             cout << mit->first;
  954             if ( mit->second != "" ) {
  955                 cout << " from " << mit->second;
  956             }
  957             cout << endl;
  958         }
  959     }
  960 
  961     const list< pair<string, InstallTransaction::InstallInfo> >& error =
  962         transaction.installError();
  963     if ( error.size() ) {
  964         ++errors;
  965         cout << endl << "-- Packages where "
  966              << command[0] << " failed" << endl;
  967         list< pair<string, InstallTransaction::InstallInfo> >::const_iterator
  968             eit = error.begin();
  969 
  970         for ( ; eit != error.end(); ++eit ) {
  971             cout << eit->first;
  972             reportPrePost(eit->second);
  973             cout << endl;
  974         }
  975     }
  976 
  977     const list<string>& already = transaction.alreadyInstalledPackages();
  978     if ( already.size() ) {
  979         cout << endl << "-- Packages installed before this run (ignored)"
  980              << endl;
  981         list<string>::const_iterator ait = already.begin();
  982 
  983         bool isAlias;
  984         string provider;
  985         for ( ; ait != already.end(); ++ait ) {
  986             isAlias = false;
  987             cout << *ait;
  988             m_pkgDB->isInstalled(*ait, true, &isAlias, &provider);
  989 
  990             if (isAlias) {
  991                 cout << " (provided by " << provider << ")";
  992             }
  993             cout << endl;
  994         }
  995     }
  996 
  997 
  998     const list< pair<string, InstallTransaction::InstallInfo> >& inst =
  999         transaction.installedPackages();
 1000     if ( inst.size() ) {
 1001         cout << endl << "-- Packages " << command[1] << endl;
 1002         list< pair<string, InstallTransaction::InstallInfo> >::const_iterator
 1003             iit = inst.begin();
 1004 
 1005         bool atLeastOnePackageHasReadme = false;
 1006 
 1007         for ( ; iit != inst.end(); ++iit ) {
 1008             cout << iit->first;
 1009             if ( iit->second.hasReadme ) {
 1010                 if ( m_config->readmeMode() ==
 1011                      Configuration::COMPACT_README ) {
 1012                     cout << " (README)";
 1013                 }
 1014                 atLeastOnePackageHasReadme = true;
 1015             }
 1016             reportPrePost(iit->second);
 1017             cout << endl;
 1018         }
 1019 
 1020         // readme's
 1021         if ( atLeastOnePackageHasReadme ) {
 1022             if ( m_config->readmeMode() == Configuration::VERBOSE_README ) {
 1023                 cout << endl << "-- " << command[1]
 1024                      << " packages with README files:" << endl;
 1025                 iit = inst.begin();
 1026                 for ( ; iit != inst.end(); ++iit ) {
 1027                     if ( iit->second.hasReadme ) {
 1028                         cout << iit->first;
 1029                         cout << endl;
 1030                     }
 1031                 }
 1032             }
 1033         }
 1034 
 1035         cout << endl;
 1036     }
 1037 
 1038     if ( errors == 0 && !interrupted ) {
 1039         cout << "prt-get: " << command[1] << " successfully" << endl;
 1040     } else {
 1041         m_returnValue = PG_PARTIAL_INSTALL_ERROR;
 1042     }
 1043 }
 1044 
 1045 void PrtGet::reportPrePost(const InstallTransaction::InstallInfo& info) {
 1046     if (info.preState != InstallTransaction::NONEXISTENT) {
 1047         string preString = "failed";
 1048         if (info.preState == InstallTransaction::EXEC_SUCCESS) {
 1049             preString = "ok";
 1050         }
 1051         cout << " [pre: " << preString << "]";
 1052     }
 1053     if ( info.postState != InstallTransaction::NONEXISTENT) {
 1054         string postString = "failed";
 1055         if (info.postState == InstallTransaction::EXEC_SUCCESS){
 1056             postString = "ok";
 1057         }
 1058         cout << " [post: " << postString << "]";
 1059     }
 1060 
 1061 }
 1062 
 1063 /*! create a cache */
 1064 void PrtGet::createCache()
 1065 {
 1066     if ( m_parser->wasCalledAsPrtCached() ) {
 1067         cerr << m_appName << ": Can't create cache from cache. "
 1068              << "Use prt-get instead" << endl;
 1069         m_returnValue = PG_GENERAL_ERROR;
 1070         return;
 1071     }
 1072 
 1073     initRepo();
 1074     if (m_config->cacheFile() != "") {
 1075         m_cacheFile = m_config->cacheFile();
 1076     }
 1077 
 1078     Repository::WriteResult result = m_repo->writeCache( m_cacheFile );
 1079     if ( result == Repository::DIR_ERR ) {
 1080         cerr << "Can't create cache directory " << m_cacheFile << endl;
 1081         m_returnValue = PG_GENERAL_ERROR;
 1082         return;
 1083     }
 1084     if ( result == Repository::FILE_ERR ) {
 1085         cerr << "Can't open cache file " << m_cacheFile << " for writing"
 1086              << endl;
 1087         m_returnValue = PG_GENERAL_ERROR;
 1088         return;
 1089     }
 1090 
 1091 }
 1092 
 1093 /*!
 1094   \return true if v1 is greater than v2
 1095  */
 1096 bool PrtGet::greaterThan( const string& v1, const string& v2 )
 1097 {
 1098     using namespace VersionComparator;
 1099 
 1100     if (v1 == v2) {
 1101         return false;
 1102     }
 1103 
 1104 
 1105     if (m_parser->preferHigher() ||
 1106         (m_config->preferHigher() && !m_parser->strictDiff())) {
 1107 
 1108         COMP_RESULT result = compareVersions(v1, v2);
 1109         return (result == GREATER);
 1110     }
 1111 
 1112     return v1 != v2;
 1113 }
 1114 
 1115 int PrtGet::returnValue() const
 1116 {
 1117     return m_returnValue;
 1118 }
 1119 
 1120 
 1121 /*! print a list of packages available in the repository */
 1122 void PrtGet::printf()
 1123 {
 1124     map<string, string> sortedOutput;
 1125 
 1126     assertExactArgCount(1);
 1127 
 1128     initRepo();
 1129     string filter = "*";
 1130     if ( m_parser->hasFilter() ) {
 1131         filter = m_parser->filter();
 1132     }
 1133     list<Package*> packages;
 1134     m_repo->getMatchingPackages( filter, packages );
 1135     list<Package*>::const_iterator it = packages.begin();
 1136 
 1137     const string formatString = *(m_parser->otherArgs().begin());
 1138     string sortString =
 1139         StringHelper::stripWhiteSpace( m_parser->sortArgs() );
 1140     sortString += "%n"; // make it unique
 1141 
 1142     for ( ; it != packages.end(); ++it ) {
 1143         string output = formatString;
 1144         string sortkey = sortString;
 1145 
 1146         const Package* p = *it;
 1147 
 1148         StringHelper::replaceAll( output, "%n", p->name() );
 1149         StringHelper::replaceAll( output, "%u", p->url() );
 1150         StringHelper::replaceAll( output, "%p", p->path() );
 1151         StringHelper::replaceAll( output, "%v", p->version() );
 1152         StringHelper::replaceAll( output, "%r", p->release() );
 1153         StringHelper::replaceAll( output, "%d", p->description() );
 1154         StringHelper::replaceAll( output, "%e", p->dependencies() );
 1155         StringHelper::replaceAll( output, "%P", p->packager() );
 1156         StringHelper::replaceAll( output, "%M", p->maintainer() );
 1157 
 1158         StringHelper::replaceAll( output, "\\t", "\t" );
 1159         StringHelper::replaceAll( output, "\\n", "\n" );
 1160 
 1161         StringHelper::replaceAll( sortkey, "%n", p->name() );
 1162         StringHelper::replaceAll( sortkey, "%u", p->url() );
 1163         StringHelper::replaceAll( sortkey, "%p", p->path() );
 1164         StringHelper::replaceAll( sortkey, "%v", p->version() );
 1165         StringHelper::replaceAll( sortkey, "%r", p->release() );
 1166         StringHelper::replaceAll( sortkey, "%d", p->description() );
 1167         StringHelper::replaceAll( sortkey, "%e", p->dependencies() );
 1168         StringHelper::replaceAll( sortkey, "%P", p->packager() );
 1169         StringHelper::replaceAll( sortkey, "%M", p->maintainer() );
 1170 
 1171         string isInst = "no";
 1172         if ( m_pkgDB->isInstalled( p->name() ) ) {
 1173             string ip = p->name() + "-" +
 1174                 m_pkgDB->getPackageVersion( p->name() );
 1175             if ( ip == p->name() + "-" + p->version() + "-" + p->release() ) {
 1176                 isInst = "yes";
 1177             } else {
 1178                 isInst = "diff";
 1179             }
 1180         }
 1181         StringHelper::replaceAll( output, "%i", isInst );
 1182         StringHelper::replaceAll( sortkey, "%i", isInst );
 1183 
 1184         string isLocked = m_locker.isLocked( p->name() ) ? "yes" : "no";
 1185         StringHelper::replaceAll( output, "%l", isLocked );
 1186         StringHelper::replaceAll( sortkey, "%l", isLocked );
 1187 
 1188         string hasReadme = p->hasReadme() ? "yes" : "no";
 1189         StringHelper::replaceAll( output, "%R", hasReadme );
 1190         StringHelper::replaceAll( sortkey, "%R", hasReadme );
 1191 
 1192         string hasPreInstall = p->hasPreInstall() ? "yes" : "no";
 1193         StringHelper::replaceAll( output, "%E", hasPreInstall );
 1194         StringHelper::replaceAll( sortkey, "%E", hasPreInstall );
 1195 
 1196         string hasPostInstall = p->hasPostInstall() ? "yes" : "no";
 1197         StringHelper::replaceAll( output, "%O", hasPostInstall );
 1198         StringHelper::replaceAll( sortkey, "%O", hasPostInstall );
 1199 
 1200         sortedOutput[sortkey] = output;
 1201     }
 1202 
 1203     map<string, string>::iterator sortIt = sortedOutput.begin();
 1204     for ( ; sortIt != sortedOutput.end(); ++sortIt ) {
 1205         if ( StringHelper::stripWhiteSpace(sortIt->second).length() > 0) {
 1206             cout << sortIt->second;
 1207         }
 1208     }
 1209 }
 1210 
 1211 void PrtGet::readme()
 1212 {
 1213     assertExactArgCount(1);
 1214 
 1215     initRepo();
 1216     string arg = *(m_parser->otherArgs().begin());
 1217     const Package* p = m_repo->getPackage( arg );
 1218     if ( p ) {
 1219         string file = p->path() + "/" + p->name() + "/README";
 1220         printFile(file);
 1221     } else {
 1222         cerr << "Package '" << arg << "' not found" << endl;
 1223         m_returnValue = PG_GENERAL_ERROR;
 1224         return;
 1225     }
 1226 }
 1227 
 1228 bool PrtGet::printFile(const string& file)
 1229 {
 1230     if (!File::fileExists(file)) {
 1231         return false;
 1232     }
 1233 
 1234     char* pager = getenv("PAGER");
 1235     if (pager) {
 1236         Process proc(pager, file);
 1237         proc.executeShell();
 1238     } else {
 1239         FILE* fp = fopen( file.c_str(), "r" );
 1240         char buf[255];
 1241         if ( fp ) {
 1242             while ( fgets( buf, 255, fp ) ) {
 1243                 cout << buf;
 1244             }
 1245             fclose( fp );
 1246         }
 1247     }
 1248 
 1249     return true;
 1250 }
 1251 
 1252 void PrtGet::printDependent()
 1253 {
 1254     assertExactArgCount(1);
 1255 
 1256     initRepo();
 1257     string arg = *(m_parser->otherArgs().begin());
 1258     
 1259     if (m_parser) {
 1260         cout << arg << endl;
 1261         printDependent(arg, 2);
 1262     } else {
 1263         printDependent(arg, 0);
 1264     }   
 1265 }
 1266     
 1267 void PrtGet::printDependent(const string& dep, int level)
 1268 {
 1269     map<string, Package*>::const_iterator it = m_repo->packages().begin();
 1270     static map<string, bool> shownMap;
 1271 
 1272     set<const Package*> dependent;
 1273     for ( ; it != m_repo->packages().end(); ++it ) {
 1274 
 1275         // TODO: is the following line needed?
 1276         const Package* p = it->second;
 1277         if ( p && p->dependencies().find( dep ) != string::npos ) {
 1278             list<string> tokens;
 1279             StringHelper::split( p->dependencies(), ',', tokens );
 1280             list<string>::iterator it = find( tokens.begin(),
 1281                                               tokens.end(),
 1282                                               dep );
 1283             if ( it != tokens.end() ) {
 1284                 dependent.insert( p );
 1285             }
 1286         }
 1287     }
 1288 
 1289     // - there are two modes, tree and non-tree recursive mode; in
 1290     // tree mode, packages are shown multiple times, in non tree
 1291     // recursive mode they're only printed the first time; this is not
 1292     // necessarily optimal for rebuilding:
 1293     //
 1294     // a -> b -> d
 1295     //  \     ^
 1296     //   > c /
 1297     // 
 1298     // trying to rebuild 'd' before 'c' might possibly fail
 1299     string indent = "";
 1300     if (m_parser->printTree()) {
 1301         for (int i = 0; i < level; ++i) {
 1302             indent += " ";
 1303         }
 1304     }
 1305     set<const Package*>::iterator it2 = dependent.begin();
 1306     for ( ; it2 != dependent.end(); ++it2 ) {
 1307         const Package* p = *it2;
 1308         
 1309         if (m_parser->recursive() && !m_parser->printTree()) {
 1310             if (shownMap[p->name()]) {
 1311                 continue;
 1312             }
 1313             shownMap[p->name()] = true;
 1314         }
 1315                 
 1316         if ( m_parser->all() || m_pkgDB->isInstalled( p->name() ) ) {
 1317                         
 1318             cout << indent << p->name();
 1319             if ( m_parser->verbose() > 0 ) {
 1320                 cout << " " << p->version() << "-" << p->release();
 1321             }
 1322             if ( m_parser->verbose() > 1 ) {
 1323                 cout << ":  " << p->description();
 1324             }
 1325 
 1326             cout << endl;
 1327             
 1328             if (m_parser->recursive()) {
 1329                 printDependent( p->name(), level+2 );
 1330             }
 1331         }
 1332     }
 1333 }
 1334 
 1335 void PrtGet::listOrphans()
 1336 {
 1337     initRepo();
 1338     map<string, string> installed = m_pkgDB->installedPackages();
 1339     map<string, bool> required;
 1340     map<string, string>::iterator it = installed.begin();
 1341     
 1342     for (; it != installed.end(); ++it) {
 1343         list<string> tokens;
 1344         const Package* p = m_repo->getPackage(it->first);
 1345         if (p) {
 1346             StringHelper::split( p->dependencies(), ',', tokens );
 1347             list<string>::iterator lit = tokens.begin();
 1348             for (; lit != tokens.end(); ++lit) {
 1349                 required[*lit] = true;
 1350             }
 1351         }
 1352     }   
 1353     
 1354     // - we could store the package pointer in another map to avoid
 1355     // another getPackage lockup, but it seems better to optimized for
 1356     // memory since it's only used when called with -vv
 1357     
 1358     it = installed.begin();
 1359     for (; it != installed.end(); ++it) {
 1360         if (!required[it->first]) {
 1361             cout << it->first;
 1362             if ( m_parser->verbose() > 0 ) {
 1363                 cout << " " << it->second;
 1364             }
 1365             if ( m_parser->verbose() > 1 ) {
 1366                 const Package* p = m_repo->getPackage(it->first);
 1367                 if (p) {
 1368                     cout << ":  " << p->description();
 1369                 }
 1370             }
 1371             cout << endl;
 1372         }
 1373     }
 1374 }
 1375 
 1376 
 1377 void PrtGet::warnPackageNotFound(InstallTransaction& transaction)
 1378 {
 1379     cerr << "The package '";
 1380     cerr << transaction.missing().begin()->first;
 1381     cerr << "' could not be found: " << endl;
 1382 }
 1383 
 1384 void PrtGet::sysup()
 1385 {
 1386     // TODO: refactor getDifferentPackages from diff/quickdiff
 1387     initRepo();
 1388 
 1389     list<string>* target;
 1390     list<string> packagesToUpdate;
 1391     list<string> sortedList;
 1392 
 1393     const map<string, string>& installed = m_pkgDB->installedPackages();
 1394     map<string, string>::const_iterator it = installed.begin();
 1395     const Package* p = 0;
 1396     for ( ; it != installed.end(); ++it ) {
 1397         if ( !m_locker.isLocked( it->first ) ) {
 1398             p = m_repo->getPackage( it->first );
 1399             if ( p ) {
 1400                 if ( greaterThan( p->version() + "-" + p->release(),
 1401                                   it->second ) ) {
 1402                     packagesToUpdate.push_back( it->first );
 1403                 }
 1404             }
 1405         }
 1406     }
 1407 
 1408     if ( packagesToUpdate.empty() ) {
 1409         cout << "System is up to date" << endl;
 1410         return;
 1411     }
 1412 
 1413     if ( m_parser->nodeps() ) {
 1414         target = &packagesToUpdate;
 1415     } else {
 1416         // sort by dependency
 1417 
 1418         // TODO: refactor code from printDepends
 1419         InstallTransaction depTrans( packagesToUpdate,
 1420                                      m_repo, m_pkgDB, m_config );
 1421         InstallTransaction::InstallResult result = depTrans.calcDependencies();
 1422         if ( result == InstallTransaction::CYCLIC_DEPEND ) {
 1423             cerr << "cyclic dependencies" << endl;
 1424             m_returnValue = PG_GENERAL_ERROR;
 1425             return;
 1426         } else if ( result == InstallTransaction::PACKAGE_NOT_FOUND ) {
 1427             warnPackageNotFound(depTrans);
 1428             m_returnValue = PG_GENERAL_ERROR;
 1429             return;
 1430         }
 1431 
 1432         const list<string>& deps = depTrans.dependencies();
 1433         if ( deps.size() > 0 ) {
 1434             list<string>::const_iterator it = deps.begin();
 1435             for ( ; it != deps.end(); ++it ) {
 1436                 if ( find( packagesToUpdate.begin(),
 1437                            packagesToUpdate.end(), *it ) !=
 1438                      packagesToUpdate.end() ) {;
 1439                      sortedList.push_back( *it );
 1440                 }
 1441             }
 1442         }
 1443 
 1444         target = &sortedList;
 1445     }
 1446 
 1447     InstallTransaction transaction( *target,
 1448                                     m_repo, m_pkgDB, m_config );
 1449     executeTransaction( transaction, true, false );
 1450 }
 1451 
 1452 
 1453 void PrtGet::expandWildcardsPkgDB( const list<char*>& in,
 1454                                    map<string, string>& target )
 1455 {
 1456     list<char*>::const_iterator it = in.begin();
 1457     for ( ; it != in.end(); ++it ) {
 1458         map<string, string> l;
 1459         m_pkgDB->getMatchingPackages( *it, l, m_useRegex );
 1460         map<string, string>::iterator iit = l.begin();
 1461         for ( ; iit != l.end(); ++iit ) {
 1462             target[iit->first] = iit->second;
 1463         }
 1464     }
 1465 }
 1466 
 1467 void PrtGet::expandWildcardsRepo( const list<char*>& in, list<string>& target )
 1468 {
 1469     list<char*>::const_iterator it = in.begin();
 1470 
 1471     for ( ; it != in.end(); ++it ) {
 1472         list<Package*> l;
 1473         m_repo->getMatchingPackages( *it, l );
 1474         list<Package*>::iterator iit = l.begin();
 1475         for ( ; iit != l.end(); ++iit ) {
 1476             target.push_back( (*iit)->name() );
 1477         }
 1478     }
 1479 }
 1480 
 1481 
 1482 void PrtGet::current()
 1483 {
 1484     assertExactArgCount(1);
 1485 
 1486     const map<string, string>& installed = m_pkgDB->installedPackages();
 1487     map<string, string>::const_iterator it = installed.begin();
 1488     string search = *(m_parser->otherArgs().begin());
 1489 
 1490     for ( ; it != installed.end(); ++it ) {
 1491         if ( it->first == search ) {
 1492             cout << it->second.c_str() << endl;
 1493             return;
 1494         }
 1495     }
 1496 
 1497     cout << "Package " << search << " not installed" << endl;
 1498     m_returnValue = 1;
 1499 }
 1500 
 1501 SignalHandler::HandlerResult PrtGet::handleSignal( int signal )
 1502 {
 1503     // TODO: second argument could also be true:
 1504     // TODO: kill installtransaction
 1505 
 1506     cout << "prt-get: interrupted" << endl;
 1507     if ( m_currentTransaction ) {
 1508         evaluateResult( *m_currentTransaction, false, true );
 1509     }
 1510 }
 1511 
 1512 /*!
 1513   find files matching a pattern in repository
 1514 
 1515   \sa Repository::getMatchingPackages()
 1516 */
 1517 void PrtGet::fsearch()
 1518 {
 1519     assertMinArgCount(1);
 1520 
 1521     string arg = "*";
 1522     if ( m_parser->otherArgs().size() == 1 ) {
 1523         arg = *(m_parser->otherArgs().begin());
 1524     }
 1525 
 1526     initRepo();
 1527     const map<string, Package*>& packages = m_repo->packages();
 1528     map<string, Package*>::const_iterator it = packages.begin();
 1529     bool first = true;
 1530     for ( ; it != packages.end(); ++it ) {
 1531         list<string> matches;
 1532         string fp =
 1533             it->second->path() + "/" +
 1534             it->second->name() + "/" + ".footprint";
 1535         if ( File::grep( fp, arg, matches,
 1536                          m_parser->fullPath(),
 1537                          m_useRegex)) {
 1538             if ( matches.size() > 0 ) {
 1539                 if ( first ) {
 1540                     first = false;
 1541                 } else {
 1542                     cout << endl;
 1543                 }
 1544                 cout << "Found in "
 1545                      << it->second->path() << "/"
 1546                      << it->first << ":" << endl;
 1547                 list<string>::iterator it = matches.begin();
 1548                 for ( ; it != matches.end(); ++it ) {
 1549                     cout << "  " << *it << endl;
 1550                 }
 1551             }
 1552         }
 1553     }
 1554 
 1555     if ( first ) {
 1556         m_returnValue = PG_GENERAL_ERROR;
 1557     }
 1558 }
 1559 
 1560 void PrtGet::setLock( bool lock )
 1561 {
 1562     assertMinArgCount(1);
 1563 
 1564     if ( lock ) {
 1565         initRepo();
 1566     }
 1567 
 1568     const list<char*>& args = m_parser->otherArgs();
 1569     list<char*>::const_iterator it = args.begin();
 1570     for ( ; it != args.end(); ++it ) {
 1571         if ( lock ) {
 1572             if (m_pkgDB->isInstalled( *it )) {
 1573                 if (!m_locker.lock( *it )) {
 1574                     cerr << "Already locked: " << *it << endl;
 1575                     m_returnValue = PG_GENERAL_ERROR;
 1576                 }
 1577             } else {
 1578                 cerr << "Package '" << *it << "' not found" << endl;
 1579                 m_returnValue = PG_GENERAL_ERROR;
 1580                 return;
 1581             }
 1582 
 1583         } else {
 1584             if ( !m_locker.unlock( *it ) ) {
 1585                 cerr << "Not locked previously: " << *it << endl;
 1586                 m_returnValue = PG_GENERAL_ERROR;
 1587                 return;
 1588             }
 1589         }
 1590     }
 1591 								
 1592     if (!m_locker.store()) {
 1593         cerr << "Failed to write lock data" << endl;
 1594         m_returnValue = PG_GENERAL_ERROR;
 1595     }
 1596 }
 1597 
 1598 void PrtGet::listLocked()
 1599 {
 1600     // shares some code with listInstalled
 1601     if ( m_locker.openFailed() ) {
 1602         cerr << "Failed to open lock data file" << endl;
 1603         m_returnValue = PG_GENERAL_ERROR;
 1604     }
 1605 
 1606     const map<string, string>& l = m_pkgDB->installedPackages();
 1607 
 1608     if ( l.empty() ) {
 1609         return;
 1610     }
 1611 
 1612     if ( m_parser->verbose() > 1 ) {
 1613         // warning: will slow down the process...
 1614         initRepo();
 1615     }
 1616 
 1617 
 1618     const vector<string>& lockedPackages = m_locker.lockedPackages();
 1619     vector<string>::const_iterator it = lockedPackages.begin();
 1620     for ( ; it != lockedPackages.end(); ++it ) {
 1621         cout << *it;
 1622         if ( m_parser->verbose() > 0 ) {
 1623             cout << " " << m_pkgDB->getPackageVersion(*it);
 1624         }
 1625         if ( m_parser->verbose() > 1 ) {
 1626             const Package* p = m_repo->getPackage( *it );
 1627             if ( p ) {
 1628                 cout << ": " << p->description();
 1629             }
 1630         }
 1631 
 1632         cout << endl;
 1633 
 1634     }
 1635 }
 1636 
 1637 
 1638 void PrtGet::edit()
 1639 {
 1640     assertMinArgCount(1);
 1641     assertMaxArgCount(2);
 1642 
 1643     char* editor = getenv("EDITOR");
 1644     if (editor) {
 1645         initRepo();
 1646 
 1647         list<char*>::const_iterator it = m_parser->otherArgs().begin();
 1648         string arg = *it;
 1649         const Package* p = m_repo->getPackage( arg );
 1650         if ( p ) {
 1651             string fileName = "Pkgfile";
 1652             if (++it != m_parser->otherArgs().end()) {
 1653                 fileName = *it;
 1654             }
 1655             string file = p->path() + "/" + p->name() + "/" + fileName;
 1656             Process proc(editor, file);
 1657             m_returnValue = proc.executeShell();
 1658             if (m_returnValue) {
 1659                 cerr << "error while execution the editor" << endl;
 1660             }
 1661         } else {
 1662             cerr << "Package '" << arg << "' not found" << endl;
 1663             m_returnValue = PG_GENERAL_ERROR;
 1664             return;
 1665         }
 1666 
 1667     } else {
 1668         cerr << "Environment variable EDITOR not set" << endl;;
 1669         m_returnValue = PG_GENERAL_ERROR;
 1670         return;
 1671     }
 1672 
 1673 }
 1674 
 1675 void PrtGet::ls()
 1676 {
 1677     assertExactArgCount(1);
 1678 
 1679     initRepo();
 1680 
 1681     list<char*>::const_iterator it = m_parser->otherArgs().begin();
 1682     string arg = *it;
 1683     const Package* p = m_repo->getPackage( arg );
 1684     if ( p ) {
 1685         string dirname = p->path() + "/" + p->name();
 1686         DIR* dir = opendir(dirname.c_str());
 1687         struct dirent* entry;
 1688         vector<string> files;
 1689         while (entry = readdir(dir)) {
 1690             string dName = entry->d_name;
 1691             if (dName != "." && dName != "..") {
 1692                 files.push_back(dName);
 1693             }
 1694         }
 1695         closedir(dir);
 1696 
 1697         sort(files.begin(), files.end());
 1698         vector<string>::iterator fit = files.begin();
 1699         for (; fit != files.end(); ++fit) {
 1700             if (m_parser->printPath()) {
 1701                 cout << p->path() + "/" +p->name() + "/";
 1702             }
 1703             cout << *fit << endl;
 1704         }
 1705     } else {
 1706         cerr << "Package '" << arg << "' not found" << endl;
 1707         m_returnValue = PG_GENERAL_ERROR;
 1708         return;
 1709     }
 1710 }
 1711 
 1712 void PrtGet::cat()
 1713 {
 1714     assertMinArgCount(1);
 1715     assertMaxArgCount(2);
 1716 
 1717     initRepo();
 1718 
 1719     list<char*>::const_iterator it = m_parser->otherArgs().begin();
 1720     string arg = *it;
 1721     const Package* p = m_repo->getPackage( arg );
 1722     if ( p ) {
 1723         string fileName = "Pkgfile";
 1724         if (++it != m_parser->otherArgs().end()) {
 1725             fileName = *it;
 1726         }
 1727         string file = p->path() + "/" + p->name() + "/" + fileName;
 1728         if (!printFile(file)) {
 1729             cerr << "File '" << *it << "' not found" << endl;
 1730             m_returnValue = PG_GENERAL_ERROR;
 1731             return;
 1732         }
 1733     } else {
 1734         cerr << "Package '" << arg << "' not found" << endl;
 1735         m_returnValue = PG_GENERAL_ERROR;
 1736         return;
 1737     }
 1738 }
 1739 
 1740 void PrtGet::remove()
 1741 {
 1742     assertMinArgCount(1);
 1743 
 1744     list<string> removed;
 1745     list<string> failed;
 1746     list<string> notInstalled;
 1747 
 1748     if ( m_parser->isTest() ) {
 1749         cout << "*** " << m_appName << ": test mode" << endl;
 1750     }
 1751 
 1752     string command = InstallTransaction::PKGRM_DEFAULT_COMMAND;
 1753     if (m_config->removeCommand() != "") {
 1754         command = m_config->removeCommand();
 1755     }
 1756 
 1757     const list<char*>& args = m_parser->otherArgs();
 1758     list<char*>::const_iterator it = args.begin();
 1759     for ( ; it != args.end(); ++it ) {
 1760         if (m_pkgDB->isInstalled(*it)) {
 1761             // TODO: prettify
 1762             string args = "";
 1763             if (m_parser->installRoot() != "") {
 1764                 args = "-r " + m_parser->installRoot() + " ";
 1765             }
 1766             args += (m_parser->pkgrmArgs() + " " + *it);
 1767 
 1768             Process proc(command, args);
 1769             if (m_parser->isTest() || proc.executeShell() == 0) {
 1770                 removed.push_back(*it);
 1771                 if (m_locker.isLocked(*it)) {
 1772                     m_locker.unlock(*it);
 1773                     m_locker.store();
 1774                 }
 1775             } else {
 1776                 failed.push_back(*it);
 1777             }
 1778         } else {
 1779             notInstalled.push_back(*it);
 1780         }
 1781     }
 1782 
 1783     if ( removed.size() ) {
 1784         cout << endl << "-- Packages removed"
 1785              << endl;
 1786         list<string>::const_iterator it = removed.begin();
 1787 
 1788         for ( ; it != removed.end(); ++it ) {
 1789             cout << *it << endl;
 1790         }
 1791     }
 1792 
 1793     if ( failed.size() ) {
 1794         cout << endl << "-- Packages where removal failed"
 1795              << endl;
 1796         list<string>::const_iterator it = failed.begin();
 1797 
 1798         for ( ; it != failed.end(); ++it ) {
 1799             cout << *it << endl;
 1800         }
 1801     }
 1802 
 1803     if ( notInstalled.size() ) {
 1804         cout << endl << "-- Packages which were not installed"
 1805              << endl;
 1806         list<string>::const_iterator it = notInstalled.begin();
 1807 
 1808         for ( ; it != notInstalled.end(); ++it ) {
 1809             cout << *it << endl;
 1810         }
 1811     }
 1812 
 1813     if ( m_parser->isTest() ) {
 1814         cout << "*** " << m_appName << ": test mode end" << endl;
 1815     }
 1816 
 1817 
 1818 
 1819 }
 1820 
 1821 void PrtGet::assertMaxArgCount(int count)
 1822 {
 1823     if ( m_parser->otherArgs().size() > count ) {
 1824         argCountFailure(count, "at most");
 1825     }
 1826 }
 1827 
 1828 void PrtGet::assertExactArgCount(int count)
 1829 {
 1830     if ( m_parser->otherArgs().size() != count ) {
 1831         argCountFailure(count, "exactly");
 1832     }
 1833 }
 1834 
 1835 void PrtGet::assertMinArgCount(int count)
 1836 {
 1837      if ( m_parser->otherArgs().size() < count ) {
 1838          argCountFailure(count, "at least");
 1839      }
 1840 }
 1841 
 1842 void PrtGet::argCountFailure(int count, const string& specifier)
 1843 {
 1844     cerr << m_appName << " "
 1845          << m_parser->commandName() << " takes " << specifier << " "
 1846          << count << (count > 1 ? " arguments" : " argument") << endl;
 1847     exit(PG_ARG_ERROR);
 1848 }
 1849 
 1850 
 1851 void PrtGet::printDependTree()
 1852 {
 1853     assertExactArgCount(1);
 1854 
 1855     initRepo();
 1856 
 1857     list<char*>::const_iterator it = m_parser->otherArgs().begin();
 1858     string arg = *it;
 1859     const Package* p = m_repo->getPackage( arg );
 1860     if (!p) {
 1861         cerr << "Package '" << arg << "' not found" << endl;
 1862         m_returnValue = PG_GENERAL_ERROR;
 1863         return;
 1864     }
 1865 
 1866     if (p->dependencies().length() > 0) {
 1867 
 1868         cout << "-- dependencies ([i] = installed";
 1869         if (!m_parser->all()) {
 1870             cout << ", '-->' = seen before";
 1871         }
 1872         cout << ")" << endl;
 1873         if ( m_pkgDB->isInstalled( *it ) ) {
 1874             cout << "[i] ";
 1875         } else {
 1876             cout << "[ ] ";
 1877         }
 1878         cout << p->name() << endl;
 1879         printDepsLevel(2, p);
 1880     }
 1881 
 1882 }
 1883 
 1884 void PrtGet::printDepsLevel(int indent, const Package* package)
 1885 {
 1886     static map<string, bool> shownMap;
 1887 
 1888     list<string> deps;
 1889     StringHelper::split(package->dependencies(), ',', deps);
 1890     list<string>::iterator it = deps.begin();
 1891     bool isAlias = false;
 1892     string aliasName = "";
 1893 
 1894     for (; it != deps.end(); ++it) {
 1895         if ( m_pkgDB->isInstalled( *it, true, &isAlias, &aliasName ) ) {
 1896             cout << "[i] ";
 1897         } else {
 1898             cout << "[ ] ";
 1899         }
 1900         for (int i = 0; i < indent; ++i) {
 1901             cout << " ";
 1902         }
 1903         cout << *it;
 1904         if (isAlias) {
 1905             cout << " (provided by " << aliasName << ")";
 1906         }
 1907         const Package* p = m_repo->getPackage( *it );
 1908         if (p) {
 1909             if  (p->dependencies().length() > 0) {
 1910                 map<string, bool>::iterator shownIt = shownMap.find(*it);
 1911                 if (shownIt != shownMap.end()) {
 1912                     cout << " -->" << endl;;
 1913                 } else {
 1914                     cout << endl;
 1915                     printDepsLevel(indent+2, p);
 1916                     if (!m_parser->all()) {
 1917                         shownMap[*it] = true;
 1918                     }
 1919                 }
 1920             } else {
 1921                 cout << endl;
 1922             }
 1923         } else {
 1924             cout << " (not found in ports tree)" << endl;
 1925         }
 1926     }
 1927 }
 1928 
 1929 void PrtGet::dumpConfig()
 1930 {
 1931 
 1932     cout.setf( ios::left, ios::adjustfield );
 1933     cout.width( 20 );
 1934     cout.fill( ' ' );
 1935     cout << "Alias file: " << PkgDB::ALIAS_STORE << endl;
 1936 
 1937     cout.setf( ios::left, ios::adjustfield );
 1938     cout.width( 20 );
 1939     cout.fill( ' ' );
 1940     cout << "Ext. dep. file: "
 1941          << Repository::EXTERNAL_DEPENDENCY_FILE << endl;
 1942 
 1943 
 1944     if (!m_parser->noStdConfig()) {
 1945         string fName = CONF_FILE;
 1946         if ( m_parser->isAlternateConfigGiven() ) {
 1947             fName = m_parser->alternateConfigFile();
 1948         }
 1949         cout.setf( ios::left, ios::adjustfield );
 1950         cout.width( 20 );
 1951         cout.fill( ' ' );
 1952         cout << "Configuration file: " << fName << endl;
 1953     }
 1954 
 1955     if (m_config->cacheFile() != "") {
 1956         cout.setf( ios::left, ios::adjustfield );
 1957         cout.width( 20 );
 1958         cout.fill( ' ' );
 1959         cout << "Cache file: " << m_config->cacheFile() << endl;
 1960     }
 1961     if (m_config->makeCommand() != "") {
 1962         cout.setf( ios::left, ios::adjustfield );
 1963         cout.width( 20 );
 1964         cout.fill( ' ' );
 1965         cout << "Make command file: " << m_config->makeCommand() << endl;
 1966     }
 1967     if (m_config->addCommand() != "") {
 1968         cout.setf( ios::left, ios::adjustfield );
 1969         cout.width( 20 );
 1970         cout.fill( ' ' );
 1971         cout << "Add command: " << m_config->addCommand() << endl;
 1972     }
 1973     if (m_config->removeCommand() != "") {
 1974         cout.setf( ios::left, ios::adjustfield );
 1975         cout.width( 20 );
 1976         cout.fill( ' ' );
 1977         cout << "Remove command: " << m_config->removeCommand() << endl;
 1978     }
 1979     if (m_config->runscriptCommand() != "") {
 1980         cout.setf( ios::left, ios::adjustfield );
 1981         cout.width( 20 );
 1982         cout.fill( ' ' );
 1983         cout << "Runscript command: " << m_config->runscriptCommand() << endl;
 1984     }
 1985 
 1986     cout.setf( ios::left, ios::adjustfield );
 1987     cout.width( 20 );
 1988     cout.fill( ' ' );
 1989     cout << "Run scripts: " <<(m_config->runScripts() ? "yes" : "no" )
 1990          << endl;
 1991 
 1992     cout.setf( ios::left, ios::adjustfield );
 1993     cout.width( 20 );
 1994     cout.fill( ' ' );
 1995     cout << "Keep higher version:" <<(m_config->preferHigher() ? "yes" : "no" )
 1996          << endl;
 1997 
 1998     cout.setf( ios::left, ios::adjustfield );
 1999     cout.width( 20 );
 2000     cout.fill( ' ' );
 2001     cout << "Readme mode:  ";
 2002     switch (m_config->readmeMode()) {
 2003         case Configuration::VERBOSE_README:
 2004             cout << "verbose";
 2005             break;
 2006         case Configuration::COMPACT_README:
 2007             cout << "compact";
 2008             break;
 2009         case Configuration::NO_README:
 2010             cout << "off";
 2011             break;
 2012     }
 2013     cout << endl;
 2014 
 2015     cout << endl;
 2016 
 2017     if (m_config->logFilePattern() != "") {
 2018         cout.setf( ios::left, ios::adjustfield );
 2019         cout.width( 20 );
 2020         cout.fill( ' ' );
 2021         cout << "Log file: " << m_config->logFilePattern() << endl;
 2022     }
 2023     cout.setf( ios::left, ios::adjustfield );
 2024     cout.width( 20 );
 2025     cout.fill( ' ' );
 2026     cout << "  Write log: " << (m_config->writeLog() ? "yes" : "no" ) << endl;
 2027     cout.setf( ios::left, ios::adjustfield );
 2028     cout.width( 20 );
 2029     cout.fill( ' ' );
 2030     cout << "  Append log: " <<(m_config->appendLog() ? "yes" : "no" ) << endl;
 2031 
 2032 
 2033 
 2034     cout << endl;
 2035     list< pair<string, string> >::const_iterator it =
 2036         m_config->rootList().begin();
 2037     cout << "Port "
 2038          << (m_config->rootList().size() == 1 ? "directory" : "directories")
 2039          << ": " << endl;
 2040     for (; it != m_config->rootList().end(); ++it) {
 2041         cout << " " << it->first;
 2042         if (it->second != "") {
 2043             cout << " (" << it->second << ")";
 2044         }
 2045         cout << endl;
 2046     }
 2047 }

Generated by cgit