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