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