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