Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
21 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
2 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
#include <iostream>
19
#include <iomanip>
20
#include <string>
21
#include <vector>
22
#include <algorithm>
235 andreas 23
 
2 andreas 24
#include "tconfig.h"
235 andreas 25
#include "terror.h"
3 andreas 26
#include "tpagemanager.h"
2 andreas 27
#include "tqtmain.h"
326 andreas 28
#include "testmode.h"
239 andreas 29
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
22 andreas 30
#include <QStandardPaths>
31
#endif
32
#ifdef __ANDROID__
33
#include <android/log.h>
34
#endif
35
 
2 andreas 36
using std::string;
37
using std::find;
38
using std::vector;
21 andreas 39
using std::cout;
239 andreas 40
using std::cerr;
21 andreas 41
using std::endl;
2 andreas 42
 
90 andreas 43
bool _restart_ = false;
239 andreas 44
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
91 andreas 45
extern std::atomic<bool> killed;
46
extern std::atomic<bool> _netRunning;
47
#endif
90 andreas 48
 
21 andreas 49
/**
50
 * @class InputParser
51
 * @brief The InputParser class parses the command line.
52
 *
53
 * This class takes the command line arguments and parses them. It creates an
54
 * internal vector array to hold the parameters.
55
 */
2 andreas 56
class InputParser
57
{
3 andreas 58
    public:
21 andreas 59
        /**
60
         * @brief InputParser is the constructor.
61
         *
62
         * The constructor requires the command line parameters. It immediately
118 andreas 63
         * starts to parse each parameter. If it finds the string `--` it stops
21 andreas 64
         * and ignores the rest of parameters. \p argc contains the rest of
65
         * the parameters after `--`, if there are any.
66
         *
67
         * @param argc  A pointer to the numbers of command line parameters.
68
         *              This parameter must not be `NULL`.
69
         * @param argv  The 2 dimensional array of command line parameters.
70
         *              This parameter must not be `NULL`.
71
         */
3 andreas 72
        InputParser(int *argc, char **argv)
73
        {
74
            int i;
2 andreas 75
 
3 andreas 76
            for (i = 1; i < *argc; ++i)
77
            {
78
                if (string(argv[i]).compare("--") == 0)
79
                    break;
2 andreas 80
 
3 andreas 81
                this->tokens.push_back(string(argv[i]));
82
            }
2 andreas 83
 
3 andreas 84
            *argc -= i;
2 andreas 85
 
3 andreas 86
            if (*argc <= 1)
87
                *argc = 1;
88
            else
89
            {
90
                *argc = *argc + 1;
91
                *(argv + i - 1) = *argv;
92
            }
93
        }
21 andreas 94
        /**
95
         * @brief getCmdOption searches for the command line option \p option.
96
         * @author iain
97
         *
98
         * The method searches for the command line option \p option and
99
         * returns the parameter after the option, if there is one.
100
         *
101
         * @param option    The name of a command line option. This is any
102
         *                  name starting with 1 or 2 dash (-). The name in
103
         *                  the parameter must include the dash(es) in front
104
         *                  of the name.\n
105
         *                  If the option was found and the next parameter on
106
         *                  the command line doesn't start with a dash, the
107
         *                  parameter is returned.
108
         *
109
         * @return Tf the option was found and the parameter on the command
110
         * line following the option doesn't start with a dash, it is returned.
111
         * Otherwise an empty string is returned.
112
         */
3 andreas 113
        const string& getCmdOption(const string &option) const
114
        {
115
            vector<string>::const_iterator itr;
116
            itr = find(this->tokens.begin(), this->tokens.end(), option);
2 andreas 117
 
3 andreas 118
            if (itr != this->tokens.end() && ++itr != this->tokens.end())
119
                return *itr;
2 andreas 120
 
3 andreas 121
            static const string empty_string("");
122
            return empty_string;
123
        }
21 andreas 124
 
125
        /**
126
         * @brief cmdOptionExists tests for an existing option.
127
         * @author iain
128
         *
129
         * This function tests whether the option \p option exists or not. If
130
         * the option was found, it returnes `true`.
131
         *
132
         * @param option    The name of a command line option. This is any
133
         *                  name starting with 1 or 2 dash (-). The name in
134
         *                  the parameter must include the dash(es) in front
135
         *                  of the name.\n
136
         *
137
         * @return If the command line option was found in the internal vector
138
         * array `true` is returned. Otherwise it returnes `false`.
139
         */
3 andreas 140
        bool cmdOptionExists(const string &option) const
141
        {
142
            return find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
143
        }
2 andreas 144
 
3 andreas 145
    private:
146
        vector <string> tokens;
2 andreas 147
};
148
 
21 andreas 149
/**
150
 * @brief usage displays on the standard output a small help.
151
 *
152
 * This function shows a short help with all available parameters and a brief
153
 * description of them.
154
 * \verbatim
155
 * NOTE: This function is not available on Android systems.
156
 * \endverbatim
157
 */
2 andreas 158
void usage()
159
{
239 andreas 160
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
132 andreas 161
    cout << TConfig::getProgName() << " version " <<  VERSION_STRING() << endl << endl;
326 andreas 162
#if TESTMODE == 1
163
    cout << "Usage: tpanel [-c <config file>] [-t]" << endl;
164
#else
21 andreas 165
    cout << "Usage: tpanel [-c <config file>]" << endl;
326 andreas 166
#endif
21 andreas 167
    cout << "-c | --config-file <file> The path and name of the configuration file." << endl;
168
    cout << "                          This parameter is optional. If it is omitted," << endl;
169
    cout << "                          The standard path is searched for the" << endl;
170
    cout << "                          configuration file." << endl << endl;
326 andreas 171
#if TESTMODE == 1
324 andreas 172
    cout << "-t | --test-mode          Opens all files in the local directory with the" << endl;
173
    cout << "                          extension .tst and executes the content. It" << endl;
174
    cout << "                          prints detailed information about the status of" << endl;
175
    cout << "                          each test." << endl << endl;
326 andreas 176
#endif
21 andreas 177
    cout << "-h | --help               This help." << endl << endl;
178
#endif
2 andreas 179
}
180
 
239 andreas 181
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
21 andreas 182
/**
183
 * @brief banner displays a shor banner with informations about this application.
184
 *
185
 * This function shows a short information about this application. It prints
186
 * this on the standard output.
187
 * \verbatim
188
 * NOTE: This function is not available on Android systems.
189
 * \endverbatim
190
 *
191
 * @param pname The name of this application.
192
 */
2 andreas 193
void banner(const string& pname)
194
{
3 andreas 195
    if (!TConfig::showBanner())
196
        return;
2 andreas 197
 
132 andreas 198
    cout << pname << " v" << VERSION_STRING() << endl;
21 andreas 199
    cout << "(C) Andreas Theofilu <andreas@theosys.at>" << endl;
200
    cout << "This program is under the terms of GPL version 3" << endl << endl;
22 andreas 201
}
21 andreas 202
#endif
2 andreas 203
 
21 andreas 204
/**
90 andreas 205
 * @brief Is called whenever the program starts up.
206
 *
207
 * This method is called to start up the program. It initializes the main
208
 * classes and waits until the main loop ends.
209
 * It is also called if the program have to start over. This happens when
210
 * the settings change the host, port or channel ID, or after receiving a new
211
 * surface.
212
 *
93 andreas 213
 * @param oldArgc   This is the previous parameter counter (only for desktop).
90 andreas 214
 * @param argc      This is the actual parameter counter.
215
 * @param argv      This is the pointer array to the environment.
216
 *
217
 * @return of success TRUE is returned. Otherwise FALSE.
218
 */
239 andreas 219
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
90 andreas 220
bool _startUp(int oldArgc, int argc, char *argv[])
125 andreas 221
#else
222
bool _startUp(int, int argc, char *argv[])
223
#endif
90 andreas 224
{
92 andreas 225
    DECL_TRACER("_startUp(int oldArgc, int argc, char *argv[])");
90 andreas 226
 
92 andreas 227
    TPageManager *pageManager = new TPageManager;
228
 
90 andreas 229
    if (TError::isError())
92 andreas 230
    {
260 andreas 231
#ifdef __ANDROID__
232
        __android_log_print(ANDROID_LOG_FATAL, "tpanel", "There was an unrecoverable error in creating the page manager!");
233
#else
239 andreas 234
        cerr << "FATAL: There was an unrecoverable error in creating the page manager!" << endl;
260 andreas 235
#endif
92 andreas 236
        delete pageManager;
119 andreas 237
        pageManager = nullptr;
90 andreas 238
        return false;
92 andreas 239
    }
90 andreas 240
 
241
    // Prepare command line stack
239 andreas 242
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
90 andreas 243
    int pt = oldArgc - argc;
244
#else
245
    int pt = 0;
246
#endif
247
    // Start the graphical environment
248
    int ret = 0;
93 andreas 249
    // The _restart_ variable is reset in class initialization MainWindow.
92 andreas 250
    ret = qtmain(argc, &argv[pt], pageManager);
251
    delete pageManager;
119 andreas 252
    pageManager = nullptr;
90 andreas 253
 
254
    if (ret != 0)
255
        return false;
256
 
257
    return true;
258
}
259
 
260
/**
21 andreas 261
 * @brief main is the main entry function.
262
 *
263
 * This is where the program starts.
264
 *
265
 * @param argc  The number of command line arguments.
266
 * @param argv  A pointer to a 2 dimensional array containing the command line
267
 *              parameters.
268
 *
269
 * @return 0 on success. This means that no errors occured.\n
270
 * In case of an error a number grater than 0 is returned.
271
 */
2 andreas 272
int main(int argc, char *argv[])
273
{
3 andreas 274
    string configFile;
326 andreas 275
    string testPath;
91 andreas 276
    int oldArgc = argc;
239 andreas 277
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
3 andreas 278
    string pname = *argv;
279
    size_t pos = pname.find_last_of("/");
251 andreas 280
    bool haveParameters = (argc > 1);
2 andreas 281
 
3 andreas 282
    if (pos != string::npos)
283
        pname = pname.substr(pos + 1);
22 andreas 284
#else
285
    string pname = "tpanel";
91 andreas 286
    killed = false;
287
    _netRunning = false;
251 andreas 288
    bool haveParameters = false;
22 andreas 289
#endif
21 andreas 290
    TConfig::setProgName(pname);    // Remember the name of this application.
239 andreas 291
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
21 andreas 292
    InputParser input(&argc, argv); // Parse the command line parameters.
326 andreas 293
#if TESTMODE == 1
324 andreas 294
    if (input.cmdOptionExists("-t") || input.cmdOptionExists("--test-mode"))
295
    {
326 andreas 296
        testPath = input.getCmdOption("-t");
297
 
298
        if (testPath.empty())
299
            testPath = input.getCmdOption("--test-mode");
300
 
301
        if (testPath.empty())
302
        {
303
            banner(pname);
304
            std::cerr << "Missing the path where the test cases are!" << std::endl;
305
            usage();
306
            return 1;
307
        }
308
 
324 andreas 309
        _testmode = true;
326 andreas 310
#ifdef __ANDROID__
311
        __android_log_print(ANDROID_LOG_INFO, "tpanel", "Testmode enabled!");
312
#else
313
        cout << "Testmod enabled" << endl;
314
#endif
324 andreas 315
    }
326 andreas 316
#endif
21 andreas 317
    // Evaluate the command line parameters.
3 andreas 318
    if (input.cmdOptionExists("-h") || input.cmdOptionExists("--help"))
319
    {
320
        banner(pname);
321
        usage();
322
        return 0;
323
    }
2 andreas 324
 
326 andreas 325
    if (input.cmdOptionExists("-c") || input.cmdOptionExists("--config-file"))
3 andreas 326
    {
327
        configFile = input.getCmdOption("-c");
2 andreas 328
 
3 andreas 329
        if (configFile.empty())
330
            configFile = input.getCmdOption("--config-file");
2 andreas 331
 
3 andreas 332
        if (configFile.empty())
333
        {
334
            banner(pname);
335
            std::cerr << "Missing the path and name of the configuration file!" << std::endl;
336
            usage();
337
            return 1;
338
        }
339
    }
22 andreas 340
#endif
326 andreas 341
    if (!_testmode && haveParameters && configFile.empty())
251 andreas 342
    {
260 andreas 343
#ifdef __ANDROID__
344
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "Unknown command line parameter found!");
345
#else
251 andreas 346
        cerr << "ERROR: Unknown command line parameter found!" << endl;
260 andreas 347
#endif
251 andreas 348
        usage();
349
        return 1;
350
    }
326 andreas 351
    else if (_testmode)
352
    {
327 andreas 353
        configFile = testPath + "/testconfig.cfg";
354
        _gTestMode = new _TestMode(testPath);
326 andreas 355
    }
251 andreas 356
 
21 andreas 357
    TError::clear();                    // Clear all errors (initialize)
358
    TConfig config(configFile);         // Read the configuration file.
2 andreas 359
 
21 andreas 360
    if (TError::isError())              // Exit if the previous command failed.
22 andreas 361
    {
126 andreas 362
#ifdef __ANDROID__
363
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "%s", TError::getErrorMsg().c_str());
364
#endif
22 andreas 365
        TError::displayMessage(TError::getErrorMsg());
3 andreas 366
        return 1;
22 andreas 367
    }
239 andreas 368
#if not defined(Q_OS_ANDROID) && not defined(Q_OS_IOS)
3 andreas 369
    banner(pname);
22 andreas 370
#endif
3 andreas 371
    TError::clear();
58 andreas 372
    // Start the page manager. This is the core class handling everything.
373
    try
374
    {
90 andreas 375
        bool ret;
2 andreas 376
 
90 andreas 377
        do
378
        {
379
            ret = _startUp(oldArgc, argc, argv);
2 andreas 380
 
90 andreas 381
            if (_restart_)
382
            {
383
                MSG_INFO("Starting over ...");
384
                prg_stopped = false;
385
                killed = false;
386
            }
387
        }
388
        while (_restart_);
21 andreas 389
 
90 andreas 390
        if (!ret)
391
        {
126 andreas 392
#ifdef __ANDROID__
260 andreas 393
            __android_log_print(ANDROID_LOG_FATAL, "tpanel", "Terminating because of a previous fatal error!");
394
#else
395
            MSG_ERROR("Terminating because of a previous fatal error!");
126 andreas 396
#endif
90 andreas 397
            return 1;
398
        }
58 andreas 399
    }
400
    catch (std::exception& e)
401
    {
126 andreas 402
#ifdef __ANDROID__
260 andreas 403
        __android_log_print(ANDROID_LOG_FATAL, "tpanel", "%s", e.what());
126 andreas 404
#endif
58 andreas 405
        MSG_ERROR("Fatal: " << e.what());
406
        return 1;
407
    }
2 andreas 408
 
3 andreas 409
    return 0;
2 andreas 410
}