xref: /onnv-gate/usr/src/cmd/man/src/util/nsgmls.src/lib/CmdLineApp.cxx (revision 425:fbaa857e997e)
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