PageRenderTime 84ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/qcommandline/qcommandline.cpp

https://gitlab.com/atkrad/phantomjs
C++ | 534 lines | 432 code | 76 blank | 26 comment | 111 complexity | 46ac3a204c0e8d349116aae228255c1a MD5 | raw file
  1. /* This file is part of QCommandLine
  2. *
  3. * Copyright (C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Library General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Library General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Library General Public License
  16. * along with this library; see the file COPYING.LIB. If not, write to
  17. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. * Boston, MA 02110-1301, USA.
  19. */
  20. #include <QtCore/QCoreApplication>
  21. #include <QtCore/QQueue>
  22. #include <QtCore/QVariant>
  23. #include <QtCore/QFileInfo>
  24. #include <QDebug>
  25. #include <iostream>
  26. #include "qcommandline.h"
  27. const QCommandLineConfigEntry QCommandLine::helpEntry = { QCommandLine::Switch, QLatin1Char('h'), QLatin1String("help"), tr("Display this help and exit"), QCommandLine::Optional };
  28. const QCommandLineConfigEntry QCommandLine::versionEntry = { QCommandLine::Switch, QLatin1Char('V'), QLatin1String("version"), tr("Display version and exit"), QCommandLine::Optional };
  29. QCommandLine::QCommandLine(QObject * parent)
  30. : QObject(parent), d(new QCommandLinePrivate)
  31. {
  32. setArguments(QCoreApplication::instance()->arguments());
  33. }
  34. QCommandLine::QCommandLine(const QCoreApplication & app,
  35. const QCommandLineConfig & config,
  36. QObject * parent)
  37. : QObject(parent), d(new QCommandLinePrivate)
  38. {
  39. setArguments(app.arguments());
  40. setConfig(config);
  41. enableHelp(true);
  42. enableVersion(true);
  43. }
  44. QCommandLine::QCommandLine(int argc, char *argv[],
  45. const QCommandLineConfig & config,
  46. QObject * parent)
  47. : QObject(parent), d(new QCommandLinePrivate)
  48. {
  49. setArguments(argc, argv);
  50. setConfig(config);
  51. enableHelp(true);
  52. enableVersion(true);
  53. }
  54. QCommandLine::QCommandLine(const QStringList args,
  55. const QCommandLineConfig & config,
  56. QObject * parent)
  57. : QObject(parent), d(new QCommandLinePrivate)
  58. {
  59. setArguments(args);
  60. setConfig(config);
  61. enableHelp(true);
  62. enableVersion(true);
  63. }
  64. QCommandLine::~QCommandLine()
  65. {
  66. delete d;
  67. }
  68. void
  69. QCommandLine::setConfig(const QCommandLineConfig & config)
  70. {
  71. d->config = config;
  72. }
  73. void
  74. QCommandLine::setConfig(const QCommandLineConfigEntry config[])
  75. {
  76. d->config.clear();
  77. while (config->type) {
  78. d->config << *config;
  79. config++;
  80. }
  81. }
  82. QCommandLineConfig
  83. QCommandLine::config()
  84. {
  85. return d->config;
  86. }
  87. void
  88. QCommandLine::setArguments(int argc, char *argv[])
  89. {
  90. d->args.clear();
  91. for (int i = 0; i < argc; i++)
  92. d->args.append(QLatin1String(argv[i]));
  93. }
  94. void
  95. QCommandLine::setArguments(QStringList args)
  96. {
  97. d->args = args;
  98. }
  99. QStringList
  100. QCommandLine::arguments()
  101. {
  102. return d->args;
  103. }
  104. void
  105. QCommandLine::enableHelp(bool enable)
  106. {
  107. d->help = enable;
  108. }
  109. bool
  110. QCommandLine::helpEnabled()
  111. {
  112. return d->help;
  113. }
  114. void
  115. QCommandLine::enableVersion(bool enable)
  116. {
  117. d->version = enable;
  118. }
  119. bool
  120. QCommandLine::versionEnabled()
  121. {
  122. return d->version;
  123. }
  124. bool
  125. QCommandLine::parse()
  126. {
  127. QMap < QString, QCommandLineConfigEntry > conf;
  128. QMap < QString, QCommandLineConfigEntry > confLong;
  129. QQueue < QCommandLineConfigEntry > params;
  130. QMap < QString, QList < QString > > optionsFound;
  131. QMap < QString, int > switchsFound;
  132. QStringList options, switchs;
  133. QStringList args = d->args;
  134. bool allparam = false;
  135. foreach (QCommandLineConfigEntry entry, d->config) {
  136. #if 0
  137. if (entry.type != QCommandLine::Param && entry.shortName == QLatin1Char('\0'))
  138. qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
  139. if (entry.longName.isEmpty())
  140. qWarning() << QLatin1String("QCommandLine: Empty shortname detected");
  141. if (entry.type != QCommandLine::Param && conf.find(entry.shortName) != conf.end())
  142. qWarning() << QLatin1String("QCommandLine: Duplicated shortname detected ") << entry.shortName;
  143. #endif
  144. if (conf.find(entry.longName) != conf.end())
  145. qWarning() << QLatin1String("QCommandLine: Duplicated longname detected ") << entry.shortName;
  146. if (entry.type == QCommandLine::Param)
  147. params << entry;
  148. else
  149. conf[entry.shortName] = entry;
  150. confLong[entry.longName] = entry;
  151. }
  152. if (d->help) {
  153. conf[helpEntry.shortName] = helpEntry;
  154. confLong[helpEntry.longName] = helpEntry;
  155. }
  156. if (d->version) {
  157. conf[versionEntry.shortName] = versionEntry;
  158. confLong[versionEntry.longName] = versionEntry;
  159. }
  160. for (int i = 1; i < args.size(); ++i) {
  161. QString arg = args[i];
  162. bool param = true, shrt = false, stay = false, forward = false;
  163. /* A '+' was found, all remaining options are params */
  164. if (allparam)
  165. param = true;
  166. else if (arg.startsWith(QLatin1String("--"))) {
  167. param = false;
  168. shrt = false;
  169. arg = arg.mid(2);
  170. } else if (arg.startsWith(QLatin1Char('-')) || arg.startsWith(QLatin1Char('+'))) {
  171. if (arg.startsWith(QLatin1Char('+')))
  172. allparam = true;
  173. param = false;
  174. shrt = true;
  175. /* Handle stacked args like `tar -xzf` */
  176. if (arg.size() > 2) {
  177. args[i] = arg.mid(0, 2);
  178. args.insert(i + 1, arg.mid(0, 1) + arg.mid(2));
  179. arg = arg.mid(1, 1);
  180. } else {
  181. arg = arg.mid(1);
  182. }
  183. }
  184. /* Handle params */
  185. if (param) {
  186. if (!params.size()) {
  187. emit parseError(tr("Unknown param: %1").arg(arg));
  188. return false;
  189. }
  190. QCommandLineConfigEntry & entry = params.first();
  191. if (entry.flags & QCommandLine::Mandatory) {
  192. entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
  193. entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
  194. }
  195. if (entry.flags & QCommandLine::ParameterFence) {
  196. allparam = true;
  197. }
  198. emit paramFound(entry.longName, arg);
  199. if (!(entry.flags & QCommandLine::Multiple))
  200. params.dequeue();
  201. } else { /* Options and switchs* */
  202. QString key;
  203. QString value;
  204. int idx = arg.indexOf(QLatin1Char('='));
  205. if (idx != -1) {
  206. key = arg.mid(0, idx);
  207. value = arg.mid(idx + 1);
  208. } else {
  209. key = arg;
  210. }
  211. QMap < QString, QCommandLineConfigEntry > & c = shrt ? conf : confLong;
  212. if (c.find(key) == c.end()) {
  213. emit parseError(tr("Unknown option: %1").arg(key));
  214. return false;
  215. }
  216. QCommandLineConfigEntry & entry = c[key];
  217. if (entry.type == QCommandLine::Switch) {
  218. if (!switchsFound.contains(entry.longName))
  219. switchs << entry.longName;
  220. if (entry.flags & QCommandLine::Multiple)
  221. switchsFound[entry.longName]++;
  222. else
  223. switchsFound[entry.longName] = 1;
  224. } else {
  225. if (stay) {
  226. emit parseError(tr("Option %1 need a value").arg(key));
  227. return false;
  228. }
  229. if (idx == -1) {
  230. if (i+1 < args.size() && !args[i+1].startsWith(QLatin1Char('-'))) {
  231. value = args[i+1];
  232. forward = true;
  233. } else {
  234. emit parseError(tr("Option %1 need a value").arg(key));
  235. return false;
  236. }
  237. }
  238. if (!optionsFound.contains(entry.longName))
  239. options << entry.longName;
  240. if (!(entry.flags & QCommandLine::Multiple))
  241. optionsFound[entry.longName].clear();
  242. optionsFound[entry.longName].append(value);
  243. }
  244. if (entry.flags & QCommandLine::Mandatory) {
  245. entry.flags = (QCommandLine::Flags) (entry.flags & ~QCommandLine::Mandatory);
  246. entry.flags = (QCommandLine::Flags) (entry.flags | QCommandLine::Optional);
  247. conf[entry.shortName] = entry;
  248. confLong[entry.shortName] = entry;
  249. }
  250. }
  251. /* Stay here, stacked args */
  252. if (stay)
  253. i--;
  254. else if (forward)
  255. i++;
  256. }
  257. foreach (QCommandLineConfigEntry entry, params) {
  258. if (entry.flags & QCommandLine::Mandatory) {
  259. emit parseError(tr("Param %1 is mandatory").arg(entry.longName));
  260. return false;
  261. }
  262. }
  263. foreach (QCommandLineConfigEntry entry, conf.values()) {
  264. if (entry.flags & QCommandLine::Mandatory) {
  265. QString type;
  266. if (entry.type == QCommandLine::Switch)
  267. type = tr("Switch");
  268. if (entry.type == QCommandLine::Option)
  269. type = tr("Option");
  270. emit parseError(tr("%1 %2 is mandatory").arg(type).arg(entry.longName));
  271. return false;
  272. }
  273. }
  274. foreach (QString key, switchs) {
  275. for (int i = 0; i < switchsFound[key]; i++) {
  276. if (d->help && key == helpEntry.longName)
  277. showHelp();
  278. if (d->version && key == versionEntry.longName)
  279. showVersion();
  280. emit switchFound(key);
  281. }
  282. }
  283. foreach (QString key, options) {
  284. foreach (QString opt, optionsFound[key])
  285. emit optionFound(key, opt);
  286. } return true;
  287. }
  288. void
  289. QCommandLine::addOption(const QChar & shortName,
  290. const QString & longName,
  291. const QString & descr,
  292. QCommandLine::Flags flags)
  293. {
  294. QCommandLineConfigEntry entry;
  295. entry.type = QCommandLine::Option;
  296. entry.shortName = shortName;
  297. entry.longName = longName;
  298. entry.descr = descr;
  299. entry.flags = flags;
  300. d->config << entry;
  301. }
  302. void
  303. QCommandLine::addSwitch(const QChar & shortName,
  304. const QString & longName,
  305. const QString & descr,
  306. QCommandLine::Flags flags)
  307. {
  308. QCommandLineConfigEntry entry;
  309. entry.type = QCommandLine::Switch;
  310. entry.shortName = shortName;
  311. entry.longName = longName;
  312. entry.descr = descr;
  313. entry.flags = flags;
  314. d->config << entry;
  315. }
  316. void
  317. QCommandLine::addParam(const QString & name,
  318. const QString & descr,
  319. QCommandLine::Flags flags)
  320. {
  321. QCommandLineConfigEntry entry;
  322. entry.type = QCommandLine::Param;
  323. entry.longName = name;
  324. entry.descr = descr;
  325. entry.flags = flags;
  326. d->config << entry;
  327. }
  328. void
  329. QCommandLine::removeOption(const QString & name)
  330. {
  331. int i;
  332. for (i = 0; i < d->config.size(); ++i) {
  333. if (d->config[i].type == QCommandLine::Option &&
  334. (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
  335. d->config.removeAt(i);
  336. return ;
  337. }
  338. }
  339. }
  340. void
  341. QCommandLine::removeSwitch(const QString & name)
  342. {
  343. int i;
  344. for (i = 0; i < d->config.size(); ++i) {
  345. if (d->config[i].type == QCommandLine::Switch &&
  346. (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
  347. d->config.removeAt(i);
  348. return ;
  349. }
  350. }
  351. }
  352. void
  353. QCommandLine::removeParam(const QString & name)
  354. {
  355. int i;
  356. for (i = 0; i < d->config.size(); ++i) {
  357. if (d->config[i].type == QCommandLine::Param &&
  358. (d->config[i].shortName == name.at(0) || d->config[i].longName == name)) {
  359. d->config.removeAt(i);
  360. return ;
  361. }
  362. }
  363. }
  364. QString
  365. QCommandLine::help(bool logo)
  366. {
  367. QString h;
  368. if (logo)
  369. h = version() + QLatin1String("\n");
  370. h = QLatin1String("Usage:\n ");
  371. /* Executable name */
  372. if (!d->args.isEmpty())
  373. h += QFileInfo(d->args[0]).baseName();
  374. else
  375. h += QCoreApplication::applicationName();
  376. h.append(QLatin1String(" [switches] [options]"));
  377. /* Arguments, short */
  378. foreach (QCommandLineConfigEntry entry, d->config) {
  379. if (entry.type == QCommandLine::Option) {
  380. if (entry.flags & QCommandLine::Mandatory)
  381. h.append(QLatin1String(" --") + entry.longName + QLatin1String("=<val>"));
  382. }
  383. if (entry.type == QCommandLine::Param) {
  384. h.append(QLatin1String(" "));
  385. if (entry.flags & QCommandLine::Optional)
  386. h.append(QLatin1String("["));
  387. h.append(entry.longName);
  388. if (entry.flags & QCommandLine::Multiple)
  389. h.append(QLatin1String(" [") + entry.longName + QLatin1String(" [...]]"));
  390. if (entry.flags & QCommandLine::Optional)
  391. h.append(QLatin1String("]"));
  392. }
  393. }
  394. h.append(QLatin1String("\n\n"));
  395. h.append(QLatin1String("Options:\n"));
  396. QStringList vals;
  397. QStringList descrs;
  398. int max = 0;
  399. foreach (QCommandLineConfigEntry entry, d->config) {
  400. QString val;
  401. if (entry.type == QCommandLine::Option) {
  402. if (entry.shortName != QLatin1Char('\0'))
  403. val = QLatin1String("-") + QString(entry.shortName) + QLatin1Char(',');
  404. val += QLatin1String("--") + entry.longName + QLatin1String("=<val>");
  405. }
  406. if (entry.type == QCommandLine::Switch)
  407. val = QLatin1String("-") + QString(entry.shortName) + QLatin1String(",--") + entry.longName;
  408. #if 0
  409. if (entry.type == QCommandLine::Param)
  410. val = entry.longName;
  411. #endif
  412. if (val.size() > max)
  413. max = val.size();
  414. vals.append(val);
  415. descrs.append(entry.descr + QLatin1String("\n"));
  416. }
  417. for (int i = 0; i < vals.size(); ++i) {
  418. if (vals[i].isEmpty()) continue;
  419. h.append(QLatin1String(" "));
  420. h.append(vals[i]);
  421. h.append(QString(QLatin1String(" ")).repeated(max - vals[i].size() + 2));
  422. h.append(descrs[i]);
  423. }
  424. #if 0
  425. h.append(tr("\nMandatory arguments to long options are mandatory for short options too.\n"));
  426. #endif
  427. return h;
  428. }
  429. QString
  430. QCommandLine::version()
  431. {
  432. QString v;
  433. v = QCoreApplication::applicationName() + QLatin1Char(' ');
  434. v += QCoreApplication::applicationVersion();
  435. if (!QCoreApplication::organizationDomain().isEmpty()
  436. || !QCoreApplication::organizationName().isEmpty())
  437. v = v + QLatin1String(" - ") +
  438. QCoreApplication::organizationDomain() + QLatin1String(" ") +
  439. QCoreApplication::organizationDomain();
  440. return v + QLatin1Char('\n');
  441. }
  442. void
  443. QCommandLine::showHelp(bool quit, int returnCode)
  444. {
  445. std::cerr << qPrintable(help());
  446. if (quit) {
  447. // Can't call QApplication::exit() here, because we may be called before app.exec()
  448. exit(returnCode);
  449. }
  450. }
  451. void
  452. QCommandLine::showVersion(bool quit, int returnCode)
  453. {
  454. std::cerr << qPrintable(version());
  455. if (quit) {
  456. exit(returnCode);
  457. }
  458. }