~ubuntu-branches/ubuntu/vivid/debtags/vivid-proposed

« back to all changes in this revision

Viewing changes to tools/debtags.cc

  • Committer: Bazaar Package Importer
  • Author(s): Enrico Zini
  • Date: 2006-03-18 20:31:47 UTC
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20060318203147-d9uzdeong5f5nk14
Tags: 1.5.5
* Added dumpavail command.
* Don't rebuild the database on install: apt-index-watcher does it instead.
  Closes: #357103.
* Compiles with g++ 4.1.  Closes: #357360.

Show diffs side-by-side

added added

removed removed

Lines of Context:
46
46
#include <tagcoll/experiments.h>
47
47
 
48
48
#include "Environment.h"
49
 
#include "CommandlineParser.h"
 
49
#include "DebtagsOptions.h"
50
50
#include "acqprogress.h"
51
51
#include "Printer.h"
52
52
 
66
66
using namespace aptFront::cache::component::debtags;
67
67
using namespace Tagcoll;
68
68
 
 
69
typedef OpSet<entity::Tag> TagSet;
 
70
typedef OpSet<entity::Facet> FacetSet;
 
71
typedef OpSet<entity::Package> PackageSet;
 
72
 
69
73
template<typename TAG, typename _Traits>
70
74
basic_ostream<char, _Traits>& operator<<(basic_ostream<char, _Traits>& out, const Tagcoll::OpSet<TAG>& tags)
71
75
{
116
120
void readCollection(const string& file, Tagcoll::Consumer<string, string>& output)
117
121
        throw (FileException, ParserException)
118
122
{
119
 
        Tagcoll::Converter<string, string> conv;
 
123
        Tagcoll::TrivialConverter<string, string> conv;
120
124
        if (file == "-")
121
125
        {
122
126
                StdioParserInput input(stdin, "<stdin>");
813
817
                        for (OpSet<entity::Package>::const_iterator i = toPrint.begin(); i != toPrint.end(); i++, count++)
814
818
                                if (count == (int)next)
815
819
                                {
816
 
                                        printf(" * `%.*s`_: %.*s\n", PFSTR(i->name()), PFSTR(i->shortDescription("(short description not available)")));
 
820
                                        printf(" * `%.*s`_: %.*s\n", PFSTR(i->name()), PFSTR(i->shortDescription(string("(short description not available)"))));
817
821
                                        mentionedPackages += *i;
818
822
                                        next += step;
819
823
                                        if (next > toPrint.size())
821
825
                                }
822
826
                } else {
823
827
                        for (OpSet<entity::Package>::const_iterator i = toPrint.begin(); i != toPrint.end(); i++)
824
 
                                printf(" * `%.*s`_: %.*s\n", PFSTR(i->name()), PFSTR(i->shortDescription("(short description not available)")));
 
828
                                printf(" * `%.*s`_: %.*s\n", PFSTR(i->name()), PFSTR(i->shortDescription(string("(short description not available)"))));
825
829
                        mentionedPackages += toPrint;
826
830
                }
827
831
                fputs("\n", stdout);
1008
1012
 
1009
1013
static void printVocabularyItem(const Facet& tag)
1010
1014
{
 
1015
        string ld = tag.longDescription();
1011
1016
        cout << "Facet: " << tag.name() << endl;
1012
1017
        cout << "Description: " << tag.shortDescription() << endl;
1013
 
        cout << " " << tag.longDescription() << endl;
1014
 
        cout << endl;
 
1018
        cout << " " << ld << endl;
 
1019
        if (ld[ld.size() - 1] != '\n')
 
1020
                cout << endl;
1015
1021
}
1016
1022
static void printVocabularyItem(const Tag& tag)
1017
1023
{
 
1024
        string ld = tag.longDescription();
1018
1025
        cout << "Tag: " << tag.fullname() << endl;
1019
1026
        cout << "Description: " << tag.shortDescription() << endl;
1020
 
        cout << " " << tag.longDescription() << endl;
1021
 
        cout << endl;
 
1027
        cout << " " << ld << endl;
 
1028
        if (ld[ld.size() - 1] != '\n')
 
1029
                cout << endl;
1022
1030
}
1023
1031
 
1024
1032
#if 0
1088
1096
public:
1089
1097
};
1090
1098
 
1091
 
class CommandlineParserWithCommand : public CommandlineParser
1092
 
{
1093
 
protected:
1094
 
        map<string, int> command_map;
1095
 
 
1096
 
public:
1097
 
        CommandlineParserWithCommand(const std::string& argv0,
1098
 
                                                                        const std::string& cmdline_summary,
1099
 
                                                                        const std::string& description) throw ()
1100
 
                : CommandlineParser(argv0, cmdline_summary, description)
1101
 
        {
1102
 
                add("version", 'V', "version", "print the program version, then exit");
1103
 
        }
1104
 
 
1105
 
        void addCommand(const std::string name, int id) throw ()
1106
 
        {
1107
 
                command_map[name] = id;
1108
 
        }
1109
 
 
1110
 
        int parse(int& argc, const char**& argv) throw ()
1111
 
        {
1112
 
                if (!CommandlineParser::parse(argc, argv))
1113
 
                {
1114
 
                        printHelp();
1115
 
                        exit(1);
1116
 
                }
1117
 
                if (get("help").defined())
1118
 
                {
1119
 
                        printHelp();
1120
 
                        exit(0);
1121
 
                }
1122
 
                if (get("version").defined())
1123
 
                {
1124
 
                        printf("%s ver." PACKAGE_VERSION "\n", APPNAME);
1125
 
                        exit(0);
1126
 
                }
1127
 
                if (argc == 1)
1128
 
                {
1129
 
                        printHelp();
1130
 
                        exit(1);
1131
 
                }
1132
 
 
1133
 
                string command_string = argv[1];
1134
 
                map<string, int>::const_iterator cmap_i = command_map.find(command_string);
1135
 
                if (cmap_i == command_map.end())
1136
 
                {
1137
 
                        fprintf(stderr, "Invalid command: \"%.*s\"\n", PFSTR(command_string));
1138
 
                        printHelp();
1139
 
                        exit(1);
1140
 
                }
1141
 
 
1142
 
                for (int i = 1; i < argc; i++)
1143
 
                        argv[i] = argv[i + 1];
1144
 
                --argc;
1145
 
                
1146
 
                return cmap_i->second;
1147
 
        }
1148
 
};
1149
 
 
1150
 
class CommandlineArgs
1151
 
{
1152
 
protected:
1153
 
        int argc;
1154
 
        const char** argv;
1155
 
        int _next;
1156
 
 
1157
 
public:
1158
 
        CommandlineArgs(int argc, const char* argv[]) throw () : argc(argc), argv(argv), _next(1) {}
1159
 
 
1160
 
        // Return true if there is another argument left in the list
1161
 
        bool hasNext() const throw () { return argc >= _next + 1; }
1162
 
 
1163
 
        // Return the next argument in the list
1164
 
        string next() throw ()
1165
 
        {
1166
 
                if (hasNext())
1167
 
                {
1168
 
                        return argv[_next++];
1169
 
                } else {
1170
 
                        return "-";
1171
 
                }
1172
 
        }
1173
 
};
1174
 
 
1175
 
enum valid_command { UPDATE, CHECK, TAGSHOW, TAGSEARCH, TAGCAT, SHOW, RELATED, CAT, SEARCH, GREP, INSTALL, MKPATCH, MAINTAINERS, TAG, SUBMIT, TODO, SCORE, FACETCOLL, STATS, TODOREPORT, SMARTSEARCH };
1176
 
 
1177
1099
int main(int argc, const char* argv[])
1178
1100
{
 
1101
        commandline::DebtagsOptions opts;
 
1102
 
1179
1103
        try {
1180
1104
                // Install the handler for unexpected exceptions
1181
1105
                InstallUnexpected installUnexpected;
1182
1106
 
1183
 
                CommandlineParserWithCommand opts(APPNAME, "[options] [command] [file1|-] [file2|-]",
1184
 
                                "Perform various operations on a tagged collection\n\n"
1185
 
                                "Commands are:\n"
1186
 
                                "  update        Updates the package tag database (requires root)\n"
1187
 
                                "  check <file>  Check that all the tags in the given tagged collection are present\n"
1188
 
                                "                in the tag vocabulary.  Checks the main database if no file is\n"
1189
 
                                "                specified\n"
1190
 
                                "  tagcat <tag>  Output the tag vocabulary\n"
1191
 
                                "  tagshow <tag> Show the vocabulary informations about a tag\n"
1192
 
                                "  tagsearch <string [string [string [...]]]>\n"
1193
 
                                "                Show a summary of all tags whose data contains the given strings\n"
1194
 
                                "  show <pkg>    Call apt-cache show <pkg>, but add tag informations to the output.\n"
1195
 
                                "  related <pkg1[,pkg2[,pkg2,[...]]]>\n"
1196
 
                                "                Show packages related to the specified ones.\n"
1197
 
                                "  cat           Output the full package tag database\n"
1198
 
                                "  search [-v] <tag expression>\n"
1199
 
                                "                Output the names and description of the packages that match\n"
1200
 
                                "                the given tag expression\n"
1201
 
                                "  grep [-v] [-q] <tag expression>\n"
1202
 
                                "                Output the lines of the full package tag database that match\n"
1203
 
                                "                the given tag expression\n"
1204
 
                                "  install [-v] [-q] <tag expression>\n"
1205
 
                                "                apt-get install the packages that match the given tag expression\n"
1206
 
                                "  mkpatch [filename]\n"
1207
 
                                "                Create a tag patch between the current tag database and the tag\n"
1208
 
                                "                collection [filename]\n"
1209
 
                                "  maintainers   Create a tagged collection of maintainers and the tags of the\n"
1210
 
                                "                packages they maintain\n"
1211
 
                                "  tag [add <package> <tags...>\n"
1212
 
                                "  tag [rm  <package> <tags...>\n"
1213
 
                                "  tag [ls  <package>\n"
1214
 
                                "                View and edit the tags for a package\n"
1215
 
                                "  submit [patch]\n"
1216
 
                                "                Mail the given patch file to the central tag repository.\n"
1217
 
                                "                If [patch] is omitted, mail the local tag modifications.\n"
1218
 
                                "  todo          Print a list of the installed packages that are not yet tagged\n"
1219
 
                                "  score         Score uninstalled packages according to how often their tags\n"
1220
 
                                "                appear in the packages that are installed already\n"
1221
 
                                "  facetcoll     Print the tagged collection where each package is tagged with\n"
1222
 
                                "                its facets only\n"
1223
 
                                "  stats         Print statistics about Debtags\n"
1224
 
                                "  todoreport    Print a report of packages needing work\n"
1225
 
                                "  ssearch <word [word1 [+tag [-tag1 ...]]]>\n"
1226
 
                                "                Perform a keyword search integrated with related packages.\n"
1227
 
                                "                A + prefix indicates a wanted tag.  A - prefix indicates\n"
1228
 
                                "                an unwanted tag.  Other words indicate keywords to search.\n"
1229
 
                                "                Remember to use '--' before unwanted tags to avoid to have\n"
1230
 
                                "                them interpreted as commandline switches.\n");
1231
 
 
1232
 
 
1233
 
                /*
1234
 
                opts.add("hierarchy", 's', "smart-hierarchy", "build a smart hierarchy");
1235
 
                opts.add("implications", 'm', "show-implications", "output a list of tag implications");
1236
 
                opts.add("copy", 'c', "copy", "output the collection");
1237
 
                opts.add("diff", 'd', "diff", "output a tag patch file with the differences between two files (requires two file arguments)");
1238
 
                */
1239
 
 
1240
 
                //opts.add("expanded", 'x', "expanded-output", "produce full (and redundant) output data instead of compact");
1241
 
                opts.add("groupitems", 'g', "group-items", "group items with the same tagset in the output collection");
1242
 
                opts.add("distance", 'd', "distance", "set the maximum distance to use for the \"related\" command (defaults to 0)", "num");
1243
 
 
1244
 
                opts.add("invert-match", 'v', "invert-match", "invert the sense of matching, to select non-matching lines");
1245
 
                opts.add("quiet", 'q', "quiet", "do not write anything to standard output, but exit with 0 if any match is found");
1246
 
                
1247
 
                opts.add("verbose", 'V', "verbose", "enable verbose output");
1248
 
                opts.add("debug", 0, "debug", "enable debugging output (including verbose output)");
1249
 
 
1250
 
                opts.addCommand("update", (int)UPDATE);
1251
 
                opts.addCommand("check", (int)CHECK);
1252
 
                opts.addCommand("tagcat", (int)TAGCAT);
1253
 
                opts.addCommand("tagshow", (int)TAGSHOW);
1254
 
                opts.addCommand("tagsearch", (int)TAGSEARCH);
1255
 
                opts.addCommand("show", (int)SHOW);
1256
 
                opts.addCommand("related", (int)RELATED);
1257
 
                opts.addCommand("cat", (int)CAT);
1258
 
                opts.addCommand("search", (int)SEARCH);
1259
 
                opts.addCommand("grep", (int)GREP);
1260
 
                opts.addCommand("install", (int)INSTALL);
1261
 
                opts.addCommand("mkpatch", (int)MKPATCH);
1262
 
                opts.addCommand("maintainers", (int)MAINTAINERS);
1263
 
                opts.addCommand("tag", (int)TAG);
1264
 
                opts.addCommand("submit", (int)SUBMIT);
1265
 
                opts.addCommand("todo", (int)TODO);
1266
 
                opts.addCommand("score", (int)SCORE);
1267
 
                opts.addCommand("facetcoll", (int)FACETCOLL);
1268
 
                opts.addCommand("stats", (int)STATS);
1269
 
                opts.addCommand("todoreport", (int)TODOREPORT);
1270
 
                opts.addCommand("ssearch", (int)SMARTSEARCH);
1271
 
 
1272
 
                // Process the commandline
1273
 
                valid_command cmd = (valid_command)opts.parse(argc, argv);
1274
 
 
1275
 
                CommandlineArgs args(argc, argv);
1276
 
 
1277
 
                if (opts.get("verbose").defined())
 
1107
                opts.parse(argc, argv);
 
1108
                if (!opts.lastCommand())
 
1109
                        throw commandline::BadOption("could not understand the command to execute");
 
1110
 
 
1111
                if (opts.outputGroup.verbose->boolValue())
1278
1112
                        ::Environment::get().verbose(true);
1279
1113
 
1280
 
                if (opts.get("debug").defined())
 
1114
                if (opts.outputGroup.debug->boolValue())
1281
1115
                        ::Environment::get().debug(true);
1282
1116
 
1283
 
                /*
1284
 
                if (cmd != UPDATE && cmd != CAT && cmd != GREP && cmd != INSTALL && cmd != TAGSHOW)
1285
 
                        Debtags::Environment::init(false);
1286
 
                */
1287
 
                
1288
1117
                // Perform the correct operation
1289
 
                switch (cmd)
1290
 
                {
1291
 
                        // Output the full package tag database
1292
 
                        case CAT:
1293
 
                        {
1294
 
                                component::PackageTags& debtags = debtagsInit();
1295
 
                                wantTagDatabase();
1296
 
                                auto_ptr< Printer<Package, Tag> > printer;
1297
 
 
1298
 
                                if (opts.get("groupitems").defined())
1299
 
                                        printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
1300
 
                                else
1301
 
                                        printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
1302
 
 
1303
 
                                Searcher searcher(debtags, printer.get());
1304
 
                                searcher.output();
1305
 
 
1306
 
                                return 0;
1307
 
                        }
1308
 
 
1309
 
                        // search [-v] <tag expression>\n"
1310
 
                        // Output the names and description of the packages that match\n"
1311
 
                        // the given tag expression\n"
1312
 
                        case SEARCH:
1313
 
                        {
1314
 
                                component::PackageTags& debtags = debtagsInit();
1315
 
                                wantTagDatabase();
1316
 
                                APTPrinter printer;
 
1118
                if (opts.helpGroup.help->boolValue())
 
1119
                {
 
1120
                        // Provide help as requested
 
1121
                        commandline::Help help(APPNAME, VERSION);
 
1122
                        commandline::OptionParser* o = opts.lastCommand();
 
1123
 
 
1124
                        if (o && !o->name().empty())
 
1125
                                // Help on a specific command
 
1126
                                help.outputHelp(cout, *o);
 
1127
                        else
 
1128
                                // General help
 
1129
                                help.outputHelp(cout, opts);
 
1130
                }
 
1131
                else if (opts.helpGroup.version->boolValue())
 
1132
                {
 
1133
                        // Print the program version
 
1134
                        commandline::Help help(APPNAME, VERSION);
 
1135
                        help.outputVersion(cout);
 
1136
                }
 
1137
                else if (opts.lastCommand() == &opts.generic)
 
1138
                {
 
1139
                        commandline::Help help(APPNAME, VERSION);
 
1140
                        help.outputHelp(cout, opts);
 
1141
                }
 
1142
                else if (opts.lastCommand() == &opts.help)
 
1143
                {
 
1144
                        commandline::Help help(APPNAME, VERSION);
 
1145
                        commandline::OptionParser* o = 0;
 
1146
                        if (opts.hasNext())
 
1147
                                o = opts.command(opts.next());
 
1148
 
 
1149
                        if (o)
 
1150
                                // Help on a specific command
 
1151
                                help.outputHelp(cout, *o);
 
1152
                        else
 
1153
                                // General help
 
1154
                                help.outputHelp(cout, opts);
 
1155
                }
 
1156
                else if (opts.lastCommand() == &opts.selfcheck)
 
1157
                {
 
1158
                        debtagsInit();
 
1159
                        component::Tags& voc = Global::get().tags();
 
1160
 
 
1161
                        // ensure that all facets are readable
 
1162
                        FacetSet facets = voc.facets();
 
1163
                        for (FacetSet::const_iterator i = facets.begin(); i != facets.end(); i++)
 
1164
                        {
 
1165
                                cout << "Checking facet " << i->name(string("<invalid name>")) << "..." << endl;
 
1166
                                i->name(string("foo"));
 
1167
                                i->shortDescription(string("foo"));
 
1168
                                i->longDescription(string("foo"));
 
1169
                                i->tags();
 
1170
                        }
 
1171
 
 
1172
                        // ensure that all tags are readable
 
1173
                        TagSet tags = voc.tags();
 
1174
                        for (TagSet::const_iterator i = tags.begin(); i != tags.end(); i++)
 
1175
                        {
 
1176
                                cout << "Checking tag " << i->fullname(string("<invalid name>")) << "..." << endl;
 
1177
                                i->name(string("foo"));
 
1178
                                i->fullname(string("foo"));
 
1179
                                i->shortDescription(string("foo"));
 
1180
                                i->longDescription(string("foo"));
 
1181
                        }
 
1182
 
 
1183
                        return 0;
 
1184
                }
 
1185
                // Output the full package tag database
 
1186
                else if (opts.lastCommand() == &opts.cat)
 
1187
                {
 
1188
                        component::PackageTags& debtags = debtagsInit();
 
1189
                        wantTagDatabase();
 
1190
                        auto_ptr< Printer<Package, Tag> > printer;
 
1191
 
 
1192
                        if (opts.outputGroup.group->boolValue())
 
1193
                                printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
 
1194
                        else
 
1195
                                printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
 
1196
 
 
1197
                        Searcher searcher(debtags, printer.get());
 
1198
                        searcher.output();
 
1199
 
 
1200
                        return 0;
 
1201
                }
 
1202
                // Output the full package database
 
1203
                else if (opts.lastCommand() == &opts.dumpavail)
 
1204
                {
 
1205
                        component::PackageTags& debtags = debtagsInit();
 
1206
                        wantTagDatabase();
 
1207
                        FullAPTPrinter printer;
 
1208
 
 
1209
                        int matched;
 
1210
                        if (opts.hasNext())
 
1211
                        {
1317
1212
                                Searcher searcher(debtags, &printer);
1318
 
                                string expression = args.next();
1319
 
 
1320
 
                                int matched =
1321
 
                                        searcher.output(expression, opts.get("invert-match").defined());
1322
 
 
1323
 
                                return matched > 0 ? 0 : 1;
1324
 
                        }
1325
 
                        
1326
 
 
1327
 
                        // grep [-v] [-q] <tag expression>
1328
 
                        // Output the lines of the full package tag database that match the
1329
 
                        // given tag expression
1330
 
                        case GREP:
1331
 
                        {
1332
 
                                component::PackageTags& debtags = debtagsInit();
1333
 
                                wantTagDatabase();
1334
 
                                auto_ptr< Printer<Package, Tag> > printer;
1335
 
 
1336
 
                                if (opts.get("quiet").defined())
1337
 
                                        printer = auto_ptr< Printer<Package, Tag> >(new NullPrinter);
1338
 
                                if (opts.get("groupitems").defined())
1339
 
                                        printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
1340
 
                                else
1341
 
                                        printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
1342
 
 
1343
 
                                Searcher searcher(debtags, printer.get());
1344
 
                                string expression = args.next();
1345
 
 
1346
 
                                int matched =
1347
 
                                        searcher.output(expression, opts.get("invert-match").defined());
1348
 
 
1349
 
                                return matched > 0 ? 0 : 1;
1350
 
                        }
1351
 
 
1352
 
                        // install [-v] [-q] <tag expression>
1353
 
                        // apt-get install the packages that match the given tag expression
1354
 
                        case INSTALL:
1355
 
                        {
1356
 
                                component::PackageTags& debtags = debtagsInit();
1357
 
                                wantTagDatabase();
1358
 
                                Installer installer;
1359
 
 
1360
 
                                Searcher searcher(debtags, &installer);
1361
 
                                string expression = args.next();
1362
 
 
1363
 
                                searcher.output(expression, opts.get("invert-match").defined());
1364
 
 
 
1213
                                matched = searcher.output(opts.next(), opts.matchGroup.invert->boolValue());
 
1214
                        } else {
 
1215
                                if (!opts.matchGroup.invert->boolValue())
 
1216
                                        for (component::Packages::iterator i = Global::get().packages().packagesBegin();
 
1217
                                                        i != Global::get().packages().packagesEnd(); ++i)
 
1218
                                                printer.consume(*i);
 
1219
                                matched = printer.count();
 
1220
                        }
 
1221
 
 
1222
                        return matched > 0 ? 0 : 1;
 
1223
                }
 
1224
                // search [-v] <tag expression>\n"
 
1225
                // Output the names and description of the packages that match\n"
 
1226
                // the given tag expression\n"
 
1227
                else if (opts.lastCommand() == &opts.search)
 
1228
                {
 
1229
                        component::PackageTags& debtags = debtagsInit();
 
1230
                        wantTagDatabase();
 
1231
                        APTPrinter printer;
 
1232
                        Searcher searcher(debtags, &printer);
 
1233
                        string expression = opts.next();
 
1234
 
 
1235
                        int matched =
 
1236
                                searcher.output(expression, opts.matchGroup.invert->boolValue());
 
1237
 
 
1238
                        return matched > 0 ? 0 : 1;
 
1239
                }
 
1240
                // grep [-v] [-q] <tag expression>
 
1241
                // Output the lines of the full package tag database that match the
 
1242
                // given tag expression
 
1243
                else if (opts.lastCommand() == &opts.grep)
 
1244
                {
 
1245
                        component::PackageTags& debtags = debtagsInit();
 
1246
                        wantTagDatabase();
 
1247
                        auto_ptr< Printer<Package, Tag> > printer;
 
1248
 
 
1249
                        if (opts.outputGroup.quiet->boolValue())
 
1250
                                printer = auto_ptr< Printer<Package, Tag> >(new NullPrinter);
 
1251
                        if (opts.outputGroup.group->boolValue())
 
1252
                                printer = auto_ptr< Printer<Package, Tag> >(new GroupedTagcollPrinter);
 
1253
                        else
 
1254
                                printer = auto_ptr< Printer<Package, Tag> >(new TagcollPrinter);
 
1255
 
 
1256
                        Searcher searcher(debtags, printer.get());
 
1257
                        string expression = opts.next();
 
1258
 
 
1259
                        int matched =
 
1260
                                searcher.output(expression, opts.matchGroup.invert->boolValue());
 
1261
 
 
1262
                        return matched > 0 ? 0 : 1;
 
1263
                }
 
1264
                // install [-v] [-q] <tag expression>
 
1265
                // apt-get install the packages that match the given tag expression
 
1266
                else if (opts.lastCommand() == &opts.install)
 
1267
                {
 
1268
                        component::PackageTags& debtags = debtagsInit();
 
1269
                        wantTagDatabase();
 
1270
                        Installer installer;
 
1271
 
 
1272
                        Searcher searcher(debtags, &installer);
 
1273
                        string expression = opts.next();
 
1274
 
 
1275
                        searcher.output(expression, opts.matchGroup.invert->boolValue());
 
1276
 
 
1277
                        return 1;
 
1278
                }
 
1279
                // tagcat
 
1280
                // Output the entire tag vocabulary
 
1281
                else if (opts.lastCommand() == &opts.tagcat)
 
1282
                {
 
1283
                        debtagsInit();
 
1284
                        wantTagDatabase();
 
1285
 
 
1286
                        component::Tags& voc = Global::get().tags();
 
1287
 
 
1288
                        OpSet<Facet> facets = voc.facets();
 
1289
                        for (OpSet<Facet>::const_iterator i = facets.begin();
 
1290
                                        i != facets.end(); i++)
 
1291
                        {
 
1292
                                printVocabularyItem(*i);
 
1293
 
 
1294
                                OpSet<Tag> tags = i->tags();
 
1295
                                for (OpSet<Tag>::const_iterator j = tags.begin();
 
1296
                                                j != tags.end(); j++)
 
1297
                                        printVocabularyItem(*j);
 
1298
                        }
 
1299
                        return 0;
 
1300
                }
 
1301
                // tagshow <tag>
 
1302
                // Show the vocabulary informations about a tag
 
1303
                else if (opts.lastCommand() == &opts.tagshow)
 
1304
                {
 
1305
                        debtagsInit();
 
1306
                        wantTagDatabase();
 
1307
                        string tag = opts.next();
 
1308
 
 
1309
                        Tag t = Global::get().tags().tagByName(tag);
 
1310
                        if (!t)
 
1311
                        {
 
1312
                                verbose("Tag `%.*s' was not found in tag vocabulary\n", PFSTR(tag));
1365
1313
                                return 1;
1366
1314
                        }
1367
 
 
1368
 
                        // tagcat
1369
 
                        // Output the entire tag vocabulary
1370
 
                        case TAGCAT:
 
1315
                        else
1371
1316
                        {
1372
 
                                debtagsInit();
1373
 
                                wantTagDatabase();
1374
 
 
1375
 
                                component::Tags& voc = Global::get().tags();
1376
 
 
1377
 
                                OpSet<Facet> facets = voc.facets();
1378
 
                                for (OpSet<Facet>::const_iterator i = facets.begin();
1379
 
                                                i != facets.end(); i++)
1380
 
                                {
1381
 
                                        printVocabularyItem(*i);
1382
 
 
1383
 
                                        OpSet<Tag> tags = i->tags();
1384
 
                                        for (OpSet<Tag>::const_iterator j = tags.begin();
1385
 
                                                        j != tags.end(); j++)
1386
 
                                                printVocabularyItem(*j);
1387
 
                                }
 
1317
                                printVocabularyItem(t);
1388
1318
                                return 0;
1389
1319
                        }
1390
 
                        // tagshow <tag>
1391
 
                        // Show the vocabulary informations about a tag
1392
 
                        case TAGSHOW:
1393
 
                        {
1394
 
                                debtagsInit();
1395
 
                                wantTagDatabase();
1396
 
                                string tag = args.next();
1397
 
 
1398
 
                                Tag t = Global::get().tags().tagByName(tag);
1399
 
                                if (!t)
1400
 
                                {
1401
 
                                        verbose("Tag `%.*s' was not found in tag vocabulary\n", PFSTR(tag));
1402
 
                                        return 1;
1403
 
                                }
1404
 
                                else
1405
 
                                {
1406
 
                                        printVocabularyItem(t);
1407
 
                                        return 0;
1408
 
                                }
1409
 
                        }
1410
 
                        // tagsearch <pattern [pattern [pattern [...]]]>
1411
 
                        // Show a summary of all tags matching the given patterns
1412
 
                        case TAGSEARCH:
1413
 
                        {
1414
 
                                debtagsInit();
1415
 
                                wantTagDatabase();
1416
 
 
1417
 
                                SubstringTagMatcher match;
1418
 
                                
1419
 
                                // Get the patterns to be matched
1420
 
                                bool empty;
1421
 
                                while (args.hasNext())
1422
 
                                {
1423
 
                                        string pattern = args.next();
1424
 
                                        match.add(pattern);
1425
 
                                        empty = false;
1426
 
                                }
1427
 
 
1428
 
                                if (empty)
1429
 
                                {
1430
 
                                        error("No patterns given in commandline\n");
1431
 
                                        return 1;
1432
 
                                }
1433
 
 
1434
 
                                component::Tags& voc = Global::get().tags();
1435
 
 
1436
 
                                int matched = 0;
1437
 
 
1438
 
                                OpSet<Facet> facets = voc.facets();
1439
 
                                for (OpSet<Facet>::const_iterator i = facets.begin();
1440
 
                                                i != facets.end(); i++)
1441
 
                                {
1442
 
                                        if (match(*i))
 
1320
                }
 
1321
                // tagsearch <pattern [pattern [pattern [...]]]>
 
1322
                // Show a summary of all tags matching the given patterns
 
1323
                else if (opts.lastCommand() == &opts.tagsearch)
 
1324
                {
 
1325
                        debtagsInit();
 
1326
                        wantTagDatabase();
 
1327
 
 
1328
                        SubstringTagMatcher match;
 
1329
 
 
1330
                        // Get the patterns to be matched
 
1331
                        bool empty;
 
1332
                        while (opts.hasNext())
 
1333
                        {
 
1334
                                string pattern = opts.next();
 
1335
                                match.add(pattern);
 
1336
                                empty = false;
 
1337
                        }
 
1338
 
 
1339
                        if (empty)
 
1340
                        {
 
1341
                                error("No patterns given in commandline\n");
 
1342
                                return 1;
 
1343
                        }
 
1344
 
 
1345
                        component::Tags& voc = Global::get().tags();
 
1346
 
 
1347
                        int matched = 0;
 
1348
 
 
1349
                        OpSet<Facet> facets = voc.facets();
 
1350
                        for (OpSet<Facet>::const_iterator i = facets.begin();
 
1351
                                        i != facets.end(); i++)
 
1352
                        {
 
1353
                                if (match(*i))
 
1354
                                {
 
1355
                                        matched++;
 
1356
                                        printShortVocabularyItem(*i);
 
1357
                                }
 
1358
 
 
1359
                                OpSet<Tag> tags = i->tags();
 
1360
                                for (OpSet<Tag>::const_iterator j = tags.begin();
 
1361
                                                j != tags.end(); j++)
 
1362
                                        if (match(*j))
1443
1363
                                        {
1444
1364
                                                matched++;
1445
 
                                                printShortVocabularyItem(*i);
1446
 
                                        }
1447
 
 
1448
 
                                        OpSet<Tag> tags = i->tags();
1449
 
                                        for (OpSet<Tag>::const_iterator j = tags.begin();
1450
 
                                                        j != tags.end(); j++)
1451
 
                                                if (match(*j))
1452
 
                                                {
1453
 
                                                        matched++;
1454
 
                                                        printShortVocabularyItem(*j);
1455
 
                                                }
1456
 
                                }
1457
 
 
1458
 
                                return matched > 0 ? 0 : 1;
1459
 
                        }
1460
 
                        // show <pkg>
1461
 
                        // Call apt-cache show <pkg>, but add tag informations to the output.\n"
1462
 
                        case SHOW:
1463
 
                        {
1464
 
                                component::PackageTags& debtags = debtagsInit();
1465
 
                                wantTagDatabase();
1466
 
 
1467
 
                                while (args.hasNext())
1468
 
                                {
1469
 
                                        string name = args.next();
1470
 
                                        
1471
 
                                        entity::Package pkg = packageByName(name);
1472
 
                                        if (pkg.valid())
1473
 
                                        {
1474
 
                                                cout << pkg.candidateVersion().completeRecord() << endl;
1475
 
                                                cout << "Tags: ";
1476
 
                                                OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
1477
 
                                                for (OpSet<Tag>::const_iterator i = ts.begin();
1478
 
                                                                i != ts.end(); i++)
1479
 
                                                        if (i == ts.begin())
1480
 
                                                                cout << i->fullname();
1481
 
                                                        else
1482
 
                                                                cout << ", " + i->fullname();
1483
 
                                                cout << endl;
1484
 
                                                return 0;
1485
 
                                        } else {
1486
 
                                                verbose("Package %.*s not found", PFSTR(name));
1487
 
                                                return 1;
1488
 
                                        }
1489
 
                                }
1490
 
                        }
1491
 
                        // related <pkg1[,pkg2[,pkg2,[...]]]>
1492
 
                        // Show packages related to the specified ones
1493
 
                        case RELATED:
1494
 
                        {
1495
 
                                component::PackageTags& debtags = debtagsInit();
1496
 
                                wantTagDatabase();
1497
 
 
1498
 
                                int maxdist = 0;
1499
 
                                if (opts.get("distance").defined())
1500
 
                                        maxdist = opts.get("distance").intVal();
1501
 
                                string pkg = args.next();
1502
 
 
1503
 
                                // Split the items on commas
1504
 
                                string splititem;
1505
 
                                OpSet<entity::Package> splititems;
1506
 
                                for (string::const_iterator c = pkg.begin(); c != pkg.end(); c++)
1507
 
                                        if (*c == ',')
1508
 
                                        {
1509
 
                                                entity::Package p = packageByName(splititem);
1510
 
                                                if (p.valid())
1511
 
                                                {
1512
 
                                                        splititems.insert(p);
1513
 
                                                } else {
1514
 
                                                        error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
1515
 
                                                        return 1;
1516
 
                                                }
1517
 
                                                splititem = string();
1518
 
                                        } else
1519
 
                                                splititem += *c;
1520
 
                                entity::Package p = packageByName(splititem);
1521
 
                                if (p.valid())
1522
 
                                {
1523
 
                                        splititems.insert(p);
1524
 
                                } else {
1525
 
                                        error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
1526
 
                                        return 1;
1527
 
                                }
1528
 
 
1529
 
                                // Get the tagset as the intersection of the tagsets of all input items
1530
 
                                OpSet<entity::Package>::const_iterator i = splititems.begin();
1531
 
                                OpSet<Tag> ts = debtags.tagdb().getTags(*i);
1532
 
                                for (++i; i != splititems.end(); i++)
1533
 
                                        ts = ts ^ debtags.tagdb().getTags(*i);
1534
 
 
1535
 
                                if (ts.empty())
1536
 
                                {
1537
 
                                        if (splititems.size() > 1)
1538
 
                                                fprintf(stderr, "The packages %.*s are unrelated: cannot find a barycenter to start computing relationships from.\n", PFSTR(pkg));
1539
 
                                        else
1540
 
                                                fprintf(stderr, "The package %.*s has no tags attached.\n", PFSTR(pkg));
1541
 
                                        return 1;
1542
 
                                }
1543
 
 
1544
 
                                APTPrinter printer(splititems);
1545
 
                                OpSet<entity::Package> related(debtags.tagdb().getRelatedItems(ts, maxdist));
1546
 
                                printer.print(related);
1547
 
                                printer.flush();
1548
 
 
1549
 
                                if (printer.count() > 50 && maxdist == 0 && isatty(1))
1550
 
                                {
1551
 
                                        string tags;
 
1365
                                                printShortVocabularyItem(*j);
 
1366
                                        }
 
1367
                        }
 
1368
 
 
1369
                        return matched > 0 ? 0 : 1;
 
1370
                }
 
1371
                // show <pkg>
 
1372
                // Call apt-cache show <pkg>, but add tag informations to the output.\n"
 
1373
                else if (opts.lastCommand() == &opts.show)
 
1374
                {
 
1375
                        component::PackageTags& debtags = debtagsInit();
 
1376
                        wantTagDatabase();
 
1377
 
 
1378
                        while (opts.hasNext())
 
1379
                        {
 
1380
                                string name = opts.next();
 
1381
 
 
1382
                                entity::Package pkg = packageByName(name);
 
1383
                                if (pkg.valid())
 
1384
                                {
 
1385
                                        cout << pkg.candidateVersion().completeRecord() << endl;
 
1386
                                        cout << "Tags: ";
 
1387
                                        OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
1552
1388
                                        for (OpSet<Tag>::const_iterator i = ts.begin();
1553
1389
                                                        i != ts.end(); i++)
1554
1390
                                                if (i == ts.begin())
1555
 
                                                        tags += i->fullname();
 
1391
                                                        cout << i->fullname();
1556
1392
                                                else
1557
 
                                                        tags += "%2C" + i->fullname();
1558
 
                                        feedback("\nIt seems that this set of packages lacks tag information that could help to better distinguish package similarities.\nYou can help providing better tagging: just point your browser to http://debian.vitavonni.de/packagebrowser/?tags=%.*s\n", PFSTR(tags));
 
1393
                                                        cout << ", " + i->fullname();
 
1394
                                        cout << endl;
 
1395
                                        return 0;
 
1396
                                } else {
 
1397
                                        verbose("Package %.*s not found", PFSTR(name));
 
1398
                                        return 1;
1559
1399
                                }
 
1400
                        }
 
1401
                }
 
1402
                // related <pkg1[,pkg2[,pkg2,[...]]]>
 
1403
                // Show packages related to the specified ones
 
1404
                else if (opts.lastCommand() == &opts.related)
 
1405
                {
 
1406
                        component::PackageTags& debtags = debtagsInit();
 
1407
                        wantTagDatabase();
 
1408
 
 
1409
                        int maxdist = 0;
 
1410
                        if (opts.related.distance->boolValue())
 
1411
                                maxdist = opts.related.distance->intValue();
 
1412
                        string pkg = opts.next();
 
1413
 
 
1414
                        // Split the items on commas
 
1415
                        string splititem;
 
1416
                        OpSet<entity::Package> splititems;
 
1417
                        for (string::const_iterator c = pkg.begin(); c != pkg.end(); c++)
 
1418
                                if (*c == ',')
 
1419
                                {
 
1420
                                        entity::Package p = packageByName(splititem);
 
1421
                                        if (p.valid())
 
1422
                                        {
 
1423
                                                splititems.insert(p);
 
1424
                                        } else {
 
1425
                                                error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
 
1426
                                                return 1;
 
1427
                                        }
 
1428
                                        splititem = string();
 
1429
                                } else
 
1430
                                        splititem += *c;
 
1431
                        entity::Package p = packageByName(splititem);
 
1432
                        if (p.valid())
 
1433
                        {
 
1434
                                splititems.insert(p);
 
1435
                        } else {
 
1436
                                error("Item \"%.*s\" does not exist in the collection\n", PFSTR(splititem));
 
1437
                                return 1;
 
1438
                        }
 
1439
 
 
1440
                        // Get the tagset as the intersection of the tagsets of all input items
 
1441
                        OpSet<entity::Package>::const_iterator i = splititems.begin();
 
1442
                        OpSet<Tag> ts = debtags.tagdb().getTags(*i);
 
1443
                        for (++i; i != splititems.end(); i++)
 
1444
                                ts = ts ^ debtags.tagdb().getTags(*i);
 
1445
 
 
1446
                        if (ts.empty())
 
1447
                        {
 
1448
                                if (splititems.size() > 1)
 
1449
                                        fprintf(stderr, "The packages %.*s are unrelated: cannot find a barycenter to start computing relationships from.\n", PFSTR(pkg));
 
1450
                                else
 
1451
                                        fprintf(stderr, "The package %.*s has no tags attached.\n", PFSTR(pkg));
 
1452
                                return 1;
 
1453
                        }
 
1454
 
 
1455
                        APTPrinter printer(splititems);
 
1456
                        OpSet<entity::Package> related(debtags.tagdb().getRelatedItems(ts, maxdist));
 
1457
                        printer.print(related);
 
1458
                        printer.flush();
 
1459
 
 
1460
                        if (printer.count() > 50 && maxdist == 0 && isatty(1))
 
1461
                        {
 
1462
                                string tags;
 
1463
                                for (OpSet<Tag>::const_iterator i = ts.begin();
 
1464
                                                i != ts.end(); i++)
 
1465
                                        if (i == ts.begin())
 
1466
                                                tags += i->fullname();
 
1467
                                        else
 
1468
                                                tags += "%2C" + i->fullname();
 
1469
                                feedback("\nIt seems that this set of packages lacks tag information that could help to better distinguish package similarities.\nYou can help providing better tagging: just point your browser to http://debian.vitavonni.de/packagebrowser/?tags=%.*s\n", PFSTR(tags));
 
1470
                        }
 
1471
                        return 0;
 
1472
                }
 
1473
                // check <file>
 
1474
                // Check that all the tags in the given tagged collection are
 
1475
                // present in the tag vocabulary.  Checks the main database if no
 
1476
                // file is specified
 
1477
                else if (opts.lastCommand() == &opts.check)
 
1478
                {
 
1479
                        debtagsInit();
 
1480
                        wantTagDatabase();
 
1481
 
 
1482
                        string file;
 
1483
                        if (opts.hasNext())
 
1484
                                file = opts.next();
 
1485
                        else
 
1486
                                file = utils::Path::tagdb();
 
1487
 
 
1488
                        TagcollChecker checker;
 
1489
                        readCollection(file, checker);
 
1490
 
 
1491
                        if (checker.missingCount() > 0)
 
1492
                        {
 
1493
                                checker.report();
 
1494
                                return 1;
 
1495
                        }
 
1496
                        else
1560
1497
                                return 0;
1561
 
                        }
1562
 
                        // check <file>
1563
 
                        // Check that all the tags in the given tagged collection are
1564
 
                        // present in the tag vocabulary.  Checks the main database if no
1565
 
                        // file is specified
1566
 
                        case CHECK:
1567
 
                        {
1568
 
                                debtagsInit();
 
1498
                }
 
1499
                // update
 
1500
                // Updates the package tag database (requires root)
 
1501
                else if (opts.lastCommand() == &opts.update)
 
1502
                {
 
1503
                        if (geteuid() != 0)
 
1504
                        {
 
1505
                                throw ConsistencyCheckException("You must be root to update the system debtags database");
 
1506
                        }
 
1507
                        struct winsize ws;
 
1508
                        unsigned int ScreenWidth;
 
1509
 
 
1510
                        if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
 
1511
                                ScreenWidth = ws.ws_col - 1;
 
1512
 
 
1513
                        AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
 
1514
                        //Debtags::Environment::get().updateDebtagsDatabase(&Stat);
 
1515
                        mode_t prev_umask = umask(022);
 
1516
                        //debtags::updateDatabase(&Stat);
 
1517
                        if (!opts.update.local->boolValue())
 
1518
                                debtags::fetchNewData(&Stat);
 
1519
                        debtagsInit();
 
1520
                        umask(prev_umask);
 
1521
                }
 
1522
                // mkpatch [filename]
 
1523
                // Create a tag patch between the current tag database and the tag
 
1524
                // collection [filename]
 
1525
                else if (opts.lastCommand() == &opts.diff)
 
1526
                {
 
1527
                        component::PackageTags& debtags = debtagsInit();
 
1528
                        wantTagDatabase();
 
1529
 
 
1530
                        string file = opts.next();
 
1531
 
 
1532
                        InputMerger<entity::Package, Tag> coll;
 
1533
                        readCollection(
 
1534
                                        Global::get().packagestringconverter(),
 
1535
                                        Global::get().tagstringconverter(),
 
1536
                                        file, coll);
 
1537
 
 
1538
                        PatchList<entity::Package, Tag> newpatches;
 
1539
                        newpatches.addPatch(debtags.tagdb(), coll);
 
1540
 
 
1541
                        TextFormat<entity::Package, Tag>::outputPatch(
 
1542
                                        Global::get().packagestringconverter(),
 
1543
                                        Global::get().tagstringconverter(),
 
1544
                                        newpatches, stdout);
 
1545
                }
 
1546
                // maintainers
 
1547
                // Create a tagged collection of maintainers and the tags of the
 
1548
                // packages they maintain
 
1549
                else if (opts.lastCommand() == &opts.maintainers)
 
1550
                {
 
1551
                        debtagsInit();
 
1552
                        wantTagDatabase();
 
1553
 
 
1554
                        // Gather maintainer informations
 
1555
                        InputMerger<string, Tag> maints;
 
1556
                        for (component::Packages::iterator i = Global::get().packages().packagesBegin();
 
1557
                                        i != Global::get().packages().packagesEnd(); ++i)
 
1558
                        {
 
1559
                                if (!i->valid())
 
1560
                                        continue;
 
1561
                                entity::Version v = i->candidateVersion();
 
1562
                                if (!v.valid())
 
1563
                                        continue;
 
1564
                                maints.consume(v.maintainer(), i->tags());
 
1565
                        }
 
1566
                        MaintPrinter printer;
 
1567
                        maints.output(printer);
 
1568
                }
 
1569
                // tag
 
1570
                //   tag [add <package> <tags...>\n"
 
1571
                //   tag [rm  <package> <tags...>\n"
 
1572
                //   tag [ls  <package>\n"
 
1573
                //                View and edit the tags for a package\n");
 
1574
                else if (opts.lastCommand() == &opts.tag)
 
1575
                {
 
1576
                        std::string cmd = opts.next();
 
1577
 
 
1578
                        if (cmd == "add" || cmd == "rm")
 
1579
                        {
 
1580
                                component::PackageTags& debtags = debtagsInit(true);
1569
1581
                                wantTagDatabase();
1570
1582
 
1571
 
                                string file;
1572
 
                                if (args.hasNext())
1573
 
                                        file = args.next();
1574
 
                                else
1575
 
                                        file = utils::Path::tagdb();
1576
 
 
1577
 
                                TagcollChecker checker;
1578
 
                                readCollection(file, checker);
1579
 
 
1580
 
                                if (checker.missingCount() > 0)
 
1583
                                string name = opts.next();
 
1584
 
 
1585
                                entity::Package pkg = packageByName(name);
 
1586
                                if (!pkg.valid())
1581
1587
                                {
1582
 
                                        checker.report();
 
1588
                                        error("Package %.*s not found\n", PFSTR(name));
1583
1589
                                        return 1;
1584
1590
                                }
1585
 
                                else
 
1591
 
 
1592
                                OpSet<Tag> tagset;
 
1593
 
 
1594
                                while (opts.hasNext())
 
1595
                                {
 
1596
                                        string tag = opts.next();
 
1597
                                        Tag t = Global::get().tags().tagByName(tag);
 
1598
                                        if (t)
 
1599
                                                tagset += t;
 
1600
                                        else
 
1601
                                                error("Tag `%.*s' not found: ignored\n", PFSTR(tag));
 
1602
                                }
 
1603
 
 
1604
                                if (!tagset.empty())
 
1605
                                {
 
1606
                                        PatchList<entity::Package, Tag> change;
 
1607
                                        if (cmd == "add")
 
1608
                                                change.addPatch(Patch<entity::Package, Tag>(pkg, tagset, OpSet<Tag>()));
 
1609
                                        else
 
1610
                                                change.addPatch(Patch<entity::Package, Tag>(pkg, OpSet<Tag>(), tagset));
 
1611
                                        debtags.tagdb().applyChange(change);
 
1612
                                        debtags.savePatch();
 
1613
                                } else
 
1614
                                        verbose("No tags to add\n");
 
1615
                        }
 
1616
                        else if (cmd == "ls")
 
1617
                        {
 
1618
                                component::PackageTags& debtags = debtagsInit();
 
1619
                                wantTagDatabase();
 
1620
 
 
1621
                                string name = opts.next();
 
1622
                                entity::Package pkg = packageByName(name);
 
1623
                                if (pkg.valid())
 
1624
                                {
 
1625
                                        OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
 
1626
                                        for (OpSet<Tag>::const_iterator i = ts.begin();
 
1627
                                                        i != ts.end(); i++)
 
1628
                                                printf("%.*s\n", PFSTR(i->fullname()));
1586
1629
                                        return 0;
1587
 
                        }
1588
 
                        // update
1589
 
                        // Updates the package tag database (requires root)
1590
 
                        case UPDATE:
1591
 
                        {
1592
 
                                if (geteuid() != 0)
1593
 
                                {
1594
 
                                        throw ConsistencyCheckException("You must be root to update the system debtags database");
1595
 
                                }
1596
 
                                struct winsize ws;
1597
 
                                unsigned int ScreenWidth;
1598
 
 
1599
 
                                if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
1600
 
                                        ScreenWidth = ws.ws_col - 1;
1601
 
 
1602
 
                                AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
1603
 
                                //Debtags::Environment::get().updateDebtagsDatabase(&Stat);
1604
 
                                mode_t prev_umask = umask(022);
1605
 
                                debtags::updateDatabase(&Stat);
1606
 
                                umask(prev_umask);
1607
 
                                feedback("Done.\n");
1608
 
                                break;
1609
 
                        }
1610
 
                        // mkpatch [filename]
1611
 
                        // Create a tag patch between the current tag database and the tag
1612
 
                        // collection [filename]
1613
 
                        case MKPATCH:
1614
 
                        {
1615
 
                                component::PackageTags& debtags = debtagsInit();
1616
 
                                wantTagDatabase();
1617
 
 
1618
 
                                string file = args.next();
1619
 
 
1620
 
                                InputMerger<entity::Package, Tag> coll;
1621
 
                                Converter<string, Package> toitem;
1622
 
                                Converter<string, Tag> totag;
1623
 
                                readCollection(toitem, totag, file, coll);
1624
 
 
1625
 
                                PatchList<entity::Package, Tag> newpatches;
1626
 
                                newpatches.addPatch(debtags.tagdb(), coll);
1627
 
 
1628
 
                                Converter<entity::Package, string> fromitem;
1629
 
                                Converter<Tag, string> fromtag;
1630
 
                                TextFormat<entity::Package, Tag>::outputPatch(fromitem, fromtag, newpatches, stdout);
1631
 
                                break;
1632
 
                        }
1633
 
                        // maintainers
1634
 
                        // Create a tagged collection of maintainers and the tags of the
1635
 
                        // packages they maintain
1636
 
                        case MAINTAINERS:
1637
 
                        {
1638
 
                                debtagsInit();
1639
 
                                wantTagDatabase();
1640
 
 
1641
 
                                // Gather maintainer informations
1642
 
                                InputMerger<string, Tag> maints;
1643
 
                                for (component::Packages::iterator i = Global::get().packages().packagesBegin();
1644
 
                                                i != Global::get().packages().packagesEnd(); i++)
1645
 
                                {
1646
 
                                        if (!i->valid())
1647
 
                                                continue;
1648
 
                                        entity::Version v = i->candidateVersion();
1649
 
                                        maints.consume(v.maintainer(), i->tags());
1650
 
                                }
1651
 
                                MaintPrinter printer;
1652
 
                                maints.output(printer);
1653
 
                                break;
1654
 
                        }
1655
 
                        // tag
1656
 
                        //   tag [add <package> <tags...>\n"
1657
 
                        //   tag [rm  <package> <tags...>\n"
1658
 
                        //   tag [ls  <package>\n"
1659
 
                        //                View and edit the tags for a package\n");
1660
 
                        case TAG:
1661
 
                        {
1662
 
                                std::string cmd = args.next();
1663
 
 
1664
 
                                if (cmd == "add" || cmd == "rm")
1665
 
                                {
1666
 
                                        component::PackageTags& debtags = debtagsInit(true);
1667
 
                                        wantTagDatabase();
1668
 
 
1669
 
                                        string name = args.next();
1670
 
 
1671
 
                                        entity::Package pkg = packageByName(name);
1672
 
                                        if (!pkg.valid())
1673
 
                                        {
1674
 
                                                error("Package %.*s not found\n", PFSTR(name));
1675
 
                                                return 1;
1676
 
                                        }
1677
 
 
1678
 
                                        OpSet<Tag> tagset;
1679
 
 
1680
 
                                        while (args.hasNext())
1681
 
                                        {
1682
 
                                                string tag = args.next();
1683
 
                                                Tag t = Global::get().tags().tagByName(tag);
1684
 
                                                if (t)
1685
 
                                                        tagset += t;
1686
 
                                                else
1687
 
                                                        error("Tag `%.*s' not found: ignored\n", PFSTR(tag));
1688
 
                                        }
1689
 
                                        
1690
 
                                        if (!tagset.empty())
1691
 
                                        {
1692
 
                                                PatchList<entity::Package, Tag> change;
1693
 
                                                if (cmd == "add")
1694
 
                                                        change.addPatch(Patch<entity::Package, Tag>(pkg, tagset, OpSet<Tag>()));
1695
 
                                                else
1696
 
                                                        change.addPatch(Patch<entity::Package, Tag>(pkg, OpSet<Tag>(), tagset));
1697
 
                                                debtags.tagdb().applyChange(change);
1698
 
                                                debtags.savePatch();
1699
 
                                        } else
1700
 
                                                verbose("No tags to add\n");
1701
 
                                }
1702
 
                                else if (cmd == "ls")
1703
 
                                {
1704
 
                                        component::PackageTags& debtags = debtagsInit();
1705
 
                                        wantTagDatabase();
1706
 
 
1707
 
                                        string name = args.next();
1708
 
                                        entity::Package pkg = packageByName(name);
1709
 
                                        if (pkg.valid())
1710
 
                                        {
1711
 
                                                OpSet<Tag> ts = debtags.tagdb().getTags(pkg);
1712
 
                                                for (OpSet<Tag>::const_iterator i = ts.begin();
1713
 
                                                                i != ts.end(); i++)
1714
 
                                                        printf("%.*s\n", PFSTR(i->fullname()));
1715
 
                                                return 0;
1716
 
                                        } else {
1717
 
                                                verbose("Package %.*s not found", PFSTR(name));
1718
 
                                                return 1;
1719
 
                                        }
1720
 
                                }
1721
 
                                else
1722
 
                                        throw ConsistencyCheckException("command " + cmd + " is not valid working with tags");
1723
 
                                break;
1724
 
                        }
1725
 
                        // submit
1726
 
                        // Mail the local updates to the tag database to the central tag
1727
 
                        // repository
1728
 
                        case SUBMIT:
1729
 
                        {
1730
 
                                component::PackageTags& debtags = debtagsInit();
1731
 
                                wantTagDatabase();
1732
 
 
1733
 
                                if (args.hasNext())
1734
 
                                {
1735
 
                                        StdioParserInput in(args.next());
1736
 
                                        Converter<string, Package> toitem;
1737
 
                                        Converter<string, Tag> totag;
1738
 
                                        PatchList<entity::Package, Tag> patch =
1739
 
                                                        TextFormat<entity::Package, Tag>::parsePatch(toitem, totag, in);
1740
 
                                        debtags.sendPatch(patch);
1741
 
                                }
1742
 
                                else
1743
 
                                        debtags.sendPatch();
1744
 
 
1745
 
                                break;
1746
 
                        }
1747
 
                        // todo
1748
 
                        // Print a list of the installed packages that are not yet tagged
1749
 
                        case TODO:
1750
 
                        {
1751
 
                                component::PackageTags& debtags = debtagsInit();
1752
 
                                wantTagDatabase();
1753
 
 
1754
 
                                // Write the package names to stdout
1755
 
                                APTPrinter printer;
1756
 
 
1757
 
                                // Filter to select the right packages
1758
 
                                TODOFilter filter(printer);
1759
 
 
1760
 
                                debtags.outputPatched(filter);
1761
 
 
1762
 
                                printer.flush();
1763
 
 
1764
 
                                break;
1765
 
                        }
1766
 
                        // score
1767
 
                        // Score uninstalled packages according to how often their tags
1768
 
                        // appear in the packages that are installed already
1769
 
                        case SCORE:
1770
 
                        {
1771
 
                                component::PackageTags& debtags = debtagsInit();
1772
 
                                wantTagDatabase();
1773
 
 
1774
 
                                // Compute tag scores
1775
 
                                TagsScorer tscorer;
1776
 
                                debtags.outputPatched(tscorer);
1777
 
 
1778
 
                                // Compute package scores
1779
 
                                PackageScorer pscorer(tscorer);
1780
 
                                debtags.outputPatched(pscorer);
1781
 
 
1782
 
                                // Print the results
1783
 
                                for (PackageScorer::const_iterator i = pscorer.begin();
1784
 
                                                i != pscorer.end(); i++)
1785
 
                                        printf("%d %.*s - %.*s\n", i->first,
1786
 
                                                        PFSTR(i->second.name()), PFSTR(i->second.shortDescription("(short description not available)")));
1787
 
                                break;
1788
 
                        }
1789
 
                        // facetcoll
1790
 
                        // Print the tagged collection where each package is tagged with
1791
 
                        // its facets only
1792
 
                        case FACETCOLL:
1793
 
                        {
1794
 
                                component::PackageTags& debtags = debtagsInit();
1795
 
                                wantTagDatabase();
1796
 
 
1797
 
                                FacetcollPrinter<entity::Package> printer;
1798
 
                                TagToFacet<entity::Package> tagToFacet(printer);
1799
 
                                debtags.outputPatched(tagToFacet);
1800
 
                                break;
1801
 
                        }
1802
 
                        // stats
1803
 
                        // Print statistics about Debtags
1804
 
                        case STATS:
1805
 
                        {
1806
 
                                component::PackageTags& debtags = debtagsInit();
1807
 
                                wantTagDatabase();
1808
 
 
1809
 
                                int pkgCount = Global::get().packages().packageCount();
1810
 
                                printf("Total count of packages: %d\n", pkgCount);
1811
 
 
1812
 
                                StatsCollector stats;
1813
 
                                debtags.outputPatched(stats);
1814
 
 
1815
 
                                printf("Total count of packages (according to APT): %d\n", pkgCount);
1816
 
                                printf("Total count of packages (according to Debtags): %d\n", stats.get_seen());
1817
 
 
1818
 
                                const component::Tags& voc = Global::get().tags();
1819
 
                                printf("Number of facets: %d\n", voc.facets().size());
1820
 
                                printf("Number of tags: %d\n", voc.tags().size());
1821
 
 
1822
 
                                // Copied from Debtags class: compute the toplevel facets
1823
 
                                // TODO: use Debtags instead of Environment throughout all Debtags
1824
 
                                CardinalityStore<entity::Package, Facet> coll;
1825
 
                                TagToFacet<entity::Package> tagStripper(coll);
1826
 
                                debtags.outputPatched(tagStripper);
1827
 
                                Facet f;
1828
 
                                SmartHierarchyNode<entity::Package, Facet> node(f, coll, 0);
1829
 
                                printf("Number of automatically computed toplevel facets: %d\n", node.size());
1830
 
 
1831
 
                                printf("Number of packages with special::completely-tagged tags: %d (%.1f%%)\n",
1832
 
                                                stats.get_complete(), (float)stats.get_complete()*100/stats.get_seen());
1833
 
                                printf("Number of packages with tags, but no special::not-yet-tagged tags: %d (%.1f%%)\n",
1834
 
                                                stats.get_tagged(), (float)stats.get_tagged()*100/stats.get_seen());
1835
 
                                printf("Number of packages with special::not-yet-tagged tags: %d (%.1f%%)\n",
1836
 
                                                stats.get_nyt(), (float)stats.get_nyt()*100/stats.get_seen());
1837
 
                                printf("Number of packages with only special::not-yet-tagged tags: %d (%.1f%%)\n",
1838
 
                                                stats.get_onlynyt(), (float)stats.get_onlynyt()*100/stats.get_seen());
1839
 
                                printf("Number of packages with no tags: %d (%.1f%%)\n",
1840
 
                                                stats.get_notags(), (float)stats.get_notags()*100/stats.get_seen());
1841
 
 
1842
 
                                break;
1843
 
                        }
1844
 
                        // todoreport
1845
 
                        // Print a report of packages needing work
1846
 
                        case TODOREPORT:
1847
 
                        {
1848
 
                                debtagsInit();
1849
 
                                wantTagDatabase();
1850
 
 
1851
 
                                unsigned int itemsPerGroup = 0;
1852
 
                                if (args.hasNext())
1853
 
                                        itemsPerGroup = atoi(args.next().c_str());
1854
 
 
1855
 
                                ReportMaker rm(itemsPerGroup);
1856
 
                                rm.printReport();
1857
 
 
1858
 
                                break;
1859
 
                        }
1860
 
                        // ssearch <word [word1 [word2 ...]]>
1861
 
                        // Perform a keyword search integrated with related packages
1862
 
                        case SMARTSEARCH:
1863
 
                        {
1864
 
                                component::PackageTags& debtags = debtagsInit();
1865
 
                                wantTagDatabase();
1866
 
                                APTPrinter printer;
1867
 
                                SmartSearcher smart(debtags, &printer);
1868
 
                                Searcher searcher(debtags, &smart);
1869
 
                                predicate::Predicate<Package> p = predicate::True<Package>();
1870
 
                                        //predicate::Factory<Package>::description(args.next());
1871
 
                                while (args.hasNext())
1872
 
                                {
1873
 
                                        string arg = args.next();
1874
 
                                        switch (arg[0])
1875
 
                                        {
1876
 
                                                case '+':
1877
 
                                                        p = p and predicate::Factory<Package>::tag(
1878
 
                                                                        Global::get().tags().tagByName(arg.substr(1)));
1879
 
                                                        break;
1880
 
                                                case '-':
1881
 
                                                        p = p and not predicate::Factory<Package>::tag(
1882
 
                                                                        Global::get().tags().tagByName(arg.substr(1)));
1883
 
                                                        break;
1884
 
                                                default:
1885
 
                                                        p = p and predicate::Factory<Package>::description(arg);
1886
 
                                                        break;
1887
 
                                        }
1888
 
                                }
1889
 
 
1890
 
                                int step1 = searcher.output(p);
1891
 
                                int step2 = smart.outputRelated();
1892
 
 
1893
 
                                cout << step1 << " normal matches plus " << step2 << " related packages." << endl;
1894
 
                                OpSet<Tag> topTags = smart.topTags();
1895
 
                                cout << "Top tags were: " << topTags << endl;
1896
 
 
1897
 
                                return step1 + step2 > 0 ? 0 : 1;
1898
 
                        }
1899
 
                        
1900
 
 
1901
 
                }
 
1630
                                } else {
 
1631
                                        verbose("Package %.*s not found", PFSTR(name));
 
1632
                                        return 1;
 
1633
                                }
 
1634
                        }
 
1635
                        else
 
1636
                                throw ConsistencyCheckException("command " + cmd + " is not valid working with tags");
 
1637
                }
 
1638
                // submit
 
1639
                // Mail the local updates to the tag database to the central tag
 
1640
                // repository
 
1641
                else if (opts.lastCommand() == &opts.submit)
 
1642
                {
 
1643
                        component::PackageTags& debtags = debtagsInit();
 
1644
                        wantTagDatabase();
 
1645
 
 
1646
                        if (opts.hasNext())
 
1647
                        {
 
1648
                                StdioParserInput in(opts.next());
 
1649
                                PatchList<entity::Package, Tag> patch =
 
1650
                                        TextFormat<entity::Package, Tag>::parsePatch(
 
1651
                                                        Global::get().packagestringconverter(),
 
1652
                                                        Global::get().tagstringconverter(),
 
1653
                                                        in);
 
1654
                                debtags.sendPatch(patch);
 
1655
                        }
 
1656
                        else
 
1657
                                debtags.sendPatch();
 
1658
 
 
1659
                }
 
1660
                // todo
 
1661
                // Print a list of the installed packages that are not yet tagged
 
1662
                else if (opts.lastCommand() == &opts.todo)
 
1663
                {
 
1664
                        component::PackageTags& debtags = debtagsInit();
 
1665
                        wantTagDatabase();
 
1666
 
 
1667
                        // Write the package names to stdout
 
1668
                        APTPrinter printer;
 
1669
 
 
1670
                        // Filter to select the right packages
 
1671
                        TODOFilter filter(printer);
 
1672
 
 
1673
                        debtags.outputPatched(filter);
 
1674
 
 
1675
                        printer.flush();
 
1676
 
 
1677
                }
 
1678
                // score
 
1679
                // Score uninstalled packages according to how often their tags
 
1680
                // appear in the packages that are installed already
 
1681
                else if (opts.lastCommand() == &opts.score)
 
1682
                {
 
1683
                        component::PackageTags& debtags = debtagsInit();
 
1684
                        wantTagDatabase();
 
1685
 
 
1686
                        // Compute tag scores
 
1687
                        TagsScorer tscorer;
 
1688
                        debtags.outputPatched(tscorer);
 
1689
 
 
1690
                        // Compute package scores
 
1691
                        PackageScorer pscorer(tscorer);
 
1692
                        debtags.outputPatched(pscorer);
 
1693
 
 
1694
                        // Print the results
 
1695
                        for (PackageScorer::const_iterator i = pscorer.begin();
 
1696
                                        i != pscorer.end(); i++)
 
1697
                                printf("%d %.*s - %.*s\n", i->first,
 
1698
                                                PFSTR(i->second.name()), PFSTR(i->second.shortDescription(string("(short description not available)"))));
 
1699
                }
 
1700
                // facetcoll
 
1701
                // Print the tagged collection where each package is tagged with
 
1702
                // its facets only
 
1703
                else if (opts.lastCommand() == &opts.facetcoll)
 
1704
                {
 
1705
                        component::PackageTags& debtags = debtagsInit();
 
1706
                        wantTagDatabase();
 
1707
 
 
1708
                        FacetcollPrinter<entity::Package> printer;
 
1709
                        TagToFacet<entity::Package> tagToFacet(printer);
 
1710
                        debtags.outputPatched(tagToFacet);
 
1711
                }
 
1712
                // stats
 
1713
                // Print statistics about Debtags
 
1714
                else if (opts.lastCommand() == &opts.stats)
 
1715
                {
 
1716
                        component::PackageTags& debtags = debtagsInit();
 
1717
                        wantTagDatabase();
 
1718
 
 
1719
                        int pkgCount = Global::get().packages().packageCount();
 
1720
                        printf("Total count of packages: %d\n", pkgCount);
 
1721
 
 
1722
                        StatsCollector stats;
 
1723
                        debtags.outputPatched(stats);
 
1724
 
 
1725
                        printf("Total count of packages (according to APT): %d\n", pkgCount);
 
1726
                        printf("Total count of packages (according to Debtags): %d\n", stats.get_seen());
 
1727
 
 
1728
                        const component::Tags& voc = Global::get().tags();
 
1729
                        printf("Number of facets: %d\n", voc.facets().size());
 
1730
                        printf("Number of tags: %d\n", voc.tags().size());
 
1731
 
 
1732
                        // Copied from Debtags class: compute the toplevel facets
 
1733
                        // TODO: use Debtags instead of Environment throughout all Debtags
 
1734
                        CardinalityStore<entity::Package, Facet> coll;
 
1735
                        TagToFacet<entity::Package> tagStripper(coll);
 
1736
                        debtags.outputPatched(tagStripper);
 
1737
                        Facet f;
 
1738
                        SmartHierarchyNode<entity::Package, Facet> node(f, coll, 0);
 
1739
                        printf("Number of automatically computed toplevel facets: %d\n", node.size());
 
1740
 
 
1741
                        printf("Number of packages with special::completely-tagged tags: %d (%.1f%%)\n",
 
1742
                                        stats.get_complete(), (float)stats.get_complete()*100/stats.get_seen());
 
1743
                        printf("Number of packages with tags, but no special::not-yet-tagged tags: %d (%.1f%%)\n",
 
1744
                                        stats.get_tagged(), (float)stats.get_tagged()*100/stats.get_seen());
 
1745
                        printf("Number of packages with special::not-yet-tagged tags: %d (%.1f%%)\n",
 
1746
                                        stats.get_nyt(), (float)stats.get_nyt()*100/stats.get_seen());
 
1747
                        printf("Number of packages with only special::not-yet-tagged tags: %d (%.1f%%)\n",
 
1748
                                        stats.get_onlynyt(), (float)stats.get_onlynyt()*100/stats.get_seen());
 
1749
                        printf("Number of packages with no tags: %d (%.1f%%)\n",
 
1750
                                        stats.get_notags(), (float)stats.get_notags()*100/stats.get_seen());
 
1751
 
 
1752
                }
 
1753
                // todoreport
 
1754
                // Print a report of packages needing work
 
1755
                else if (opts.lastCommand() == &opts.todoreport)
 
1756
                {
 
1757
                        debtagsInit();
 
1758
                        wantTagDatabase();
 
1759
 
 
1760
                        unsigned int itemsPerGroup = 0;
 
1761
                        if (opts.hasNext())
 
1762
                                itemsPerGroup = atoi(opts.next().c_str());
 
1763
 
 
1764
                        ReportMaker rm(itemsPerGroup);
 
1765
                        rm.printReport();
 
1766
 
 
1767
                }
 
1768
                // ssearch <word [word1 [word2 ...]]>
 
1769
                // Perform a keyword search integrated with related packages
 
1770
                else if (opts.lastCommand() == &opts.smartsearch)
 
1771
                {
 
1772
                        component::PackageTags& debtags = debtagsInit();
 
1773
                        wantTagDatabase();
 
1774
                        APTPrinter printer;
 
1775
                        SmartSearcher smart(debtags, &printer);
 
1776
                        Searcher searcher(debtags, &smart);
 
1777
                        predicate::Predicate<Package> p = predicate::True<Package>();
 
1778
                        //predicate::Factory<Package>::description(opts.next());
 
1779
                        while (opts.hasNext())
 
1780
                        {
 
1781
                                string arg = opts.next();
 
1782
                                switch (arg[0])
 
1783
                                {
 
1784
                                        case '+':
 
1785
                                                p = p and predicate::Factory<Package>::tag(
 
1786
                                                                Global::get().tags().tagByName(arg.substr(1)));
 
1787
                                                break;
 
1788
                                        case '-':
 
1789
                                                p = p and not predicate::Factory<Package>::tag(
 
1790
                                                                Global::get().tags().tagByName(arg.substr(1)));
 
1791
                                                break;
 
1792
                                        default:
 
1793
                                                p = p and predicate::Factory<Package>::description(arg);
 
1794
                                                break;
 
1795
                                }
 
1796
                        }
 
1797
 
 
1798
                        int step1 = searcher.output(p);
 
1799
                        int step2 = smart.outputRelated();
 
1800
 
 
1801
                        cout << step1 << " normal matches plus " << step2 << " related packages." << endl;
 
1802
                        OpSet<Tag> topTags = smart.topTags();
 
1803
                        cout << "Top tags were: " << topTags << endl;
 
1804
 
 
1805
                        return step1 + step2 > 0 ? 0 : 1;
 
1806
                }
 
1807
                else
 
1808
                        throw commandline::BadOption(string("unhandled command ") +
 
1809
                                                (opts.lastCommand() ? opts.lastCommand()->name() : "(null)"));
1902
1810
 
1903
1811
                return 0;
1904
 
        }
1905
 
        catch (Exception& e)
1906
 
        {
 
1812
        } catch (commandline::BadOption& e) {
 
1813
                cerr << e.desc() << endl;
 
1814
                commandline::Help help(APPNAME, VERSION);
 
1815
                if (opts.lastCommand())
 
1816
                {
 
1817
                        help.outputHelp(cerr, *opts.lastCommand());
 
1818
                } else {
 
1819
                        help.outputHelp(cerr, opts);
 
1820
                }
 
1821
                exit(1);
 
1822
        } catch (Exception& e) {
1907
1823
                fatal_error("%s: %.*s\n", e.type(), PFSTR(e.desc()));
1908
1824
                return 1;
1909
1825
        }