Subversion Repositories tpanel

Rev

Rev 446 | Details | Compare with Previous | Last modification | View Log | RSS feed

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