akabeiparser.cpp 6.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * This file is part of the Chakra project
 * Parses an akabei-style command line

   Copyright (C) 2011 Lisa Vitolo <shainer@chakra-project.org>

   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.
*/

Lisa's avatar
Lisa committed
13
#include <akabeiparser.h>
14

15 16
AkabeiParser::AkabeiParser(const QStringList &args, const AkabeiAboutData& about)
    : CommandLineParser(args, about)
17 18
{
    /* Special operations */
Lisa's avatar
Lisa committed
19 20 21 22
    AkabeiOperation help(APM::NoType, APM::Help, "", "help", QObject::tr("Show a help message"));
    AkabeiOperation version(APM::NoType, APM::Version, "", "version", QObject::tr("Show version information"));
    AkabeiOperation license(APM::NoType, APM::License, "", "license", QObject::tr("Show license information"));
    AkabeiOperation authors(APM::NoType, APM::Authors, "", "authors", QObject::tr("Show information about the authors"));
23

24 25 26 27 28 29
    m_acceptedOperations.insert("help", help);
    m_acceptedOperations.insert("version", version);
    m_acceptedOperations.insert("license", license);
    m_acceptedOperations.insert("authors", authors);
}

30
void AkabeiParser::addOperation(AkabeiOperation &op)
31
{
32 33 34
    foreach (APM::OptionName opt, generalOptions) {
        op.addConnectionWithOption(opt);
    }
35

36
    m_acceptedOperations.insert(op.commandLong(), op);
37 38
}

Lisa's avatar
Lisa committed
39
void AkabeiParser::addOption(APM::OptionName name, QString const& cmdShort, QString const& cmdLong, QString const& desc, QString const& argname)
40
{
Lisa's avatar
Lisa committed
41
    AkabeiOption option(name, cmdShort, cmdLong, desc, argname);
42

43 44 45 46 47 48
    m_acceptedOptions.insert(cmdShort, option);
    m_acceptedOptions.insert(cmdLong, option);
}

void AkabeiParser::connectOptionWithEverything(APM::OptionName name)
{
49
    generalOptions.push_front(name);
50 51 52 53 54
}

void AkabeiParser::parse()
{
    doParse();
55

56
    switch (m_operation.name()) {
Lisa's avatar
Lisa committed
57 58 59 60 61
        case APM::Help: {
            QMultiHash<APM::OperationType, AkabeiOperation> acceptedOpForMessage;
            foreach (const AkabeiOperation &op, m_acceptedOperations.values()) {
                acceptedOpForMessage.insertMulti(op.type(), op);
            }
62

Lisa's avatar
Lisa committed
63
            CommandLineUtils::printHelpMessage(acceptedOpForMessage, m_acceptedOptions.values(), m_data);
64
            break;
Lisa's avatar
Lisa committed
65
        }
66

Lisa's avatar
Lisa committed
67
        case APM::Version: {
68 69
            CommandLineUtils::printVersion(m_data);
            break;
Lisa's avatar
Lisa committed
70
        }
71

Lisa's avatar
Lisa committed
72
        case APM::License: {
73 74
            CommandLineUtils::printLicenses(m_data);
            break;
Lisa's avatar
Lisa committed
75
        }
76

Lisa's avatar
Lisa committed
77
        case APM::Authors: {
78
            CommandLineUtils::printAuthors(m_data);
Lisa's avatar
Lisa committed
79 80
            break;
        }
81

82 83 84 85 86 87
        default:
            break;
    }
}

void AkabeiParser::doParse()
88
{
89 90 91 92
    bool foundOp = false;
    bool isArgExpected = false;
    AkabeiOption previousOption;
    QString optionLit;
93

94 95
    for (QStringList::iterator it = m_argv.begin(); it < m_argv.end(); it++) {
        QString arg = (*it);
96
        optionLit.clear();
97

98 99 100 101 102
        /*
         * This means a situation like (--ignore requires arg)
         * --ignore arg1,arg2
         * So in the previous iteration we were waiting for args to pop up.
         */
103

104 105 106 107 108 109 110
        if (isArgExpected) {
            /* The option wasn't added, so we add it now */
            previousOption.addArguments(arg);
            m_options.insert(previousOption.name(), previousOption);
            isArgExpected = false;
            continue;
        }
111

112 113 114 115 116
        /* Leaves out the dashes */
        if (arg.startsWith("--")) {
            optionLit = arg.mid(2);
        } else if (arg.startsWith("-")) {
            optionLit = arg.mid(1);
117
        }
118

119 120 121 122 123 124 125
        /* If there actually were dashes... */
        if (!optionLit.isEmpty()) {
            if (!m_acceptedOptions.contains(optionLit)) {
                throw UnknownOptionException(optionLit);
            }
            /* Takes the argument from our structure to have all the info in one go */
            AkabeiOption& option = m_acceptedOptions[optionLit];
126

127 128 129 130 131
            if (option.hasArgs()) {
                isArgExpected = true;
                previousOption = option;
                continue;
            }
132 133

            m_options.insert(option.name(), option);
134 135 136 137 138 139
        } else { /* the first normal string is an operation, the next generic arguments */
            if (!foundOp) {

                if (!m_acceptedOperations.contains(arg)) {
                    throw UnknownOperationException(arg);
                }
140

141 142 143 144 145 146 147
                m_operation = m_acceptedOperations[arg];
                foundOp = true;
            } else {
                m_args.append(arg);
            }
        }
    }
148

149 150 151 152
    /* We waited without success for the argument to appear */
    if (isArgExpected) {
        throw OptionArgExpectedException(optionLit);
    }
153

154 155 156 157
    /* An operation was never found */
    if (!foundOp) {
        throw MissingOperationException();
    }
158

159 160 161 162 163 164
    /* Checks if all the options are in-context */
    foreach (const AkabeiOption& opt, m_options.values()) {
        if (!m_operation.isOptionSupported(opt.name())) {
            throw OptionOutOfContextException(opt.commandLong());
        }
    }
165

166
    /* Free arguments */
167
    /*if ((!m_args.isEmpty() && !m_operation.hasFreeArgs())) {
168
        throw ArgumentsOutOfContextException();
169
    }*///NOTE: We don't throw this anymore, as we could have arguments, but not needed... e.g. akabei files can have -p /path/to/package OR an argument
170 171 172 173 174 175 176 177 178 179 180 181 182
    if (m_args.isEmpty() && m_operation.hasFreeArgs()) {
        throw ExpectedArgumentsException();
    }
}

/*
 * Maps the current operation with the type associated
 */
APM::OperationType AkabeiParser::type()
{
    if (m_options.contains(APM::Local)) {
        return APM::QueryOperationType;
    }
183

184 185 186 187 188 189 190 191 192
    return m_operation.type();
}

/*
 * Getters from the parsing results
 */
QList< APM::OperationName > AkabeiParser::operations()
{
    QList<APM::OperationName> ops;
193

194 195 196 197 198 199
    /*
     * This happens because the two parsers aren't 100% compatibles
     * in the operations returned.
     */
    if (m_operation.name() == APM::UpgradeSystem) {
        ops.append(APM::UpdateDatabases);
Lisa's avatar
Lisa committed
200
        ops.append(APM::UpdateSystem);
201 202 203
    } else if (m_operation.name() == APM::Newest) {
        ops.append(APM::UpdateDatabases);
        ops.append(APM::Install);
204 205
    } else if (m_operation.name() == APM::UpdateDatabases && m_options.contains(APM::Force)) {
        ops.append(APM::ForceUpdateDatabases);
206 207 208
    } else {
        ops.append(m_operation.name());
    }
209

210 211 212 213 214 215
    return ops;
}

QHash<APM::OptionName, QStringList> AkabeiParser::options()
{
    QHash<APM::OptionName, QStringList> result;
216

217 218 219
    foreach (APM::OptionName key, m_options.keys()) {
        result.insert(key, m_options[key].arguments());
    }
220

221 222 223 224 225 226
    return result;
}

QStringList AkabeiParser::args()
{
    return m_args;
227
}