Logo Search packages:      
Sourcecode: paco version File versions  Download package

options.cc

//=======================================================================
// options.cc
//-----------------------------------------------------------------------
// This file is part of the package paco
// Copyright (C) 2004-2009 David Rosal
// For more information visit http://paco.sourceforge.net
//=======================================================================

#include "config.h"
#include "global.h"
#include "options.h"
#include "pkgset.h"
#include <fstream>
#include <iostream>
#include <getopt.h>

using std::string;
using std::endl;
using std::cout;
using std::cerr;
using namespace Paco;

// Forward declarations
static SortType getSortType(string const& s);
static void help();
static void version();
static int toSizeUnit(char* arg);
static string getDirname();
static void dieHelp(string const& msg = "");


Options::Options(int argc, char* argv[])
:
      std::bitset<N_BITS>(0),
      mSortType(NO_SORT),
      mSizeUnit(HUMAN_READABLE),
      mSkip(),
      mLogPkg(),
      mInclude(Config::include()),
      mExclude(Config::exclude()),
      mMode(PKGLIST),
      mArgs()
{
      if (argc == 1)
            dieHelp("No input packages");

      set(LOG_IGNORE_ERRORS, Config::logIgnoreErrors());

      enum {
            OPT_SORT = 1,
            OPT_REMOVE_SHARED,
            OPT_VERSION,
            OPT_LOG_IGNORE_ERRORS, 
            OPT_LOG_MISSING, 
            OPT_LOG_IGNORE_SHARED
      };
      struct option opt[] = {
            // General options
            { "help", 0, 0, 'h' },
            { "version", 0, 0, OPT_VERSION },
            { "logdir", 1, 0, 'L' },
            { "verbose", 0, 0, 'v' },
            { "all", 0, 0, 'a' },
            { "expand", 0, 0, 'x' },
            // Database maintenance options
            { "update", 0, 0, 'u' },
            { "unlog", 0, 0, 'U' },
            // List options
            { "date", 0, 0, 'd' },
            { "sort", 1, 0, OPT_SORT },
            { "block-size", 1, 0, 'b' },
            { "kilobytes", 0, 0, 'k' },
            { "size", 0, 0, 's' },
            { "missing-size", 0, 0, 'n' },
            { "files", 0, 0, 'f' },
            { "missing-files", 0, 0, 'm' },
            { "shared", 0, 0, 'c' },
            { "non-shared", 0, 0, 'N' },
            { "who-shares", 0, 0, 'w' },
            { "reverse", 0, 0, 'R' },
            { "total", 0, 0, 't' },
            { "one-column", 0, 0, '1' },
            { "symlinks", 0, 0, 'y' },
            { "no-package-name", 0, 0, 'z' },
            // Info options
            { "info", 0, 0, 'i' },
            { "query", 0, 0, 'q' },
            { "owner", 0, 0, 'q' },
            { "configure-options", 0, 0, 'o' },
            // Remove options
            { "remove", 0, 0, 'r' },
            { "remove-shared", 0, 0, OPT_REMOVE_SHARED },
            { "batch", 0, 0, 'B' },
            { "skip", 1, 0, 'e' },
            // Log options
            { "log", 0, 0, 'l' },
            { "package", 1, 0, 'p' },
            { "include", 1, 0, 'I' },
            { "exclude", 1, 0, 'E' },
            { "append", 0, 0, '+' },
            { "dirname", 0, 0, 'D' },
            { "log-missing", 0, 0, OPT_LOG_MISSING },
            { "ignore-errors", 0, 0, OPT_LOG_IGNORE_ERRORS },
            { "ignore-shared", 0, 0, OPT_LOG_IGNORE_SHARED },
            { NULL ,0, 0, 0 },
      };
      
      // Deal with the weird non-getopt-friendly '-p+' option 
    for (int i = 1; i < argc; ++i) {
        if (argv[i][0] == '-' && argv[i][1] != '-') {
                  for (char* p = argv[i]; (p = strstr(p, "p+")); )
                        memcpy(p, "+p", 2);
            }
    }

      int op, r = 0;
      bool cLower(false);

    while ((op = getopt_long(argc, argv,
            "1+aBb:cCdDe:E:fFhiI:klL:nNmMop:qrRstuUvwxyz", opt, 0)) >= 0) {
        
            switch (op) {
                  
                  // General options
                  case OPT_VERSION: version();
                  case 'h': help();
                  case 'L': Config::logdir(optarg); break;
                  case 'v': gOut.verbosity()++; break;
                  case 'a': set(ALL_PKGS); break;
                  case 'x': break;  // obsolete
                  
                  // Database options
                  case 'U': setMode(UNLOG, op); break;
                  case 'u': setMode(UPDATE, op); break;

                  // Info options
                  case 'i': setMode(INFO, op); break;
                  case 'o': setMode(CONFOPTS, op); break;
                  case 'q': setMode(QUERYFILES, op); break;

                  // General list options
                  case OPT_SORT: mSortType = getSortType(optarg); break;
                  case 'R': set(REVERSE_SORT); break;
                  case 't': set(PRINT_TOTALS); break;
                  case 's': set(PRINT_SIZE); break;
                  case 'b': mSizeUnit = toSizeUnit(optarg); break;
                  case 'k': mSizeUnit = KILOBYTE; break;
                  
                  // Package list options
                  case 'd': case 'n': case 'F': case 'M': case 'C': case '1':
                        setMode(PKGLIST, op);
                        switch (op) {
                              case 'd':
                                    set(test(PRINT_DAY) ? PRINT_HOUR : PRINT_DAY); break;
                              case 'n': set(PRINT_SIZE_MISS); break;
                              case 'F': set(PRINT_FILES_INST); break;
                              case 'M': set(PRINT_FILES_MISS); break;
                              case 'C': set(PRINT_FILES_SHARED); break;
                              case '1': set(ONE_COLUMN_LIST); break;
                        }
                        break;

                  // File list options
                  case 'f': case 'm': case 'c': case 'N': case 'y': case 'z': case 'w':
                        setMode(FILELIST, op);
                        switch (op) {
                              case 'f': set(PRINT_FILES_INST); break;
                              case 'm': set(PRINT_FILES_MISS); break;
                              case 'c': set(PRINT_FILES_SHARED); cLower = true; break;
                              case 'N': set(PRINT_FILES_NON_SHARED); break;
                              case 'y': set(PRINT_SYMLINKS); break;
                              case 'z': set(NO_PRINT_PKG_NAME); break;
                              case 'w': set(PRINT_WHO_SHARES); break;
                        }
                        break;
                  
                  // Remove options
                  case 'r': case 'e': case 'B':
                        setMode(REMOVE, op);
                        switch (op) {
                              case 'r': set(REMOVE_UNLOG, (r++ > 0)); break;
                              case 'e': mSkip = optarg; break;
                              case 'B': set(REMOVE_BATCH); break;
                        }
                        break;
                  case OPT_REMOVE_SHARED: set(REMOVE_SHARED); break;
                  
                  // Log options
                  case 'l': case 'p': case 'D': case 'I': case 'E': case '+':
                        setMode(LOG, op);
                        switch (op) {
                              case 'p': mLogPkg = optarg; break;
                              case 'D': mLogPkg = getDirname(); break;
                              case 'I': mInclude = optarg; break;
                              case 'E': mExclude = optarg; break;
                              case '+': set(LOG_APPEND); break;
                        }
                        break;
                  case OPT_LOG_MISSING: set(LOG_MISSING); break;
                  case OPT_LOG_IGNORE_ERRORS: set(LOG_IGNORE_ERRORS); break;
                  case OPT_LOG_IGNORE_SHARED: set(LOG_IGNORE_SHARED); break;
                  
                  default: dieHelp();
            }
      }

      mArgs.assign(argv + optind, argv + argc);

      if (mArgs.empty()) {
            if (mMode == QUERYFILES)
                  dieHelp("No input files");
            else if ((!allPkgs() && mMode != LOG) || mMode == REMOVE)
                  dieHelp("No input packages");
      }
            
      if (filesShared() && filesNonShared())
            dieHelp("-cN: Incompatible options");
      else if (!filesInst() && !filesMiss()) {
            if (filesShared() && cLower)
                  dieHelp("Option -c requires at least one of -f or -m");
            else if (filesNonShared())
                  dieHelp("Option -N requires at least one of -f or -m");
      }
      
      if (mMode == LOG && !mLogPkg.empty()) {
            if (!isalnum(mLogPkg.at(0)) || mLogPkg.find('/') != string::npos)
                  throw X(mLogPkg + ": Invalid package name");
      }

      if (!Config::checkLogdir())
            throw X(Config::logdir() + ": Invalid log directory");
      
      if (!Config::logdirWritable()) {
            if (mMode == UPDATE || mMode == UNLOG || mMode == REMOVE)
                  throw XErrno(Config::logdir());
            else if (mMode == LOG && !mLogPkg.empty()) {
                  if (errno != ENOENT || mkdir(Config::logdir().c_str(), 0755) < 0)
                        throw XErrno(Config::logdir());
            }
      }
}


void Options::setMode(Mode m, char optchar)
{
      static char modes[NMODES] = { 0 };
      string optstr("-");

      modes[m] = optchar;

      for (int i = 0; i < NMODES; ++i) {
            if (modes[i]) {
                  optstr += modes[i];
                  if (optstr.size() > 2)
                        dieHelp(optstr + ": Incompatible options");
            }
      }

      mMode = m;
}


static void help()
{
cout <<
"paco - the source code pacKAGE oRGANIZER\n\n"
"Usage:\n"
"  paco [OPTIONS] <packages|files|command>\n\n"
"General options:\n"
"  -L, --logdir=DIR         Use DIR as the log directory.\n"
"  -a, --all                Apply to all logged packages (not with -r).\n"
"  -v, --verbose            Verbose output (-vv produces debugging messages).\n"
"  -h, --help               Display this help message.\n"
"      --version            Display version information.\n\n"
"Database maintenance options:\n"
"  -u, --update             Update the log of the package.\n"
"  -U, --unlog              Remove the log of the package.\n\n"
"General list options:\n"
"  -b, --block-size=SIZE    Use blocks of SIZE bytes for the sizes.\n"
"  -k, --kilobytes          Like '--block-size=1024'.\n"
"  -R, --reverse            Reverse order while sorting.\n"
"      --sort=WORD          Sort by WORD: 'name', 'date' (or 'time'), 'size',\n"
"                           'files', 'missing-size' or 'missing-files'.\n"
"  -t, --total              Print totals.\n\n"
"Package list options:\n"
"  -1, --one-column         Print one package per line.\n"
"  -F                       Print the number of installed files.\n"
"  -M                       Print the number of missing files.\n"
"  -C                       Print the number of shared files.\n"
"  -d, --date               Print the installation day (-dd prints the hour too).\n"
"  -s, --size               Print the installed size of each package.\n"
"  -n, --missing-size       Print the missing size of each package.\n\n"
"File list options:\n"
"  -f, --files              List installed files.\n"
"  -m, --missing-files      List missing files.\n"
"  -c, --shared             With -f and/or -m: List only the shared files.\n"
"  -N, --non-shared         With -f and/or -m: List only the non shared files.\n"
"  -w, --who-shares         With -c: Print the packages that share each file.\n"
"  -y, --symlinks           Print the contents of symbolic links.\n"
"  -z, --no-package-name    Don't print the name of the package.\n"
"  -s, --size               Show the size of each file.\n\n"
"Information options:\n"
"  Note: Information may be not available for all packages.\n"
"  -i, --info               Print package information.\n"
"  -o, --configure-options  Print the options passed to configure when the\n"
"                           package was installed.\n"
"  -q, --query, --owner     Query for the packages that own one or more files.\n\n"
"Remove options:\n"
"  -r, --remove             Remove the (non shared) files of the package. '-rr'\n"
"                           forces the package to be removed from the database.\n"
"      --remove-shared      Remove also the shared files.\n"
"  -B, --batch              Do not ask for confirmation when removing.\n"
"  -e, --skip=PATH:...      Do not remove files in PATHs (see the man page).\n\n"
"Log options:\n"
"  -l, --log                Enable log mode. See the man page.\n"
"  -p, --package=PKG        Name of the package to log.\n" 
"  -D, --dirname            Use the name of the current directory as the name\n"
"                           of the package.\n"
"  -+, --append             With -p or -D: If the package is already logged,\n"
"                           append the list of files to its log.\n"
"      --log-missing        Do not skip missing files.\n"
"      --ignore-shared      With -p or -D: Do not log the shared files.\n"
"      --ignore-errors      Do not exit if the install command fails.\n"
"  -I, --include=PATH:...   List of paths to scan.\n"
"  -E, --exclude=PATH:...   List of paths to skip.\n\n"
"Note: The package list mode is enabled by default.\n\n"
"Send bugs to: David Rosal <" PACKAGE_BUGREPORT ">" << endl;

      exit(EXIT_SUCCESS);
}


static void version()
{
      cout << "paco-" PACKAGE_VERSION "  (" RELEASEDATE ")\n"
            "Copyright (C) David Rosal <" PACKAGE_BUGREPORT ">\n"
            "Protected by the GNU General Public License" << endl;
      exit(EXIT_SUCCESS);
}


static string getDirname()
{
      char dirname[8192];

      if (!getcwd(dirname, sizeof(dirname)))
            throw XErrno("getcwd()");

      return strrchr(dirname, '/') + 1;
}


// 
// Process the '--block-size=SIZE' option
//
static int toSizeUnit(char* arg)
{
      int i = -1, b = 0, unit;
      
      while (isdigit(arg[++i])) ;
      switch (arg[i]) {
            case 'k': case 'K': b = KILOBYTE; break;
            case 'm': case 'M': b = MEGABYTE; break;
            case 'b': case 'B': case 0: b = 1; break;
            default: goto ____invalid;
      }
      if ((unit = i ? (Paco::str2num<int>(arg) * b) : b))
            return unit ? unit : HUMAN_READABLE;

____invalid:
      throw X(string(arg) + ": Invalid block size");
}


//
// Process the '--sort=WORD' option
//
static SortType getSortType(string const& s)
{
      SortType ret(SORT_NAME);

      if (!s.size())
            goto ____failure;
      else if (!s.compare(0, s.size(), "size", s.size()))
            ret = SORT_SIZE;
      else if (!s.compare(0, s.size(), "date", s.size())
            || !s.compare(0, s.size(), "time", s.size()))
            ret = SORT_DATE;
      else if (!s.compare(0, s.size(), "missing-size", s.size()) && s.size() > 8)
            ret = SORT_SIZE_MISS;
      else if (!s.compare(0, s.size(), "missing-files", s.size()) && s.size() > 8)
            ret = SORT_FILES_MISS;
      else if (!s.compare(0, s.size(), "files", s.size()))
            ret = SORT_FILES_INST;
      else if (s.compare(0, s.size(), "name", s.size())) {
      ____failure:
            throw X(string("Invalid argument '") + s +
                  "' for option '--sort'.\nValid arguments are:\n"
                  "  - 'name'\n"
                  "  - 'size'\n"
                  "  - 'date' or 'time'\n"
                  "  - 'files'\n"
                  "  - 'missing-files'\n"
                  "  - 'missing-size'");
      }

      return ret;
}


static void dieHelp(string const& msg /* = "" */)
{
      string out(msg);
      if (!out.empty()) {
            out.insert(0, "paco: ");
            out += '\n';
      }
      cerr << out << "Try 'paco --help' for more information\n";
      exit(EXIT_FAILURE);
}



Generated by  Doxygen 1.6.0   Back to index