CmdLineApp.C 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /* $XConsortium: CmdLineApp.C /main/2 1996/08/12 12:36:14 mgreess $ */
  24. // Copyright (c) 1996 James Clark
  25. // See the file COPYING for copying permission.
  26. // Need option registration method that allows derived class to change
  27. // option names.
  28. #ifdef __GNUG__
  29. #pragma implementation
  30. #endif
  31. #include "splib.h"
  32. #include "CmdLineApp.h"
  33. #include "CmdLineAppMessages.h"
  34. #include "MessageArg.h"
  35. #include "ErrnoMessageArg.h"
  36. #include "Options.h"
  37. #include "version.h"
  38. #include "xnew.h"
  39. #include "macros.h"
  40. #include "sptchar.h"
  41. #include "MessageTable.h"
  42. #ifdef SP_MULTI_BYTE
  43. #include "UTF8CodingSystem.h"
  44. #include "Fixed2CodingSystem.h"
  45. #include "UnicodeCodingSystem.h"
  46. #include "EUCJPCodingSystem.h"
  47. #include "SJISCodingSystem.h"
  48. #include "ISO8859InputCodingSystem.h"
  49. #ifdef WIN32
  50. #include "Win32CodingSystem.h"
  51. #endif
  52. #endif /* SP_MULTI_BYTE */
  53. #include "IdentityCodingSystem.h"
  54. #include "ConsoleOutput.h"
  55. #if defined(__linux__) || defined(CSRG_BASED) || defined(sun)
  56. #include <iostream>
  57. #include <fstream>
  58. using namespace std;
  59. #else
  60. #include <iostream.h>
  61. #include <fstream.h>
  62. #endif
  63. #include <errno.h>
  64. #include <string.h>
  65. #include <stdlib.h>
  66. #include <ctype.h>
  67. #ifdef SP_HAVE_LOCALE
  68. #include <locale.h>
  69. #endif
  70. #ifdef SP_HAVE_SETMODE
  71. #include <fcntl.h>
  72. #include <io.h>
  73. #endif
  74. #ifdef SP_HAVE_SETMODE
  75. #define IOS_BINARY ios::binary
  76. #else
  77. #define IOS_BINARY 0
  78. #endif
  79. #ifdef SP_WIDE_SYSTEM
  80. #include <stdio.h>
  81. #else /* not SP_WIDE_SYSTEM */
  82. #include <sys/types.h>
  83. #ifdef SP_INCLUDE_UNISTD_H
  84. #include <unistd.h>
  85. #endif
  86. #ifdef SP_INCLUDE_IO_H
  87. #include <io.h>
  88. #endif
  89. #endif /* not SP_WIDE_SYSTEM */
  90. #ifdef SP_NAMESPACE
  91. namespace SP_NAMESPACE {
  92. #endif
  93. #ifdef SP_MULTI_BYTE
  94. static UTF8CodingSystem utf8CodingSystem;
  95. static Fixed2CodingSystem fixed2CodingSystem;
  96. static UnicodeCodingSystem unicodeCodingSystem;
  97. static EUCJPCodingSystem eucjpCodingSystem;
  98. static SJISCodingSystem sjisCodingSystem;
  99. #ifdef WIN32
  100. static Win32CodingSystem ansiCodingSystem(Win32CodingSystem::codePageAnsi);
  101. static Win32CodingSystem oemCodingSystem(Win32CodingSystem::codePageOEM);
  102. static UnicodeCodingSystem maybeUnicodeCodingSystem(&ansiCodingSystem);
  103. #endif
  104. #endif /* SP_MULTI_BYTE */
  105. static IdentityCodingSystem identityCodingSystem;
  106. static struct {
  107. const char *name;
  108. const CodingSystem *cs;
  109. } codingSystems[] = {
  110. #ifdef SP_MULTI_BYTE
  111. { "UTF-8", &utf8CodingSystem },
  112. { "FIXED-2", &fixed2CodingSystem },
  113. { "UNICODE", &unicodeCodingSystem },
  114. { "EUC-JP", &eucjpCodingSystem },
  115. { "SJIS", &sjisCodingSystem },
  116. #ifdef WIN32
  117. { "WINDOWS", &ansiCodingSystem },
  118. { "MS-DOS", &oemCodingSystem },
  119. { "WUNICODE", &maybeUnicodeCodingSystem },
  120. #endif
  121. #endif /* SP_MULTI_BYTE */
  122. { "IS8859-1", &identityCodingSystem },
  123. { "IDENTITY", &identityCodingSystem },
  124. };
  125. const CodingSystem *CmdLineApp::codingSystem_ = 0;
  126. static const SP_TCHAR *progName = 0;
  127. static const SP_TCHAR versionString[] = SP_VERSION;
  128. CmdLineApp::CmdLineApp()
  129. : errorFile_(0),
  130. outputCodingSystem_(0),
  131. // Colon at beginning is Posix.2ism that says to return : rather than ? for
  132. // missing option argument.
  133. optstr_(SP_T(":"), 1),
  134. MessageReporter(makeStdErr())
  135. {
  136. registerOption('b', SP_T("bctf"));
  137. registerOption('f', SP_T("error_file"));
  138. registerOption('v');
  139. }
  140. void CmdLineApp::registerOption(AppChar c, const AppChar *argName)
  141. {
  142. optstr_ += c;
  143. if (argName) {
  144. optstr_ += SP_T(':');
  145. optArgNames_.push_back(argName);
  146. }
  147. }
  148. StringC CmdLineApp::usageString()
  149. {
  150. String<AppChar> result;
  151. size_t i;
  152. if (progName)
  153. result.assign(progName, tcslen(progName));
  154. PackedBoolean hadOption[128];
  155. for (i = 0; i < 128; i++)
  156. hadOption[i] = 0;
  157. Boolean hadNoArgOption = 0;
  158. for (i = 1; i < optstr_.size(); i++) {
  159. if (optstr_[i] == 0)
  160. break;
  161. if (i + 1 < optstr_.size() && optstr_[i + 1] == ':')
  162. i++;
  163. else if (!hadOption[optstr_[i]]) {
  164. hadOption[optstr_[i]] = 1;
  165. if (!hadNoArgOption) {
  166. hadNoArgOption = 1;
  167. result.append(SP_T(" [-"), 3);
  168. }
  169. result += optstr_[i];
  170. }
  171. }
  172. if (hadNoArgOption)
  173. result += SP_T(']');
  174. size_t j = 0;
  175. for (i = 1; i < optstr_.size(); i++) {
  176. if (i + 1 < optstr_.size() && optstr_[i + 1] == ':') {
  177. if (!hadOption[optstr_[i]]) {
  178. hadOption[optstr_[i]] = 1;
  179. result += SP_T(' ');
  180. result += SP_T('[');
  181. result += SP_T('-');
  182. result += optstr_[i];
  183. result += SP_T(' ');
  184. result.append(optArgNames_[j], tcslen(optArgNames_[j]));
  185. result += SP_T(']');
  186. }
  187. i++;
  188. j++;
  189. }
  190. }
  191. result.append(SP_T(" sysid..."), tcslen(SP_T(" sysid...")));
  192. result += 0;
  193. return convertInput(result.data());
  194. }
  195. static
  196. void ewrite(const AppChar *s)
  197. {
  198. #ifdef SP_WIDE_SYSTEM
  199. fputts(s, stderr);
  200. #else
  201. int n = (int)strlen(s);
  202. while (n > 0) {
  203. int nw = write(2, s, n);
  204. if (nw < 0)
  205. break;
  206. n -= nw;
  207. s += nw;
  208. }
  209. #endif
  210. }
  211. static
  212. #ifdef SP_FANCY_NEW_HANDLER
  213. int outOfMemory(size_t)
  214. #else
  215. void outOfMemory()
  216. #endif
  217. {
  218. if (progName) {
  219. ewrite(progName);
  220. ewrite(SP_T(": "));
  221. }
  222. ewrite(SP_T(": out of memory\n"));
  223. exit(1);
  224. #ifdef SP_FANCY_NEW_HANDLER
  225. return 0;
  226. #endif
  227. }
  228. int CmdLineApp::init(int, AppChar **argv)
  229. {
  230. set_new_handler(outOfMemory);
  231. #ifdef SP_HAVE_LOCALE
  232. setlocale(LC_ALL, "");
  233. #endif
  234. #ifdef SP_HAVE_SETMODE
  235. _setmode(1, _O_BINARY);
  236. _setmode(2, _O_BINARY);
  237. #endif
  238. progName = argv[0];
  239. if (progName)
  240. setProgramName(convertInput(progName));
  241. #ifdef __GNUG__
  242. // cout is a performance disaster in libg++ unless we do this.
  243. ios::sync_with_stdio(0);
  244. #endif
  245. return 0;
  246. }
  247. int CmdLineApp::run(int argc, AppChar **argv)
  248. {
  249. int ret = init(argc, argv);
  250. if (ret)
  251. return ret;
  252. int firstArg;
  253. ret = processOptions(argc, argv, firstArg);
  254. if (ret)
  255. return ret;
  256. ret = processArguments(argc - firstArg, argv + firstArg);
  257. progName = 0;
  258. return ret;
  259. }
  260. Boolean CmdLineApp::openFilebufWrite(filebuf &file,
  261. const AppChar *filename)
  262. {
  263. #ifdef SP_WIDE_SYSTEM
  264. int fd = _wopen(filename, _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY);
  265. if (fd < 0)
  266. return 0;
  267. return file.attach(fd) != 0;
  268. #else
  269. #if defined(__linux__) || defined(CSRG_BASED) || defined(sun)
  270. return file.open(filename, ios::out|ios::trunc) != 0;
  271. #else
  272. return file.open(filename, ios::out|ios::trunc|IOS_BINARY) != 0;
  273. #endif
  274. #endif
  275. }
  276. int CmdLineApp::processOptions(int argc, AppChar **argv, int &nextArg)
  277. {
  278. AppChar ostr[2];
  279. optstr_ += SP_T('\0');
  280. Options<AppChar> options(argc, argv, optstr_.data());
  281. AppChar opt;
  282. while (options.get(opt)) {
  283. switch (opt) {
  284. case ':':
  285. ostr[0] = options.opt();
  286. ostr[1] = SP_T('\0');
  287. message(CmdLineAppMessages::missingOptionArgError,
  288. StringMessageArg(convertInput(ostr)));
  289. message(CmdLineAppMessages::usage,
  290. StringMessageArg(usageString()));
  291. return 1;
  292. case '?':
  293. ostr[0] = options.opt();
  294. ostr[1] = SP_T('\0');
  295. message(CmdLineAppMessages::invalidOptionError,
  296. StringMessageArg(convertInput(ostr)));
  297. message(CmdLineAppMessages::usage,
  298. StringMessageArg(usageString()));
  299. return 1;
  300. default:
  301. processOption(opt, options.arg());
  302. break;
  303. }
  304. }
  305. nextArg = options.ind();
  306. if (errorFile_) {
  307. static filebuf file;
  308. if (!openFilebufWrite(file, errorFile_)) {
  309. message(CmdLineAppMessages::cannotOpenOutputError,
  310. StringMessageArg(convertInput(errorFile_)),
  311. ErrnoMessageArg(errno));
  312. return 1;
  313. }
  314. setMessageStream(new IosOutputCharStream(&file, codingSystem()));
  315. }
  316. if (!outputCodingSystem_)
  317. outputCodingSystem_ = codingSystem();
  318. return 0;
  319. }
  320. void CmdLineApp::processOption(AppChar opt, const AppChar *arg)
  321. {
  322. switch (opt) {
  323. case 'b':
  324. outputCodingSystem_ = lookupCodingSystem(arg);
  325. if (!outputCodingSystem_)
  326. message(CmdLineAppMessages::unknownBctf,
  327. StringMessageArg(convertInput(arg)));
  328. break;
  329. case 'f':
  330. errorFile_ = arg;
  331. break;
  332. case 'v':
  333. // print the version number
  334. message(CmdLineAppMessages::versionInfo,
  335. StringMessageArg(convertInput(versionString)));
  336. break;
  337. default:
  338. CANNOT_HAPPEN();
  339. }
  340. }
  341. Boolean CmdLineApp::getMessageText(const MessageFragment &frag,
  342. StringC &text)
  343. {
  344. String<SP_TCHAR> str;
  345. if (!MessageTable::instance()->getText(frag, str))
  346. return 0;
  347. #ifdef SP_WIDE_SYSTEM
  348. text.assign((const Char *)str.data(), str.size());
  349. #else
  350. str += 0;
  351. text = codingSystem()->convertIn(str.data());
  352. #endif
  353. return 1;
  354. }
  355. const CodingSystem *CmdLineApp::codingSystem()
  356. {
  357. if (!codingSystem_) {
  358. const SP_TCHAR *codingName = tgetenv(SP_T("SP_BCTF"));
  359. if (codingName)
  360. codingSystem_ = lookupCodingSystem(codingName);
  361. if (!codingSystem_
  362. #ifndef SP_WIDE_SYSTEM
  363. || codingSystem_->fixedBytesPerChar() > 1
  364. #endif
  365. )
  366. codingSystem_ = &identityCodingSystem;
  367. }
  368. return codingSystem_;
  369. }
  370. const CodingSystem *
  371. CmdLineApp::lookupCodingSystem(const SP_TCHAR *codingName)
  372. {
  373. #define MAX_CS_NAME 50
  374. if (tcslen(codingName) < MAX_CS_NAME) {
  375. char buf[MAX_CS_NAME];
  376. int i;
  377. for (i = 0; codingName[i] != SP_T('\0'); i++) {
  378. SP_TUCHAR c = totupper((SP_TUCHAR)(codingName[i]));
  379. #ifdef SP_WIDE_SYSTEM
  380. if (c > (unsigned char)-1)
  381. return 0;
  382. #endif
  383. buf[i] = char(c);
  384. }
  385. buf[i] = SP_T('\0');
  386. {
  387. for (size_t i = 0; i < SIZEOF(codingSystems); i++)
  388. if (strcmp(buf, codingSystems[i].name) == 0)
  389. return codingSystems[i].cs;
  390. }
  391. }
  392. return 0;
  393. }
  394. const CodingSystem *
  395. CmdLineApp::codingSystem(size_t i, const char *&name)
  396. {
  397. if (i < SIZEOF(codingSystems)) {
  398. name = codingSystems[i].name;
  399. return codingSystems[i].cs;
  400. }
  401. return 0;
  402. }
  403. StringC CmdLineApp::convertInput(const SP_TCHAR *s)
  404. {
  405. #ifdef SP_WIDE_SYSTEM
  406. StringC str(s, wcslen(s));
  407. #else
  408. StringC str(codingSystem()->convertIn(s));
  409. #endif
  410. for (size_t i = 0; i < str.size(); i++)
  411. if (str[i] == '\n')
  412. str[i] = '\r';
  413. return str;
  414. }
  415. OutputCharStream *CmdLineApp::makeStdErr()
  416. {
  417. OutputCharStream *os = ConsoleOutput::makeOutputCharStream(2);
  418. if (os)
  419. return os;
  420. return new IosOutputCharStream(cerr.rdbuf(), codingSystem());
  421. }
  422. OutputCharStream *CmdLineApp::makeStdOut()
  423. {
  424. OutputCharStream *os = ConsoleOutput::makeOutputCharStream(1);
  425. if (os)
  426. return os;
  427. return new IosOutputCharStream(cout.rdbuf(), outputCodingSystem_);
  428. }
  429. #ifdef SP_NAMESPACE
  430. }
  431. #endif