queryoperation.cpp 14 KB
Newer Older
Lukas Appelhans's avatar
Lukas Appelhans committed
1 2 3 4 5 6 7 8 9 10
/* This file is part of the Chakra project

   Copyright (C) 2011 Lukas Appelhans <l.appelhans@gmx.de>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
*/

11 12 13
#include "queryoperation.h"
#include "fieldnames.h"
#include "utils.h"
14
#include <akabeierror.h>
Lukas Appelhans's avatar
Lukas Appelhans committed
15
#include <akabeibackend.h>
Lukas Appelhans's avatar
Lukas Appelhans committed
16
#include <akabeigroup.h>
Lukas Appelhans's avatar
Lukas Appelhans committed
17
#include <akabeidatabase.h>
Lukas Appelhans's avatar
Lukas Appelhans committed
18
#include <akabeiconfig.h>
19
#include <akabeihelpers.h>
20
#include <akabeiquery.h>
Lisa's avatar
Lisa committed
21

Lukas Appelhans's avatar
Lukas Appelhans committed
22
#include <QTextStream>
Lukas Appelhans's avatar
Lukas Appelhans committed
23
#include <QDate>
Lukas Appelhans's avatar
Lukas Appelhans committed
24 25
#include <QFile>
#include <QDir>
Lukas Appelhans's avatar
Lukas Appelhans committed
26
#include <QCoreApplication>
Lukas Appelhans's avatar
Lukas Appelhans committed
27

28
QueryOperation::QueryOperation(QList<APM::OperationName> operations, QHash<APM::OptionName, QStringList> options, QStringList args, QObject * parent)
Lisa's avatar
Lisa committed
29
  : QObject(parent)
Lisa's avatar
Lisa committed
30 31 32
  , m_operations(operations)
  , m_options(options)
  , m_args(args)
33
{}
Lukas Appelhans's avatar
Lukas Appelhans committed
34 35 36 37 38

QueryOperation::~QueryOperation()
{
}

Lisa's avatar
Lisa committed
39
void QueryOperation::start()
Lukas Appelhans's avatar
Lukas Appelhans committed
40
{
Lisa's avatar
Lisa committed
41
    APM::OperationName operation = m_operations.takeFirst();
42 43
    Akabei::Backend *backend = Akabei::Backend::instance();
    Akabei::Database *localDatabase = backend->localDatabase();
44

45 46
    switch (operation)
    {
47
        case APM::ShowInformation: {
48 49
            if (m_options.contains(APM::QueryPackageFile)) {
                Akabei::Package::List packages;
50

51 52 53
                foreach (const QString& packageFile, m_options[APM::QueryPackageFile]) {
                    packages.append(backend->loadPackageFromFile(packageFile));
                }
54

55 56 57
                showInformation(packages);
            } else {
                showInformation(backend->localDatabase()->searchPackages(m_args, Akabei::SearchNameEqual));
58
                //Change back to searchPackages when we have sorted results after Name and description
59 60 61
            }
            break;
        }
62

63
        case APM::ShowPackagesOfGroup: {
64 65 66 67 68
            foreach (const QString& group, m_args) {
                showGroup(group, localDatabase->queryGroups("SELECT * FROM groups WHERE Name LIKE \"" + group + "\""));
            }
            break;
        }
Lisa's avatar
Lisa committed
69 70
        
        case APM::ShowAllGroups: {
71 72
            connect(backend, SIGNAL(queryGroupsCompleted(QUuid,QList<Akabei::Group*>)), SLOT(listGroups(QUuid,QList<Akabei::Group*>)));
            queryId = backend->groups();
Lisa's avatar
Lisa committed
73 74
            break;
        }
75

76
        case APM::ShowChangelog: {
77 78
            if (m_options.contains(APM::QueryPackageFile)) {
                Akabei::Package::List packages;
79

80 81 82
                foreach (const QString& packageFile, m_options[APM::QueryPackageFile]) {
                    packages.append(backend->loadPackageFromFile(packageFile));
                }
83

84 85
                showChangelog(packages);
            } else {
Lukas Appelhans's avatar
Lukas Appelhans committed
86
                showChangelog(localDatabase->searchPackages(m_args, Akabei::SearchNameEqual));
87 88 89
            }
            break;
        }
90

Lukas Appelhans's avatar
Lukas Appelhans committed
91 92 93 94 95
        case APM::ShowLog: {
            showLog(localDatabase->searchPackages(m_args, Akabei::SearchNameEqual));
            break;
        }

96
        case APM::ShowInstalledAsDeps:
97
        case APM::ShowInstalledExplicitely: {
98 99 100
            QString query = "SELECT * FROM packages WHERE";
            if (!m_args.isEmpty()) {
                query = query + " Name LIKE \"" + m_args.first() + "\" AND";
101 102
            }

103 104 105 106 107 108
            if (operation == APM::ShowInstalledAsDeps) {
                query = query + " InstallReason LIKE " + QString::number((int)Akabei::Package::InstalledAsDependencyReason);
            }
            else {
                query = query + " InstallReason LIKE " + QString::number((int)Akabei::Package::ExplicitlyInstalledReason);
            }
109

110 111 112
            showInstalledAs(localDatabase->queryPackages(query));
            break;
        }
113

114
        case APM::CheckFiles: {
115 116
            if (m_options.contains(APM::QueryPackageFile)) {
                Akabei::Package::List packages;
117

118 119 120
                foreach (const QString& packageFile, m_options[APM::QueryPackageFile]) {
                    packages.append(backend->loadPackageFromFile(packageFile));
                }
121

122 123
                checkFiles(packages);
            } else {
Lukas Appelhans's avatar
Lukas Appelhans committed
124
                checkFiles(localDatabase->searchPackages(m_args, Akabei::SearchNameEqual));
125 126 127 128 129 130 131 132
            }
            break;
        }

        /*
         * TODO: possibility of specifying more than one search argument, meaning the result should contain
         * all the words specified?
         */
133
        case APM::Search: {
134 135 136 137 138
            if (!m_args.isEmpty()) {
                showLocalQuery(localDatabase->searchPackages(m_args.first(), Akabei::SearchNameAndDescriptionLike));
            } else {
                QCoreApplication::instance()->quit();
            }
139 140
            break;
        }
141

142
        case APM::ShowOwner: {
143 144 145
            showOwner(m_args);
            break;
        }
146

147
        case APM::ShowNotRequired: {
Lisa's avatar
Lisa committed
148 149
            connect(backend, SIGNAL(queryOrphansCompleted(QUuid,QList<Akabei::Package*>)), SLOT(showNotRequired(QUuid,QList<Akabei::Package*>)));
            queryId = backend->orphanPackages();
150 151
            break;
        }
152

153
        case APM::ShowUpgradeable: {
154 155
            showUpgradeable();
            break;
156
        }
157

158 159 160 161
        case APM::ShowInstalledPackages: {
            showLocalQuery(localDatabase->packages());
            break;
        }
162

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
        case APM::PrintFiles: {
            if (m_options.contains(APM::QueryPackageFile)) {
                Akabei::Package::List packages;

                foreach (const QString& packageFile, m_options[APM::QueryPackageFile]) {
                    packages.append(backend->loadPackageFromFile(packageFile));
                }

                printFiles(packages);
            } else if (!m_args.isEmpty()) {
                printFiles(localDatabase->searchPackages(m_args.first(), Akabei::SearchNameEqual));
            } else {
                QCoreApplication::instance()->quit();
            }
            break;
        }

180
        default: {
181
            QTextStream err(stderr);
Lisa's avatar
Lisa committed
182
            err << Akabei::errorPrefix << QObject::tr("Operation not supported.") << endl;
183 184
            QCoreApplication::instance()->quit();
            break;
185
        }
186 187 188 189 190 191 192 193
    }
}

bool QueryOperation::isIgnored(Akabei::Package *pkg)
{
    if (m_options[APM::Ignore].contains(pkg->name())) {
        return true;
    }
194

195 196 197
    foreach (Akabei::Group *g, pkg->groups()) {
        if (m_options[APM::IgnoreGroup].contains(g->name())) {
            return true;
198
        }
Lukas Appelhans's avatar
Lukas Appelhans committed
199
    }
200

201
    return false;
Lukas Appelhans's avatar
Lukas Appelhans committed
202 203
}

204
void QueryOperation::showInformation(Akabei::Package::List packages)
Lukas Appelhans's avatar
Lukas Appelhans committed
205
{
206
    if (packages.isEmpty()) {
207
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
208
        err << Akabei::errorPrefix << QObject::tr("No package found!") << endl;
209
        nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
210
        return;
211
    }
212 213 214

    QTextStream out( stdout );

215
    foreach (Akabei::Package *pkg, packages) {
216
        Utils::printPackageInformation(pkg, out, m_options.contains(APM::ShowLess), m_options.contains(APM::ShowMakeDeps));
217 218
    }

Lisa's avatar
Lisa committed
219
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
220
}
Lukas Appelhans's avatar
Lukas Appelhans committed
221

222
void QueryOperation::showGroup(const QString& groupName, Akabei::Group::List groups)
Lukas Appelhans's avatar
Lukas Appelhans committed
223
{
Lukas Appelhans's avatar
Lukas Appelhans committed
224
    if (groups.isEmpty()) {
225
        QTextStream err( stderr );
Lisa's avatar
Lisa committed
226
        err << Akabei::errorPrefix << QObject::tr("Unknown group: %1").arg(groupName) << endl;
227
        nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
228
        return;
Lukas Appelhans's avatar
Lukas Appelhans committed
229
    }
230

231 232 233 234 235 236
    QTextStream out( stdout );

    Akabei::Group *group = groups.first();

    Utils::printGroup(group, out, m_options.contains(APM::ShowLess));

Lukas Appelhans's avatar
Lukas Appelhans committed
237
    out.flush();
Lisa's avatar
Lisa committed
238
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
239
}
Lukas Appelhans's avatar
Lukas Appelhans committed
240

241
void QueryOperation::listGroups(QUuid uuid, QList< Akabei::Group* > groups)
Lisa's avatar
Lisa committed
242
{
243 244 245
    if (queryId != uuid) {
        nextOperation();
        return;
Lisa's avatar
Lisa committed
246 247 248 249 250 251 252 253 254 255 256 257
    }
    
    QTextStream out(stdout);
    
    foreach (Akabei::Group* group, groups) {
        out << group->name() << endl;
    }
    
    out.flush();
    nextOperation();
}

258
void QueryOperation::showChangelog(Akabei::Package::List packages)
Lukas Appelhans's avatar
Lukas Appelhans committed
259
{
Lukas Appelhans's avatar
Lukas Appelhans committed
260
    if (packages.isEmpty()) {
261
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
262
        err << Akabei::errorPrefix << QObject::tr("No package found!") << endl;
263
        nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
264
        return;
Lukas Appelhans's avatar
Lukas Appelhans committed
265
    }
266

Lukas Appelhans's avatar
Lukas Appelhans committed
267
    QTextStream out(stdout);
268 269

    foreach (Akabei::Package *pkg, packages) {
Lisa's avatar
Lisa committed
270
        QString changelog( pkg->retrieveChangelog() );
271

Lisa's avatar
Lisa committed
272 273 274
        if (!changelog.isEmpty()) {
            out << changelog << endl;
        } else {
Lisa's avatar
Lisa committed
275
            out << Akabei::errorPrefix << QObject::tr("No changelog available!") << endl;
Lisa's avatar
Lisa committed
276
        }
277
    }
278

Lukas Appelhans's avatar
Lukas Appelhans committed
279
    out.flush();
Lisa's avatar
Lisa committed
280
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
281 282
}

Lukas Appelhans's avatar
Lukas Appelhans committed
283 284 285 286
void QueryOperation::showLog(Akabei::Package::List packages)
{
    if (packages.isEmpty()) {
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
287
        err << Akabei::errorPrefix << QObject::tr("No package found!") << endl;
Lukas Appelhans's avatar
Lukas Appelhans committed
288 289 290 291 292 293 294 295 296 297 298 299
        nextOperation();
        return;
    }

    QTextStream out(stdout);

    foreach (Akabei::Package *pkg, packages) {
        QString log( pkg->retrieveLoggedActions() );

        if (!log.isEmpty()) {
            out << log;
        } else {
Lisa's avatar
Lisa committed
300
            out << Akabei::errorPrefix << QObject::tr("No changelog available!") << endl;
Lukas Appelhans's avatar
Lukas Appelhans committed
301 302 303 304 305 306 307
        }
    }

    out.flush();
    nextOperation();
}

308
void QueryOperation::showInstalledAs(Akabei::Package::List packages)
Lukas Appelhans's avatar
Lukas Appelhans committed
309
{
Lukas Appelhans's avatar
Lukas Appelhans committed
310
    if (packages.isEmpty()) {
311
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
312
        err << Akabei::errorPrefix << QObject::tr("No package found!") << endl;
313
        nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
314
        return;
Lukas Appelhans's avatar
Lukas Appelhans committed
315
    }
316

Lukas Appelhans's avatar
Lukas Appelhans committed
317
    QTextStream out(stdout);
318
    QMap<QString, Akabei::Package*> sorted;
319

320 321 322 323 324
    foreach (Akabei::Package *pkg, packages) {
        if (!isIgnored(pkg)) {
            sorted[pkg->name()] = pkg;
        }
    }
325

326
    foreach (Akabei::Package * pkg, sorted.values()) {
Lisa's avatar
Lisa committed
327
        out << pkg->name() << ' ' << pkg->version().toByteArray().data() << endl;
328
    }
329

Lukas Appelhans's avatar
Lukas Appelhans committed
330
    out.flush();
Lisa's avatar
Lisa committed
331
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
332 333
}

334
void QueryOperation::checkFiles(Akabei::Package::List packages)
Lukas Appelhans's avatar
Lukas Appelhans committed
335
{
Lukas Appelhans's avatar
Lukas Appelhans committed
336
    if (packages.isEmpty()) {
337
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
338
        err << Akabei::errorPrefix << QObject::tr("No package found!") << endl;
339
        nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
340
        return;
Lukas Appelhans's avatar
Lukas Appelhans committed
341
    }
342

Lukas Appelhans's avatar
Lukas Appelhans committed
343
    QTextStream out(stdout);
344

345 346 347
    foreach (Akabei::Package *pkg, packages) {
        int all = 0;
        int missing = 0;
348

349 350 351 352 353
        foreach (const QString &file, pkg->retrieveFiles()) {
            all++;
            if (!QFile::exists(Akabei::Config::instance()->rootDir().absoluteFilePath(file)))
                missing++;
        }
Lisa's avatar
Lisa committed
354
        out << QObject::tr("%1: %2 overall files, %3 file(s) missing!").arg(pkg->name(), QString::number(all), QString::number(missing)) << endl;
Lukas Appelhans's avatar
Lukas Appelhans committed
355
    }
356

Lukas Appelhans's avatar
Lukas Appelhans committed
357
    out.flush();
Lisa's avatar
Lisa committed
358
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
359 360
}

361
void QueryOperation::showOwner(const QStringList &queries)
Lukas Appelhans's avatar
Lukas Appelhans committed
362 363
{
    QTextStream out(stdout);
364 365
    QTextStream err(stderr);

366
    foreach (const QString& query, queries) {
367 368
        QString cleaned = QFileInfo(query).canonicalFilePath();
        if (cleaned.isEmpty()) {
AlmAck's avatar
AlmAck committed
369
            err << Akabei::errorPrefix << QObject::tr("File does not exist: %1").arg(query) << endl;
370
            err.flush();
371 372 373
            nextOperation();
            return;
        }
374

375
        cleaned.remove(Akabei::Config::instance()->root());
376 377
        // remove the first "/" (current root)
        cleaned.remove(0,1);
378 379 380 381 382 383 384 385
        //TODO: bash completition
        qDebug() << "Cleaned is" << cleaned;
        Akabei::Package::List result = 
                          Akabei::Backend::instance()->localDatabase()->queryPackages("SELECT * FROM files JOIN packages ON files.package=packages.id WHERE files.file=\"" + cleaned + "\";");
        if (result.isEmpty()) {
            out << QObject::tr("No package owns %1").arg(query) << endl;
        } else {
            out << QObject::tr("%1 is owned by %2 %3!").arg(query, result.first()->name(), result.first()->version().toByteArray()) << endl;
Lukas Appelhans's avatar
Lukas Appelhans committed
386 387
        }
    }
388

Lukas Appelhans's avatar
Lukas Appelhans committed
389
    out.flush();
Lisa's avatar
Lisa committed
390
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
391
}
Lukas Appelhans's avatar
Lukas Appelhans committed
392

393
void QueryOperation::showLocalQuery(Akabei::Package::List packages) //FIXME: Transfer to new way to print results
Lukas Appelhans's avatar
Lukas Appelhans committed
394 395 396
{
    QTextStream out(stdout);
    QMap<QString, Akabei::Package*> sorted;
397

398
    foreach (Akabei::Package * pkg, packages) {
Lisa's avatar
Lisa committed
399
        sorted[pkg->name()] = pkg;
400
    }
401

Lisa's avatar
Lisa committed
402
    foreach (Akabei::Package * pkg, sorted.values()) {
Lukas Appelhans's avatar
Lukas Appelhans committed
403
        out << "local/" << pkg->name() << ' ' << pkg->version().toByteArray().data() << endl;
404

405 406 407
        if (!m_options.contains(APM::ShowLess)) {
            out << "\t" << pkg->description() << endl;
        }
Lukas Appelhans's avatar
Lukas Appelhans committed
408
    }
409

Lukas Appelhans's avatar
Lukas Appelhans committed
410
    out.flush();
Lisa's avatar
Lisa committed
411
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
412
}
Lukas Appelhans's avatar
Lukas Appelhans committed
413

Lisa's avatar
Lisa committed
414
void QueryOperation::showNotRequired(QUuid uuid, QList< Akabei::Package* > orphans)
Lukas Appelhans's avatar
Lukas Appelhans committed
415
{
Lisa's avatar
Lisa committed
416 417 418
    if (queryId != uuid) {
        nextOperation();
        return;
419
    }
420

Lisa's avatar
Lisa committed
421
    QTextStream out(stdout);
422

Lisa's avatar
Lisa committed
423 424
    if (orphans.isEmpty()) {
        out << "No packages found!" << endl;
Lukas Appelhans's avatar
Lukas Appelhans committed
425
    }
426

Lisa's avatar
Lisa committed
427 428 429
    foreach (Akabei::Package* orphan, orphans) {
        if (!isIgnored(orphan)) {
            out << orphan->name() << ' ' << orphan->version().toByteArray().data() << endl;
430 431
        }
    }
432

Lukas Appelhans's avatar
Lukas Appelhans committed
433
    out.flush();
Lisa's avatar
Lisa committed
434
    nextOperation();
Lukas Appelhans's avatar
Lukas Appelhans committed
435
}
Lukas Appelhans's avatar
Lukas Appelhans committed
436 437 438 439 440

void QueryOperation::showUpgradeable()
{
    //FIXME: Maybe don't even check for the highest version... redundant?
    QTextStream out(stdout);
441
    Akabei::Package::List packages = Akabei::Backend::instance()->localDatabase()->packages();
Lukas Appelhans's avatar
Lukas Appelhans committed
442
    QMap<QString, Akabei::Package*> upgradeable;
443

Lisa's avatar
Lisa committed
444
    foreach (Akabei::Package * pkg, packages) {
445 446 447
        if (isIgnored(pkg)) {
            continue;
        }
448

449
        Akabei::Package *latest = Akabei::Helpers::latestVersionOfPackage(pkg->name());
450

451 452
        if (latest && latest->version() > pkg->version()) {
            upgradeable[latest->name()] = latest;
Lukas Appelhans's avatar
Lukas Appelhans committed
453 454
        }
    }
455

456
    foreach (Akabei::Package * pkg, upgradeable.values()) {
Lisa's avatar
Lisa committed
457
        out << pkg->name() << ' ' << pkg->version().toByteArray().data() << endl;
458
    }
459 460

    if (upgradeable.isEmpty()) {
Lisa's avatar
Lisa committed
461
        out << QObject::tr("No package found!") << endl;
462 463
    }

Lukas Appelhans's avatar
Lukas Appelhans committed
464
    out.flush();
Lisa's avatar
Lisa committed
465 466 467
    nextOperation();
}

468 469 470 471 472
void QueryOperation::printFiles(Akabei::Package::List packages)
{
    packages.removeAll(0);
    if (packages.isEmpty()) {
        QTextStream err(stderr);
Lisa's avatar
Lisa committed
473
        err << QObject::tr("No package found!") << endl;
474 475 476 477 478 479 480 481 482 483 484 485
        nextOperation();
        return;
    }
    QTextStream out(stdout);
    QStringList files = packages.first()->retrieveFiles();
    files.sort();
    foreach (const QString &file, files) {
        out << file << endl;
    }
    nextOperation();
}

Lisa's avatar
Lisa committed
486
void QueryOperation::nextOperation()
487
{
Lisa's avatar
Lisa committed
488
    if (m_operations.isEmpty()) {
489
        emit queryFinished();
Lisa's avatar
Lisa committed
490 491 492
        return;
    }
    start();
Lukas Appelhans's avatar
Lukas Appelhans committed
493
}