"Fossies" - the Fresh Open Source Software Archive

Member "OpenSP-1.5.2/lib/CmdLineApp.cxx" (21 Jul 2005, 15127 Bytes) of package /linux/misc/old/OpenSP-1.5.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CmdLineApp.cxx" see the Fossies "Dox" file reference documentation.

    1 // Copyright (c) 1996 James Clark, 1999 Matthias Clasen
    2 // See the file COPYING for copying permission.
    3 
    4 // Need option registration method that allows derived class to change
    5 // option names.
    6 
    7 #ifdef __GNUG__
    8 #pragma implementation
    9 #endif
   10 
   11 #include "splib.h"
   12 #include "CmdLineApp.h"
   13 #include "CmdLineAppMessages.h"
   14 #include "MessageArg.h"
   15 #include "ErrnoMessageArg.h"
   16 #include "Options.h"
   17 #include "xnew.h"
   18 #include "macros.h"
   19 #include "sptchar.h"
   20 #include "MessageTable.h"
   21 #include "CodingSystemKit.h"
   22 
   23 #include "ConsoleOutput.h"
   24 
   25 #include <errno.h>
   26 #include <string.h>
   27 #include <stdlib.h>
   28 #include <ctype.h>
   29 
   30 #ifdef SP_HAVE_LOCALE
   31 #include <locale.h>
   32 #endif
   33 #ifdef SP_HAVE_SETMODE
   34 #include <fcntl.h>
   35 #include <io.h>
   36 #endif
   37 
   38 #include <sys/types.h>
   39 #ifdef SP_INCLUDE_UNISTD_H
   40 #include <unistd.h>
   41 #endif
   42 #ifdef SP_INCLUDE_IO_H
   43 #include <io.h>
   44 #endif
   45 
   46 #ifdef _MSC_VER
   47 #include <crtdbg.h>
   48 #endif
   49 
   50 #ifndef SP_DEFAULT_ENCODING
   51 #ifdef WIN32
   52 #define SP_DEFAULT_ENCODING SP_T("WINDOWS")
   53 #else
   54 #define SP_DEFAULT_ENCODING  SP_T("IS8859-1")
   55 #endif
   56 #endif /* not SP_DEFAULT_ENCODING */
   57 
   58 #ifndef SP_MESSAGE_DOMAIN
   59 #define SP_MESSAGE_DOMAIN ""
   60 #endif /* not SP_MESSAGE_DOMAIN */
   61 
   62 #ifndef SP_LOCALE_DIR
   63 #define SP_LOCALE_DIR ""
   64 #endif /* not SP_LOCALE_DIR */
   65 
   66 #ifdef SP_NAMESPACE
   67 namespace SP_NAMESPACE {
   68 #endif
   69 
   70 static const SP_TCHAR *progName = 0;
   71 
   72 static FileOutputByteStream standardOutput(1, 0);
   73 static FileOutputByteStream standardError(2, 0);
   74 
   75 CmdLineApp::CmdLineApp(const char *requiredInternalCode)
   76 : errorFile_(0),
   77   outputCodingSystem_(0),
   78   SP_REPORTER_CLASS(0),
   79   internalCharsetIsDocCharset_(1),
   80   codingSystem_(0),
   81   action_(normalAction)
   82 {
   83   initCodingSystem(requiredInternalCode);
   84   setMessageStream(makeStdErr());
   85   if (internalCharsetIsDocCharset_) 
   86     registerOption('b', SP_T("bctf"), 
   87                    CmdLineAppMessages::name, CmdLineAppMessages::bHelp);
   88   else
   89     registerOption('b', SP_T("encoding"), 
   90                    CmdLineAppMessages::name, CmdLineAppMessages::eHelp);
   91   registerOption('f', SP_T("error-file"), 
   92                  CmdLineAppMessages::file, CmdLineAppMessages::fHelp);
   93   registerOption('v', SP_T("version"), CmdLineAppMessages::vHelp);
   94   registerOption('h', SP_T("help"), CmdLineAppMessages::hHelp);
   95   registerInfo(CmdLineAppMessages::usageStart, 1);
   96 }
   97 
   98 void CmdLineApp::resetCodingSystemKit()
   99 {
  100   codingSystemKit_ = codingSystemKit_->copy();
  101 }
  102 
  103 void CmdLineApp::registerOption(AppChar c, const AppChar *name,
  104                                 const MessageType1 &doc)
  105 {
  106   registerOption(c, name, CmdLineAppMessages::noArg, doc);
  107 }
  108 
  109 void CmdLineApp::changeOptionRegistration(AppChar oldc, AppChar newc)
  110 {
  111   for (size_t i = 0; i < opts_.size(); i++) {
  112     if (opts_[i].value == oldc) {
  113       opts_[i].value = newc;
  114 #ifdef SP_HAVE_LOCALE
  115       char *savedLocale = strdup(setlocale(LC_CTYPE, NULL));
  116       setlocale(LC_CTYPE, "C");
  117 #endif
  118       opts_[i].key = istalnum(newc) ? newc : 0;
  119 #ifdef SP_HAVE_LOCALE
  120       setlocale(LC_CTYPE, savedLocale);
  121       if (savedLocale)
  122         free(savedLocale);
  123 #endif
  124       return;
  125     }
  126   }
  127 }
  128 
  129 void CmdLineApp::registerOption(AppChar c, const AppChar *name,
  130                 const MessageFragment &arg,
  131                                 const MessageType1 &doc)
  132 {
  133   // these four are used for signals from Options<>::get()
  134   ASSERT((c != '-') && (c != ':') && (c != '?') && (c != '=')); 
  135   LongOption<AppChar> opt;
  136   opt.value = c;
  137 #ifdef SP_HAVE_LOCALE
  138   char *savedLocale = strdup(setlocale(LC_CTYPE, NULL));
  139   setlocale(LC_CTYPE, "C");
  140 #endif
  141   opt.key = istalnum(c) ? c : 0;
  142 #ifdef SP_HAVE_LOCALE
  143   setlocale(LC_CTYPE, savedLocale);
  144   if (savedLocale)
  145     free(savedLocale);
  146 #endif
  147   opt.name = name;
  148   opt.hasArgument = (arg.module() != CmdLineAppMessages::noArg.module()
  149                     || arg.number() != CmdLineAppMessages::noArg.number());
  150   for (size_t i = 0; i < opts_.size(); i++) 
  151     if (opts_[i].value == c) {
  152       for (; i + 1 < opts_.size(); i++) {
  153         opts_[i] = opts_[i + 1]; 
  154         optArgs_[i] = optArgs_[i + 1];
  155         optDocs_[i] = optDocs_[i + 1];
  156       }
  157       opts_[i] = opt;
  158       optArgs_[i] = arg; 
  159       optDocs_[i] = doc;
  160       return;
  161     }
  162   opts_.push_back(opt);
  163   optArgs_.push_back(arg);
  164   optDocs_.push_back(doc);
  165 }
  166 
  167 void CmdLineApp::registerUsage(const MessageType1 &u)
  168 {
  169   usages_.push_back(u);
  170 }
  171 
  172 void CmdLineApp::registerInfo(const MessageType1 &i, bool pre)
  173 {
  174   if (pre)
  175     preInfos_.push_back(i);
  176   else
  177     infos_.push_back(i);
  178 }
  179 
  180 // Backward compability. Will not display argName.
  181 void CmdLineApp::registerOption(AppChar c, const AppChar *argName)
  182 {
  183   if (argName)
  184     registerOption(c, 0, CmdLineAppMessages::someArg,
  185            CmdLineAppMessages::undocOption);
  186   else
  187     registerOption(c, 0, CmdLineAppMessages::undocOption);
  188 }
  189 
  190 void CmdLineApp::usage()
  191 {
  192   const OutputCharStream::Newline nl = OutputCharStream::newline;
  193   // We use the default encoding for the help message, since this is consistent
  194   // with error messages and probably is what users want.
  195   Owner<OutputCharStream> stdOut(ConsoleOutput::makeOutputCharStream(1));
  196   if (!stdOut)
  197     stdOut = new EncodeOutputCharStream(&standardOutput, codingSystem());
  198 
  199   Vector<CopyOwner<MessageArg> > args(1);
  200   StringMessageArg arg(convertInput(progName ? progName : SP_T("program")));
  201   args[0] = arg.copy();
  202   if (usages_.size() == 0) 
  203     usages_.push_back(CmdLineAppMessages::defaultUsage);
  204   for (size_t i = 0; i < usages_.size(); i++) {
  205     StrOutputCharStream ostr;
  206     StringC tem;
  207     formatMessage(usages_[i], args, ostr, 1);
  208     ostr.extractString(tem); 
  209     Vector<CopyOwner<MessageArg> > args2(1);
  210     StringMessageArg arg2(tem);
  211     args2[0] = arg2.copy();
  212     formatMessage(i ? CmdLineAppMessages::usageCont 
  213           : CmdLineAppMessages::usage,
  214           args2, *stdOut, 1);
  215     *stdOut << nl;
  216   } 
  217 
  218   for (size_t i = 0; i < preInfos_.size(); i++) {
  219     formatMessage(preInfos_[i], args, *stdOut, 1);
  220     *stdOut << nl;
  221   }
  222   Vector<StringC> leftSide;
  223   size_t leftSize = 0;
  224   for (size_t i = 0; i < opts_.size(); i++) {
  225     leftSide.resize(leftSide.size() + 1);
  226     StringC& s = leftSide.back();
  227     static const AppChar* space2 = SP_T("  ");
  228     s += convertInput(space2);
  229     if (opts_[i].key) {
  230       static const AppChar* dash = SP_T("-");
  231       s += convertInput(dash);
  232       AppChar buf[2];
  233       buf[0] = opts_[i].key;
  234       buf[1] = SP_T('\0');
  235       s += convertInput(buf);
  236       if (opts_[i].name) {
  237     static const AppChar* commaSpace = SP_T(", ");
  238     s += convertInput(commaSpace);
  239       }
  240       else if (opts_[i].hasArgument) {
  241     buf[0] = ' ';
  242     s += convertInput(buf);
  243       }
  244     }
  245     if (opts_[i].name) {
  246       static const AppChar* dash2 = SP_T("--");
  247       s += convertInput(dash2);
  248       s += convertInput(opts_[i].name);
  249       if (opts_[i].hasArgument) {
  250     static const AppChar* equal = SP_T("=");
  251     s += convertInput(equal);
  252       }
  253     }
  254     if (opts_[i].hasArgument) {
  255       StringC tem;
  256       getMessageText(optArgs_[i], tem);
  257       s += tem;
  258     }
  259     if (s.size() > leftSize)
  260       leftSize = s.size();
  261   }
  262   leftSize += 2;
  263   for (size_t i = 0; i < opts_.size(); i++) {
  264     for (size_t j = leftSide[i].size(); j <= leftSize; j++)
  265       leftSide[i] += ' ';
  266     StrOutputCharStream ostr;
  267     Vector<CopyOwner<MessageArg> > args2(1);
  268     StringC t;
  269     if (!getMessageText(optArgs_[i], t))
  270       t.resize(0);
  271     StringMessageArg arg(t);
  272     args2[0] = arg.copy(); 
  273     formatMessage(optDocs_[i], args2, ostr, 1);
  274     StringC tem;
  275     ostr.extractString(tem);
  276     *stdOut << leftSide[i] << tem << nl;
  277   }
  278   for (size_t i = 0; i < infos_.size(); i++) {
  279     formatMessage(infos_[i], args, *stdOut, 1);
  280     *stdOut << nl;
  281   }
  282 }
  283 
  284 static
  285 void ewrite(const char *s)
  286 {
  287   int n = (int)strlen(s);
  288   while (n > 0) {
  289     int nw = write(2, s, n);
  290     if (nw < 0)
  291       break;
  292     n -= nw;
  293     s += nw;
  294   }
  295 }
  296 
  297 static
  298 #ifdef SP_FANCY_NEW_HANDLER
  299 int outOfMemory(size_t)
  300 #else
  301 void outOfMemory()
  302 #endif
  303 {
  304   ewrite("SP library: out of memory\n");
  305   exit(1);
  306 #ifdef SP_FANCY_NEW_HANDLER
  307   return 0;
  308 #endif  
  309 }
  310 
  311 int CmdLineApp::init(int, AppChar **argv)
  312 {
  313 #ifndef SP_ANSI_LIB
  314   set_new_handler(outOfMemory);
  315 #endif
  316 #ifdef SP_HAVE_LOCALE
  317   setlocale(LC_ALL, "");
  318 #endif
  319 #ifdef SP_HAVE_SETMODE
  320   _setmode(1, _O_BINARY);
  321   _setmode(2, _O_BINARY);
  322 #endif
  323   progName = argv[0];
  324   if (progName)
  325     setProgramName(convertInput(progName));
  326   MessageTable::instance()->registerMessageDomain(libModule,
  327                                                   SP_MESSAGE_DOMAIN, 
  328                                                   SP_LOCALE_DIR);
  329   MessageTable::instance()->registerMessageDomain(appModule,
  330                                                   SP_MESSAGE_DOMAIN, 
  331                                                   SP_LOCALE_DIR);
  332   return 0;
  333 }
  334 
  335 int CmdLineApp::run(int argc, AppChar **argv)
  336 {
  337 #ifdef _MSC_VER
  338   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
  339 #endif
  340 #ifdef SP_ANSI_LIB
  341   try {
  342 #endif
  343   int ret = init(argc, argv);
  344   if (ret)
  345     return ret;
  346   int firstArg;
  347   ret = processOptions(argc, argv, firstArg);
  348   if (ret)
  349     return ret;
  350   // We do this here, so that the -b option works even if it is present after
  351   // the -h option.
  352   if (action_ == usageAction) {
  353     usage();
  354     return 0;
  355   }
  356   ret = processArguments(argc - firstArg, argv + firstArg);
  357   progName = 0;
  358   return ret;
  359 #ifdef SP_ANSI_LIB
  360   }
  361 catch (
  362 #ifndef SP_NO_STD_NAMESPACE
  363        std::
  364 #endif
  365         bad_alloc) {
  366 #ifdef SP_FANCY_NEW_HANDLER
  367     outOfMemory(0);
  368 #else
  369     outOfMemory();
  370 #endif
  371   }
  372   return 1;
  373 #endif /* SP_ANSI_LIB */
  374 }    
  375 
  376 int CmdLineApp::processOptions(int argc, AppChar **argv, int &nextArg)
  377 {
  378   AppChar ostr[80];
  379   Options<AppChar> options(argc, argv, opts_);
  380   AppChar opt;
  381   while (options.get(opt)) {
  382     switch (opt) {
  383     case '-':
  384     case '?':
  385     case '=':
  386     case ':':
  387       if (options.opt() == 0) {
  388         size_t i;
  389         const AppChar *t;
  390         for (i = 0, t = &argv[options.ind() - 1][2]; i < 79; i++, t++) {
  391           if  ((*t == '=') || (*t == '\0'))
  392             break;
  393           ostr[i] = *t;
  394         }
  395         ostr[i] = '\0';
  396       } 
  397       else {
  398         ostr[0] = options.opt();
  399         ostr[1] = SP_T('\0');
  400       }
  401       message((opt == '-') ? CmdLineAppMessages::ambiguousOptionError
  402                : ((opt == '=') ? CmdLineAppMessages::erroneousOptionArgError
  403                : ((opt == ':') ? CmdLineAppMessages::missingOptionArgError
  404                : CmdLineAppMessages::invalidOptionError)),
  405           StringMessageArg(convertInput(ostr)));
  406       message(CmdLineAppMessages::tryHelpOptionForInfo);
  407       return 1;
  408     default:
  409       processOption(opt, options.arg());
  410       break;
  411     }
  412   }
  413   nextArg = options.ind();
  414   if (errorFile_) {
  415     static FileOutputByteStream file;
  416     if (!file.open(errorFile_)) {
  417       message(CmdLineAppMessages::openFileError,
  418           StringMessageArg(convertInput(errorFile_)),
  419           ErrnoMessageArg(errno));
  420       return 1;
  421     }
  422     setMessageStream(new EncodeOutputCharStream(&file, codingSystem()));
  423   }
  424   if (!outputCodingSystem_)
  425     outputCodingSystem_ = codingSystem();
  426   return 0;
  427 }
  428 
  429 void CmdLineApp::processOption(AppChar opt, const AppChar *arg)
  430 {
  431   switch (opt) {
  432   case 'b':
  433     outputCodingSystem_ = lookupCodingSystem(arg);
  434     if (!outputCodingSystem_)
  435       message(internalCharsetIsDocCharset_
  436           ? CmdLineAppMessages::unknownBctf
  437           : CmdLineAppMessages::unknownEncoding,
  438           StringMessageArg(convertInput(arg)));
  439     break;
  440   case 'f':
  441     errorFile_ = arg;
  442     break;
  443   case 'v':
  444     // print the version number
  445     message(CmdLineAppMessages::versionInfo,
  446         StringMessageArg(codingSystem()->convertIn(SP_PACKAGE)),
  447         StringMessageArg(codingSystem()->convertIn(SP_VERSION)));
  448     break;
  449   case 'h':
  450     action_ = usageAction;
  451     break;
  452   default:
  453     CANNOT_HAPPEN();
  454   }
  455 }
  456 
  457 Boolean CmdLineApp::getMessageText(const MessageFragment &frag,
  458                    StringC &text)
  459 {
  460   String<SP_TCHAR> str;
  461   if (!MessageTable::instance()->getText(frag, str))
  462     return 0;
  463 #ifdef SP_WIDE_SYSTEM
  464   text.resize(str.size());
  465   for (size_t i = 0; i < str.size(); i++)
  466     text[i] = Char(str[i]);
  467 #else
  468   str += 0;
  469   text = codingSystem()->convertIn(str.data());
  470 #endif
  471   return 1;
  472 }
  473 
  474 Boolean CmdLineApp::stringMatches(const SP_TCHAR *s, const char *key)
  475 {
  476   for (; *key != '\0'; s++, key++) {
  477     if (*s != tolower(*key) && *s != toupper(*key))
  478       return 0;
  479   }
  480   return *s == '\0';
  481 }
  482 
  483 void CmdLineApp::initCodingSystem(const char *requiredInternalCode)
  484 {
  485   const char *name = requiredInternalCode;
  486 #ifdef SP_MULTI_BYTE
  487   char buf[256];
  488   if (!name) {
  489     const SP_TCHAR *internalCode = tgetenv(SP_T("SP_SYSTEM_CHARSET"));
  490     if (internalCode) {
  491       buf[255] = '\0';
  492       for (size_t i = 0; i < 255; i++) {
  493     buf[i] = internalCode[i];
  494     if (buf[i] == '\0')
  495       break;
  496       }
  497       name = buf;
  498     }
  499   }
  500   if (requiredInternalCode)
  501     internalCharsetIsDocCharset_ = 0;
  502   else {
  503     const SP_TCHAR *useInternal = tgetenv(SP_T("SP_CHARSET_FIXED"));
  504     if (useInternal
  505         && (stringMatches(useInternal, "YES")
  506         || stringMatches(useInternal, "1")))
  507       internalCharsetIsDocCharset_ = 0;
  508   }
  509 #endif /* SP_MULTI_BYTE */
  510   codingSystemKit_ = CodingSystemKit::make(name);
  511   const SP_TCHAR *codingName = tgetenv(internalCharsetIsDocCharset_
  512                        ? SP_T("SP_BCTF")
  513                        : SP_T("SP_ENCODING"));
  514   if (codingName)
  515     codingSystem_ = lookupCodingSystem(codingName);
  516 #ifdef SP_MULTI_BYTE
  517   if (!codingSystem_ && !internalCharsetIsDocCharset_)
  518     codingSystem_ = lookupCodingSystem(SP_DEFAULT_ENCODING);
  519 #endif
  520   if (!codingSystem_
  521 #ifndef SP_WIDE_SYSTEM
  522       || codingSystem_->fixedBytesPerChar() > 1
  523 #endif
  524     )
  525     codingSystem_ = codingSystemKit_->identityCodingSystem();
  526 }
  527 
  528 const CodingSystem *
  529 CmdLineApp::lookupCodingSystem(const AppChar *codingName)
  530 {
  531 #define MAX_CS_NAME 50
  532   if (tcslen(codingName) < MAX_CS_NAME) {
  533     char buf[MAX_CS_NAME];
  534     int i;
  535     for (i = 0; codingName[i] != SP_T('\0'); i++) {
  536       SP_TUCHAR c = codingName[i];
  537 #ifdef SP_WIDE_SYSTEM
  538       if (c > (unsigned char)-1)
  539     return 0;
  540 #endif
  541       buf[i] = char(c);
  542     }
  543     buf[i] = '\0';
  544     return codingSystemKit_->makeCodingSystem(buf, internalCharsetIsDocCharset_);
  545   }
  546   return 0;
  547 }
  548 
  549 StringC CmdLineApp::convertInput(const SP_TCHAR *s)
  550 {
  551 #ifdef SP_WIDE_SYSTEM
  552   StringC str;
  553   for (size_t i = 0; i < tcslen(s); i++)
  554     str += Char(s[i]);
  555 #else
  556   StringC str(codingSystem()->convertIn(s));
  557 #endif
  558   for (size_t i = 0; i < str.size(); i++)
  559     if (str[i] == '\n')
  560       str[i] = '\r';
  561   return str;
  562 }
  563 
  564 OutputCharStream *CmdLineApp::makeStdErr()
  565 {
  566   OutputCharStream *os = ConsoleOutput::makeOutputCharStream(2);
  567   if (os)
  568     return os;
  569   return new EncodeOutputCharStream(&standardError, codingSystem());
  570 }
  571 
  572 OutputCharStream *CmdLineApp::makeStdOut()
  573 {
  574   OutputCharStream *os = ConsoleOutput::makeOutputCharStream(1);
  575   if (os)
  576     return os;
  577   return new EncodeOutputCharStream(&standardOutput, outputCodingSystem_);
  578 }
  579 
  580 const MessageType2 &CmdLineApp::openFileErrorMessage()
  581 {
  582   return CmdLineAppMessages::openFileError;
  583 }
  584 
  585 const MessageType2 &CmdLineApp::closeFileErrorMessage()
  586 {
  587   return CmdLineAppMessages::closeFileError;
  588 }
  589 
  590 #ifdef SP_NAMESPACE
  591 }
  592 #endif