1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Copyright 1996 James Clark
8 * See the file COPYING for copying permission.
9 */
10
11 #pragma ident "%Z%%M% %I% %E% SMI"
12
13 // Need option registration method that allows derived class to change
14 // option names.
15
16 #ifdef __GNUG__
17 #pragma implementation
18 #endif
19
20 #include "splib.h"
21 #include "CmdLineApp.h"
22 #include "CmdLineAppMessages.h"
23 #include "MessageArg.h"
24 #include "ErrnoMessageArg.h"
25 #include "Options.h"
26 #include "version.h"
27 #include "xnew.h"
28 #include "macros.h"
29 #include "sptchar.h"
30 #include "MessageTable.h"
31 #include "CodingSystemKit.h"
32
33 #include "ConsoleOutput.h"
34
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39
40 #ifdef SP_HAVE_LOCALE
41 #include <locale.h>
42 #endif
43 #ifdef SP_HAVE_SETMODE
44 #include <fcntl.h>
45 #include <io.h>
46 #endif
47
48 #include <sys/types.h>
49 #ifdef SP_INCLUDE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef SP_INCLUDE_IO_H
53 #include <io.h>
54 #endif
55
56 #ifdef _MSC_VER
57 #include <crtdbg.h>
58 #endif
59
60 #ifndef SP_DEFAULT_ENCODING
61 #ifdef WIN32
62 #define SP_DEFAULT_ENCODING SP_T("WINDOWS")
63 #else
64 #define SP_DEFAULT_ENCODING SP_T("IS8859-1")
65 #endif
66 #endif /* not SP_DEFAULT_ENCODING */
67
68 #ifdef SP_NAMESPACE
69 namespace SP_NAMESPACE {
70 #endif
71
72 static const SP_TCHAR *progName = 0;
73
74 static const SP_TCHAR versionString[] = SP_VERSION;
75
76 static FileOutputByteStream standardOutput(1, 0);
77 static FileOutputByteStream standardError(2, 0);
78
CmdLineApp(const char * requiredInternalCode)79 CmdLineApp::CmdLineApp(const char *requiredInternalCode)
80 : errorFile_(0),
81 outputCodingSystem_(0),
82 // Colon at beginning is Posix.2ism that says to
83 // return : rather than ? for missing option argument.
84 optstr_(SP_T(":"), 1),
85 MessageReporter(0),
86 internalCharsetIsDocCharset_(1),
87 codingSystem_(0)
88 {
89 initCodingSystem(requiredInternalCode);
90 setMessageStream(makeStdErr());
91 registerOption('b', internalCharsetIsDocCharset_ ?
92 SP_T("bctf") : SP_T("encoding"));
93 registerOption('f', SP_T("error_file"));
94 registerOption('v');
95 }
96
resetCodingSystemKit()97 void CmdLineApp::resetCodingSystemKit()
98 {
99 codingSystemKit_ = codingSystemKit_->copy();
100 }
101
registerOption(AppChar c,const AppChar * argName)102 void CmdLineApp::registerOption(AppChar c, const AppChar *argName)
103 {
104 optstr_ += c;
105 if (argName) {
106 optstr_ += SP_T(':');
107 optArgNames_.push_back(argName);
108 }
109 }
110
usageString()111 StringC CmdLineApp::usageString()
112 {
113 String < AppChar > result;
114 if (progName)
115 result.assign(progName, tcslen(progName));
116 PackedBoolean hadOption[128];
117 for (int i = 0; i < 128; i++)
118 hadOption[i] = 0;
119 Boolean hadNoArgOption = 0;
120 for (size_t i = 1; i < optstr_.size(); i++) {
121 if (optstr_[i] == 0)
122 break;
123 if (i + 1 < optstr_.size() && optstr_[i + 1] == ':')
124 i++;
125 else
126 if (!hadOption[optstr_[i]]) {
127 hadOption[optstr_[i]] = 1;
128 if (!hadNoArgOption) {
129 hadNoArgOption = 1;
130 result.append(SP_T(" [-"), 3);
131 }
132 result += optstr_[i];
133 }
134 }
135 if (hadNoArgOption)
136 result += SP_T(']');
137 size_t j = 0;
138 for (size_t i = 1; i < optstr_.size(); i++) {
139 if (i + 1 < optstr_.size() && optstr_[i + 1] == ':') {
140 if (!hadOption[optstr_[i]]) {
141 hadOption[optstr_[i]] = 1;
142 result += SP_T(' ');
143 result += SP_T('[');
144 result += SP_T('-');
145 result += optstr_[i];
146 result += SP_T(' ');
147 result.append(optArgNames_[j],
148 tcslen(optArgNames_[j]));
149 result += SP_T(']');
150 }
151 i++;
152 j++;
153 }
154 }
155 result.append(SP_T(" sysid..."), tcslen(SP_T(" sysid...")));
156 result += 0;
157 return (convertInput(result.data()));
158 }
159
160 static void
ewrite(const char * s)161 ewrite(const char *s)
162 {
163 int n = (int)strlen(s);
164 while (n > 0) {
165 int nw = write(2, s, n);
166 if (nw < 0)
167 break;
168 n -= nw;
169 s += nw;
170 }
171 }
172
173 static
174 #ifdef SP_FANCY_NEW_HANDLER
175 int
outOfMemory(size_t)176 outOfMemory(size_t)
177 #else
178 void
179 outOfMemory()
180 #endif
181 {
182 ewrite("SP library: out of memory\n");
183 exit(1);
184 #ifdef SP_FANCY_NEW_HANDLER
185 return (0);
186 #endif
187 }
188
init(int,AppChar ** argv)189 int CmdLineApp::init(int, AppChar **argv)
190 {
191 #ifndef SP_ANSI_LIB
192 #ifdef __GNUC__
193 std::set_new_handler(outOfMemory);
194 #else
195 set_new_handler(outOfMemory);
196 #endif
197 #endif
198 #ifdef SP_HAVE_LOCALE
199 setlocale(LC_ALL, "");
200 #endif
201 #ifdef SP_HAVE_SETMODE
202 _setmode(1, _O_BINARY);
203 _setmode(2, _O_BINARY);
204 #endif
205 progName = argv[0];
206 if (progName)
207 setProgramName(convertInput(progName));
208 return (0);
209 }
210
run(int argc,AppChar ** argv)211 int CmdLineApp::run(int argc, AppChar **argv)
212 {
213 #ifdef _MSC_VER
214 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
215 #endif
216 #ifdef SP_ANSI_LIB
217 try {
218 #endif
219 int ret = init(argc, argv);
220 if (ret)
221 return (ret);
222 int firstArg;
223 ret = processOptions(argc, argv, firstArg);
224 if (ret)
225 return (ret);
226 ret = processArguments(argc - firstArg, argv + firstArg);
227 progName = 0;
228 return (ret);
229 #ifdef SP_ANSI_LIB
230 }
231 catch(
232 #ifndef SP_NO_STD_NAMESPACE
233 std::
234 #endif
235 bad_alloc) {
236 #ifdef SP_FANCY_NEW_HANDLER
237 outOfMemory(0);
238 #else
239 outOfMemory();
240 #endif
241 }
242 return (1);
243 #endif /* SP_ANSI_LIB */
244 }
245
processOptions(int argc,AppChar ** argv,int & nextArg)246 int CmdLineApp::processOptions(int argc, AppChar **argv, int &nextArg)
247 {
248 AppChar ostr[2];
249 optstr_ += SP_T('\0');
250 Options < AppChar > options(argc, argv, optstr_.data());
251 AppChar opt;
252 while (options.get(opt)) {
253 switch (opt) {
254 case ':':
255 ostr[0] = options.opt();
256 ostr[1] = SP_T('\0');
257 message(CmdLineAppMessages::
258 missingOptionArgError,
259 StringMessageArg(convertInput(ostr)));
260 message(CmdLineAppMessages::usage,
261 StringMessageArg(usageString()));
262 return (1);
263 case '?':
264 ostr[0] = options.opt();
265 ostr[1] = SP_T('\0');
266 message(CmdLineAppMessages::invalidOptionError,
267 StringMessageArg(convertInput(ostr)));
268 message(CmdLineAppMessages::usage,
269 StringMessageArg(usageString()));
270 return (1);
271 default:
272 processOption(opt, options.arg());
273 break;
274 }
275 }
276 nextArg = options.ind();
277 if (errorFile_) {
278 static FileOutputByteStream file;
279 if (!file.open(errorFile_)) {
280 message(CmdLineAppMessages::openFileError,
281 StringMessageArg(convertInput(errorFile_)),
282 ErrnoMessageArg(errno));
283 return (1);
284 }
285 setMessageStream(new EncodeOutputCharStream(&file,
286 codingSystem()));
287 }
288 if (!outputCodingSystem_)
289 outputCodingSystem_ = codingSystem();
290 return (0);
291 }
292
processOption(AppChar opt,const AppChar * arg)293 void CmdLineApp::processOption(AppChar opt, const AppChar *arg)
294 {
295 switch (opt) {
296 case 'b':
297 outputCodingSystem_ = lookupCodingSystem(arg);
298 if (!outputCodingSystem_)
299 message(internalCharsetIsDocCharset_
300 ? CmdLineAppMessages::unknownBctf
301 : CmdLineAppMessages::unknownEncoding,
302 StringMessageArg(convertInput(arg)));
303 break;
304 case 'f':
305 errorFile_ = arg;
306 break;
307 case 'v':
308 // print the version number
309 message(CmdLineAppMessages::versionInfo,
310 StringMessageArg(convertInput(versionString)));
311 break;
312 default:
313 CANNOT_HAPPEN();
314 }
315 }
316
getMessageText(const MessageFragment & frag,StringC & text)317 Boolean CmdLineApp::getMessageText(const MessageFragment &frag,
318 StringC &text)
319 {
320 String < SP_TCHAR > str;
321 if (!MessageTable::instance()->getText(frag, str))
322 return (0);
323 #ifdef SP_WIDE_SYSTEM
324 text.assign((const Char *)str.data(), str.size());
325 #else
326 str += 0;
327 text = codingSystem()->convertIn(str.data());
328 #endif
329 return (1);
330 }
331
stringMatches(const SP_TCHAR * s,const char * key)332 Boolean CmdLineApp::stringMatches(const SP_TCHAR *s, const char *key)
333 {
334 for (; *key != '\0'; s++, key++) {
335 if (*s != tolower(*key) && *s != toupper(*key))
336 return (0);
337 }
338 return (*s == '\0');
339 }
340
initCodingSystem(const char * requiredInternalCode)341 void CmdLineApp::initCodingSystem(const char *requiredInternalCode)
342 {
343 const char *name = requiredInternalCode;
344 #ifdef SP_MULTI_BYTE
345 char buf[256];
346 if (!name) {
347 const SP_TCHAR *internalCode =
348 tgetenv(SP_T("SP_SYSTEM_CHARSET"));
349 if (internalCode) {
350 buf[255] = '\0';
351 for (size_t i = 0; i < 255; i++) {
352 buf[i] = internalCode[i];
353 if (buf[i] == '\0')
354 break;
355 }
356 name = buf;
357 }
358 }
359 if (requiredInternalCode)
360 internalCharsetIsDocCharset_ = 0;
361 else {
362 const SP_TCHAR *useInternal = tgetenv(SP_T("SP_CHARSET_FIXED"));
363 if (useInternal &&
364 (stringMatches(useInternal, "YES") ||
365 stringMatches(useInternal, "1")))
366 internalCharsetIsDocCharset_ = 0;
367 }
368 #endif /* SP_MULTI_BYTE */
369 codingSystemKit_ = CodingSystemKit::make(name);
370 const SP_TCHAR *codingName = tgetenv(internalCharsetIsDocCharset_
371 ? SP_T("SP_BCTF")
372 : SP_T("SP_ENCODING"));
373 if (codingName)
374 codingSystem_ = lookupCodingSystem(codingName);
375 #ifdef SP_MULTI_BYTE
376 if (!codingSystem_ && !internalCharsetIsDocCharset_)
377 codingSystem_ = lookupCodingSystem(SP_DEFAULT_ENCODING);
378 #endif
379 if (!codingSystem_ ||
380 #ifndef SP_WIDE_SYSTEM
381 codingSystem_->fixedBytesPerChar() > 1
382 #endif
383 )
384 codingSystem_ = codingSystemKit_->identityCodingSystem();
385 }
386
387 const CodingSystem *
lookupCodingSystem(const AppChar * codingName)388 CmdLineApp::lookupCodingSystem(const AppChar *codingName)
389 {
390 #define MAX_CS_NAME 50
391 if (tcslen(codingName) < MAX_CS_NAME) {
392 char buf[MAX_CS_NAME];
393 int i;
394 for (i = 0; codingName[i] != SP_T('\0'); i++) {
395 SP_TUCHAR c = codingName[i];
396 #ifdef SP_WIDE_SYSTEM
397 if (c > (unsigned char)-1)
398 return (0);
399 #endif
400 buf[i] = char(c);
401 }
402 buf[i] = '\0';
403 return (codingSystemKit_->makeCodingSystem(buf,
404 internalCharsetIsDocCharset_));
405 }
406 return (0);
407 }
408
convertInput(const SP_TCHAR * s)409 StringC CmdLineApp::convertInput(const SP_TCHAR *s)
410 {
411 #ifdef SP_WIDE_SYSTEM
412 StringC str(s, wcslen(s));
413 #else
414 StringC str(codingSystem()->convertIn(s));
415 #endif
416 for (size_t i = 0; i < str.size(); i++)
417 if (str[i] == '\n')
418 str[i] = '\r';
419 return (str);
420 }
421
makeStdErr()422 OutputCharStream *CmdLineApp::makeStdErr()
423 {
424 OutputCharStream *os = ConsoleOutput::makeOutputCharStream(2);
425 if (os)
426 return (os);
427 return (new EncodeOutputCharStream(
428 &standardError, codingSystem()));
429 }
430
makeStdOut()431 OutputCharStream *CmdLineApp::makeStdOut()
432 {
433 OutputCharStream *os = ConsoleOutput::makeOutputCharStream(1);
434 if (os)
435 return (os);
436 return (new EncodeOutputCharStream(&standardOutput,
437 outputCodingSystem_));
438 }
439
openFileErrorMessage()440 const MessageType2 &CmdLineApp::openFileErrorMessage()
441 {
442 return (CmdLineAppMessages::openFileError);
443 }
444
closeFileErrorMessage()445 const MessageType2 &CmdLineApp::closeFileErrorMessage()
446 {
447 return (CmdLineAppMessages::closeFileError);
448 }
449
450 #ifdef SP_NAMESPACE
451 }
452 #endif
453