/* * * Copyright (C) 1998-2021, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: ofstd * * Author: Joerg Riesmeier * * Purpose: Handle command line arguments (Source) * */ #include "dcmtk/config/osconfig.h" #include "dcmtk/ofstd/ofcmdln.h" #include "dcmtk/ofstd/ofstd.h" #include "dcmtk/ofstd/ofcast.h" #include "dcmtk/ofstd/ofchrenc.h" #if defined(HAVE_WINDOWS_H) && !defined(__MINGW32__) #define WIN32_LEAN_AND_MEAN #include #endif /*---------------------* * macro definitions * *---------------------*/ #define COMMAND_FILE_PREFIX '@' #ifdef DCMTK_USE_WCHAR_T #define WIDE_COMMAND_FILE_PREFIX L'@' #endif /*----------------------------* * constant initializations * *----------------------------*/ const int OFCommandLine::PF_ExpandWildcards = 0x0001; // not used anymore const int OFCommandLine::PF_NoCommandFiles = 0x0002; const int OFCommandLine::AF_Exclusive = 0x0001; const int OFCommandLine::AF_Internal = 0x0002; const int OFCommandLine::AF_NoWarning = 0x0004; /*-----------------------* * struct declarations * *-----------------------*/ /* Internal structure to store valid command line options. * Options are command line arguments to use optional functions and to specify optional properties of a program. * They are all starting with one or more special characters and can therefore be detected. */ struct OFCmdOption { /** constructor * ** @param longOpt long option name * @param shortOpt short option name * @param valueCount number of additional values * @param valueDescr description of optional values * @param optDescr description of command line option * @param flags flags (see AF_xxx) */ OFCmdOption(const char *longOpt, const char *shortOpt, const int valueCount, const char *valueDescr, const char *optDescr, const int flags) : LongOption(longOpt), ShortOption(shortOpt), ValueCount(valueCount), ValueDescription(valueDescr), OptionDescription(optDescr), Flags(flags), Checked(OFFalse) { } /** destructor */ ~OFCmdOption() { #ifdef DEBUG if (!Checked && !(Flags & (OFCommandLine::AF_Exclusive | OFCommandLine::AF_NoWarning)) && !LongOption.empty()) { ofConsole.lockCerr() << "WARNING: option " << LongOption << " has possibly never been checked !" << OFendl; ofConsole.unlockCerr(); } #endif } /// long option name const OFString LongOption; /// short option name const OFString ShortOption; /// number of additional values const int ValueCount; /// description of optional values const OFString ValueDescription; /// description of command line option const OFString OptionDescription; /// flags (see AF_xxx) const int Flags; /// OFTrue if findOption has been applied to this option OFBool Checked; private: /// private undefined copy assignment operator OFCmdOption& operator=(const OFCmdOption& arg); }; /* Internal structure to handle position of command line parameters. * Parameters are all command line arguments which are no options (e.g. file names). */ struct OFCmdParamPos { /** constructor * ** @param parIter iterator pointing to a specific parameter * @param optIter iterator pointing to first option iterator in front of the parameter * @param optCount number of options in front of the parameter * @param directOpt number of options which are direct predecessor in the argument list */ OFCmdParamPos(const OFListIterator(OFString) &parIter, const OFListIterator(OFListIterator_OFString) &optIter, const int optCount, const int directOpt) : ParamIter(parIter), OptionIter(optIter), OptionCount(optCount), DirectOption(directOpt) { } /// iterator pointing to a specific parameter const OFListIterator(OFString) ParamIter; /// iterator pointing to first option iterator in front of the parameter const OFListIterator(OFListIterator_OFString) OptionIter; /// number of options in front of the parameter const int OptionCount; /// number of options which are direct predecessor in the argument list const int DirectOption; private: /// private undefined copy assignment operator OFCmdParamPos& operator=(const OFCmdParamPos& arg); }; /*------------------* * implementation * *------------------*/ OFCommandLine::OFCommandLine() : ProgramName(), ValidOptionList(), ValidParamList(), ArgumentList(), ArgumentIterator(), ParamPosList(), ParamPosNumber(0), ParamPosIterator(), OptionPosList(), OptionPosIterator(), OptionBlockIterator(), OptionBlockMode(OFFalse), OptionChars("+-"), ExclusiveOption(OFFalse), WideCharMode(OFFalse), LongColumn(0), ShortColumn(0), ParamColumn(0), MinParamCount(0), MaxParamCount(0), LastParamMode(OFCmdParam::PM_Mandatory) { } OFCommandLine::~OFCommandLine() { OFListIterator(OFCmdOption *) first_o = ValidOptionList.begin(); const OFListIterator(OFCmdOption *) last_o = ValidOptionList.end(); while (first_o != last_o) { /* avoid wrong warning message */ if (ExclusiveOption) (*first_o)->Checked = OFTrue; /* delete object and remove from list */ delete (*first_o); first_o = ValidOptionList.erase(first_o); } OFListIterator(OFCmdParam *) first_p = ValidParamList.begin(); const OFListIterator(OFCmdParam *) last_p = ValidParamList.end(); while (first_p != last_p) { delete (*first_p); first_p = ValidParamList.erase(first_p); } OFListIterator(OFCmdParamPos *) first_pp = ParamPosList.begin(); const OFListIterator(OFCmdParamPos *) last_pp = ParamPosList.end(); while (first_pp != last_pp) { delete (*first_pp); first_pp = ParamPosList.erase(first_pp); } } void OFCommandLine::setOptionChars(const char *chars) { OptionChars = chars; } void OFCommandLine::setOptionColumns(const int longCols, const int shortCols) { LongColumn = longCols; ShortColumn = shortCols; } void OFCommandLine::setParamColumn(const int column) { ParamColumn = column; } OFBool OFCommandLine::checkOption(const OFString &option, const OFBool mode) const { OFBool result = mode; const size_t optionLen = option.length(); if (optionLen > 0) // empty strings are allowed to support (sub)groups { result = OFFalse; if (optionLen >= 2) { if (OptionChars.find(option.at(0)) != OFString_npos) // options have to start with one of the defined chars { if (((option.at(0) != '-') && (option.at(0) != '+')) || // but when starting with sign character ... (option.at(1) < '0') || (option.at(1) > '9')) // ... don't allow a number as the following character { result = OFTrue; } } } } return result; } void OFCommandLine::addGeneralOptions(const int longCols, const int shortCols) { addGroup("general options:", longCols, shortCols + 2); addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); } OFBool OFCommandLine::addOption(const char *longOpt, const char *shortOpt, const int valueCount, const char *valueDescr, const char *optDescr, const int flags) { if (checkOption(longOpt) && checkOption(shortOpt)) { #ifdef DEBUG if (strlen(longOpt) > 0) { OFListIterator(OFCmdOption *) iter = ValidOptionList.begin(); const OFListIterator(OFCmdOption *) last = ValidOptionList.end(); while (iter != last) { if ((*iter)->LongOption == longOpt) { ofConsole.lockCerr() << "WARNING: long option " << longOpt << " already defined ... not added !" << OFendl; ofConsole.unlockCerr(); return OFFalse; } if ((strlen(shortOpt) > 0) && ((*iter)->ShortOption == shortOpt)) { ofConsole.lockCerr() << "WARNING: short option " << shortOpt << " already defined for " << (*iter)->LongOption << " ..." << OFendl << " option " << longOpt << " not added !" << OFendl; ofConsole.unlockCerr(); return OFFalse; } ++iter; } } #endif OFCmdOption *opt = new OFCmdOption(longOpt, shortOpt, valueCount, valueDescr, optDescr, flags); if (opt != NULL) { ValidOptionList.push_back(opt); return OFTrue; } } #ifdef DEBUG ofConsole.lockCerr() << "WARNING: invalid option " << shortOpt << "/" < hides " << param << " !" << OFendl; ofConsole.unlockCerr(); } break; /* case OFCmdParam::PM_MultiMandatory: { ofConsole.lockCerr() << "WARNING: " << ValidParamList.size() << ". parameter is multi-mandatory => hides " << param << " !" << OFendl; ofConsole.unlockCerr(); } break; */ case OFCmdParam::PM_MultiOptional: { ofConsole.lockCerr() << "WARNING: " << ValidParamList.size() << ". parameter is multi-optional => hides " << param << " !" << OFendl; ofConsole.unlockCerr(); } break; default: break; } LastParamMode = mode; #endif OFCmdParam *par = new OFCmdParam(param, descr, mode); if (par != NULL) { ValidParamList.push_back(par); return OFTrue; } } return OFFalse; } OFBool OFCommandLine::addParam(const char *param, const OFCmdParam::E_ParamMode mode) { return addParam(param, "", mode); } OFBool OFCommandLine::gotoFirstArg() { ArgumentIterator = ArgumentList.begin(); return ArgumentIterator != ArgumentList.end(); } OFBool OFCommandLine::gotoNextArg() { if (ArgumentIterator != ArgumentList.end()) return ++ArgumentIterator != ArgumentList.end(); return OFFalse; } OFBool OFCommandLine::getCurrentArg(const char *&arg) { if (ArgumentIterator != ArgumentList.end()) return strlen(arg = (*ArgumentIterator).c_str()) > 0; return OFFalse; } OFBool OFCommandLine::getCurrentArg(OFCmdString &arg) { if (ArgumentIterator != ArgumentList.end()) return !(arg = *ArgumentIterator).empty(); return OFFalse; } OFBool OFCommandLine::getLastArg(OFString &arg) { if (!ArgumentList.empty()) return !(arg = ArgumentList.back()).empty(); return OFFalse; } OFBool OFCommandLine::findParam(const int pos) { OFListIterator(OFCmdParamPos *) iter; return findParam(pos, iter); } OFBool OFCommandLine::findParam(const int pos, OFListIterator(OFCmdParamPos *) &pos_iter) { if ((pos > 0) && (pos <= getParamCount())) { int counter; if ((ParamPosNumber > 0) && (pos >= ParamPosNumber)) // can we start from previous position? { counter = pos - ParamPosNumber + 1; pos_iter = ParamPosIterator; } else { // if not, start from beginning counter = pos; pos_iter = ParamPosList.begin(); } const OFListIterator(OFCmdParamPos *) pos_last = ParamPosList.end(); while (pos_iter != pos_last) { ArgumentIterator = (*pos_iter)->ParamIter; if (--counter == 0) { ParamPosNumber = pos; // if found, store current position ParamPosIterator = pos_iter; return OFTrue; } ++pos_iter; } } return OFFalse; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, OFCmdSignedInt &value) { if (findParam(pos)) { if (sscanf((*ArgumentIterator).c_str(), "%li", &value) == 1) return PVS_Normal; return PVS_Invalid; } return PVS_CantFind; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMin(const int pos, OFCmdSignedInt &value, const OFCmdSignedInt low, const OFExplicitBool incl) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if ((value < low) || (!incl && (value == low))) return PVS_Underflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMinMax(const int pos, OFCmdSignedInt &value, const OFCmdSignedInt low, const OFCmdSignedInt high) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if (value < low) return PVS_Underflow; else if (value > high) return PVS_Overflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, OFCmdUnsignedInt &value) { if (findParam(pos)) { if (sscanf((*ArgumentIterator).c_str(), "%lu", &value) == 1) return PVS_Normal; return PVS_Invalid; } return PVS_CantFind; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMin(const int pos, OFCmdUnsignedInt &value, const OFCmdUnsignedInt low, const OFExplicitBool incl) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if ((value < low) || (!incl && (value == low))) return PVS_Underflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMinMax(const int pos, OFCmdUnsignedInt &value, const OFCmdUnsignedInt low, const OFCmdUnsignedInt high) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if (value < low) return PVS_Underflow; else if (value > high) return PVS_Overflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, OFCmdFloat &value) { if (findParam(pos)) { OFBool success = OFFalse; value = OFStandard::atof((*ArgumentIterator).c_str(), &success); if (success) return PVS_Normal; return PVS_Invalid; } return PVS_CantFind; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMin(const int pos, OFCmdFloat &value, const OFCmdFloat low, const OFExplicitBool incl) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if ((value < low) || (!incl && (value == low))) return PVS_Underflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParamAndCheckMinMax(const int pos, OFCmdFloat &value, const OFCmdFloat low, const OFCmdFloat high) { E_ParamValueStatus status = getParam(pos, value); if (status == PVS_Normal) { if (value < low) return PVS_Underflow; else if (value > high) return PVS_Overflow; } return status; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, const char *&value) { if (findParam(pos)) { value = (*ArgumentIterator).c_str(); if (strlen(value) > 0) return PVS_Normal; return PVS_Empty; } return PVS_CantFind; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, OFCmdString &value) { if (findParam(pos)) { value = *ArgumentIterator; if (!value.empty()) return PVS_Normal; return PVS_Empty; } return PVS_CantFind; } OFCommandLine::E_ParamValueStatus OFCommandLine::getParam(const int pos, OFFilename &filename) { OFString value; E_ParamValueStatus status = getParam(pos, value); if (status != PVS_CantFind) { if (status != PVS_Empty) filename.set(value, WideCharMode /* convert */); else filename.clear(); } return status; } OFBool OFCommandLine::findOption(const char *longOpt, const signed int pos, const E_FindOptionMode mode) { #ifdef DEBUG OFListIterator(OFCmdOption *) iter = ValidOptionList.begin(); const OFListIterator(OFCmdOption *) last = ValidOptionList.end(); while (iter != last) { if ((*iter)->LongOption == longOpt) { (*iter)->Checked = OFTrue; break; } ++iter; } if (iter == last) { ofConsole.lockCerr() << "WARNING: unknown option " << longOpt << " in OFCommandLine::findOption() !" << OFendl; ofConsole.unlockCerr(); return OFFalse; } #endif // reverse direction (left to right) if ((mode == FOM_FirstFromLeft) || (mode == FOM_NextFromLeft)) { #ifdef DEBUG if (pos != 0) { ofConsole.lockCerr() << "WARNING: OFCommandLine::findOption() parameter 'pos' (" << pos << ") ignored !" << OFendl; ofConsole.unlockCerr(); } #endif OFListIterator(OFListIterator_OFString) pos_iter = OptionPosList.begin(); const OFListIterator(OFListIterator_OFString) pos_end = OptionPosList.end(); if (mode == FOM_NextFromLeft) pos_iter = (OptionPosIterator == pos_end) ? pos_end : ++OptionPosIterator; while (pos_iter != pos_end) { ArgumentIterator = *pos_iter; if (*ArgumentIterator == longOpt) // searched option { OptionPosIterator = pos_iter; // store option position return OFTrue; } pos_iter++; } } else { // normal direction (right to left) OFListIterator(OFListIterator_OFString) pos_iter = (mode == FOM_Next) ? OptionPosIterator : OptionPosList.end(); const OFListIterator(OFListIterator_OFString) pos_first = OptionPosList.begin(); OFListIterator(OFCmdParamPos *) param_iter; int diropt = 0; if (findParam(abs(pos), param_iter)) // go to specified parameter position { diropt = (*param_iter)->DirectOption; // number of direct predecessors if (((*param_iter)->OptionCount == 0) || // no options in front of specified parameter or ((pos < 0) && (diropt == 0))) // no 'direct' option ... return OFFalse; pos_iter = (*param_iter)->OptionIter; // first option in front of parameter ++pos_iter; // goto next to facilitate loop condition } while (pos_iter != pos_first) { ArgumentIterator = *(--pos_iter); if (OptionBlockMode && (pos_iter == OptionBlockIterator)) // new option is in front of alternative block option return OFFalse; else if (*ArgumentIterator == longOpt) // searched option { OptionPosIterator = pos_iter; // store option position if (mode == FOM_Normal) OptionBlockIterator = pos_iter; return OFTrue; } else if ((pos < 0) && (--diropt <= 0)) // search only for the direct predecessor return OFFalse; } } return OFFalse; } OFBool OFCommandLine::gotoFirstOption() { OptionPosIterator = OptionPosList.begin(); if (OptionPosIterator != OptionPosList.end()) { ArgumentIterator = *OptionPosIterator; return OFTrue; } return OFFalse; } OFBool OFCommandLine::gotoNextOption() { if (OptionPosIterator != OptionPosList.end()) { if (++OptionPosIterator != OptionPosList.end()) { ArgumentIterator = *OptionPosIterator; return OFTrue; } } return OFFalse; } OFBool OFCommandLine::getCurrentOption(const char *&opt) { if (OptionPosIterator != OptionPosList.end()) return (opt = (**OptionPosIterator).c_str())[0] != '\0'; return OFFalse; } OFBool OFCommandLine::getCurrentOption(OFCmdString &opt) { if (OptionPosIterator != OptionPosList.end()) return !(opt = **OptionPosIterator).empty(); return OFFalse; } void OFCommandLine::beginOptionBlock() { OptionBlockIterator = OptionPosList.end(); OptionBlockMode = OFTrue; } void OFCommandLine::endOptionBlock() { OptionBlockMode = OFFalse; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(OFCmdSignedInt &value) { if (++ArgumentIterator != ArgumentList.end()) { if (sscanf((*ArgumentIterator).c_str(), "%li", &value) == 1) return VS_Normal; return VS_Invalid; } return VS_NoMore; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMin(OFCmdSignedInt &value, const OFCmdSignedInt low, const OFExplicitBool incl) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if ((value < low) || (!incl && (value == low))) return VS_Underflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMinMax(OFCmdSignedInt &value, const OFCmdSignedInt low, const OFCmdSignedInt high) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if (value < low) return VS_Underflow; else if (value > high) return VS_Overflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(OFCmdUnsignedInt &value) { if (++ArgumentIterator != ArgumentList.end()) { OFString &strVal = *ArgumentIterator; if (sscanf(strVal.c_str(), "%lu", &value) == 1) { // skip leading spaces size_t strPos = strVal.find_first_not_of(' '); // check for minus sign (negative number) if ((strPos != OFString_npos) && (strVal.at(strPos) != '-')) return VS_Normal; } return VS_Invalid; } return VS_NoMore; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMin(OFCmdUnsignedInt &value, const OFCmdUnsignedInt low, const OFExplicitBool incl) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if ((value < low) || (!incl && (value == low))) return VS_Underflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMinMax(OFCmdUnsignedInt &value, const OFCmdUnsignedInt low, const OFCmdUnsignedInt high) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if (value < low) return VS_Underflow; else if (value > high) return VS_Overflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(OFCmdFloat &value) { if (++ArgumentIterator != ArgumentList.end()) { OFBool success = OFFalse; value = OFStandard::atof((*ArgumentIterator).c_str(), &success); if (success) return VS_Normal; return VS_Invalid; } return VS_NoMore; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMin(OFCmdFloat &value, const OFCmdFloat low, const OFExplicitBool incl) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if ((value < low) || (!incl && (value == low))) return VS_Underflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValueAndCheckMinMax(OFCmdFloat &value, const OFCmdFloat low, const OFCmdFloat high) { E_ValueStatus status = getValue(value); if (status == VS_Normal) { if (value < low) return VS_Underflow; else if (value > high) return VS_Overflow; } return status; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(const char *&value) { if (++ArgumentIterator != ArgumentList.end()) { value = (*ArgumentIterator).c_str(); if (strlen(value) > 0) return VS_Normal; return VS_Empty; } return VS_NoMore; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(OFCmdString &value) { if (++ArgumentIterator != ArgumentList.end()) { value = *ArgumentIterator; if (!value.empty()) return VS_Normal; return VS_Empty; } return VS_NoMore; } OFCommandLine::E_ValueStatus OFCommandLine::getValue(OFFilename &filename) { if (++ArgumentIterator != ArgumentList.end()) { OFString strValue = *ArgumentIterator; if (!strValue.empty()) { filename.set(strValue, WideCharMode /* convert */); return VS_Normal; } else filename.clear(); return VS_Empty; } return VS_NoMore; } const OFCmdOption *OFCommandLine::findCmdOption(const OFString &option) const { OFListConstIterator(OFCmdOption *) iter = ValidOptionList.begin(); OFListConstIterator(OFCmdOption *) last = ValidOptionList.end(); while (iter != last) { if (((*iter)->LongOption == option) || ((*iter)->ShortOption == option)) return *iter; ++iter; } return NULL; } void OFCommandLine::storeParameter(const OFString ¶m, const int directOpt) { ArgumentList.push_back(param); const OFListIterator(OFListIterator_OFString) iter = (OptionPosList.size() == 0) ? OptionPosList.end() : --OptionPosList.end(); OFCmdParamPos *paramPos = new OFCmdParamPos(--ArgumentList.end(), iter, OFstatic_cast(int, OptionPosList.size()), directOpt); if (paramPos != NULL) ParamPosList.push_back(paramPos); } int OFCommandLine::packColumnValues(int longCols, int shortCols) const { if (longCols < 0) longCols = 0; if (shortCols < 0) shortCols = 0; return ((longCols & 0xffff) << 16) | (shortCols & 0xffff); } void OFCommandLine::unpackColumnValues(const int value, unsigned int &longCols, unsigned int &shortCols) const { longCols = (value == 0) ? LongColumn : (value >> 16) & 0xffff; shortCols = (value == 0) ? ShortColumn : (value & 0xffff); } OFCommandLine::E_ParseStatus OFCommandLine::checkParamCount() { MinParamCount = 0; MaxParamCount = 0; OFListIterator(OFCmdParam *) iter = ValidParamList.begin(); const OFListIterator(OFCmdParam *) last = ValidParamList.end(); while (iter != last) { if (!(*iter)->ParamName.empty()) { switch ((*iter)->ParamMode) { case OFCmdParam::PM_Mandatory: MinParamCount++; if (MaxParamCount >= 0) MaxParamCount++; break; case OFCmdParam::PM_MultiMandatory: MinParamCount++; MaxParamCount = -1; break; case OFCmdParam::PM_Optional: if (MaxParamCount >= 0) MaxParamCount++; break; case OFCmdParam::PM_MultiOptional: MaxParamCount = -1; break; } } ++iter; } if (getArgCount() == 0) return PS_NoArguments; else if (hasExclusiveOption()) return PS_ExclusiveOption; else if (getParamCount() < MinParamCount) return PS_MissingParameter; else if ((MaxParamCount >= 0) && (getParamCount() > MaxParamCount)) return PS_TooManyParameters; return PS_Normal; } OFCommandLine::E_ParseStatus OFCommandLine::parseCommandFile(const char *argValue, OFList &argList) { E_ParseStatus result = PS_NoArguments; /* check for command file parameter (syntax: "@filename") */ if ((argValue != NULL) && (argValue[0] == COMMAND_FILE_PREFIX) && (argValue[1] != '\0')) { /* skip '@' symbol in filename */ const char *filename = argValue + 1; /* open command file */ STD_NAMESPACE ifstream cmdFile(filename, OFopenmode_in_nocreate); if (cmdFile) { char c, block = 0; OFString value; /* append command file content to the list of arguments */ while (cmdFile.get(c)) { /* double or single quote */ if ((c == '"') || (c == '\'')) { if (block == c) { /* closing current quote block (also empty value) */ argList.push_back(value); value.clear(); block = 0; } else if (block == 0) { /* starting new quote block */ block = c; } else { /* append character */ value += c; } } /* space, tab, newline or return */ else if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) { /* outside block quote? */ if (block == 0) { if (!value.empty()) { /* and add current value to the list of arguments */ argList.push_back(value); value.clear(); } } else value += c; } else value += c; } /* append remaining argument (if any) */ if (!value.empty()) argList.push_back(value); #ifdef DEBUG if (block != 0) { ofConsole.lockCerr() << "WARNING: closing quotation mark (" << block << ") missing in command file " << filename << OFendl; ofConsole.unlockCerr(); } #endif result = PS_Normal; } else result = PS_CannotOpenCommandFile; } return result; } #ifdef DCMTK_USE_WCHAR_T // Windows-specific version with wide character strings (UTF-16) #ifdef DEBUG OFCommandLine::E_ParseStatus OFCommandLine::parseCommandFile(const wchar_t *argValue, const OFString &strValue, OFList &argList) #else OFCommandLine::E_ParseStatus OFCommandLine::parseCommandFile(const wchar_t *argValue, const OFString & /* strValue */, OFList &argList) #endif { E_ParseStatus result = PS_NoArguments; /* check for command file parameter (syntax: "@filename") */ if ((argValue != NULL) && (argValue[0] == WIDE_COMMAND_FILE_PREFIX) && (argValue[1] != L'\0')) { /* skip '@' symbol in filename */ const wchar_t *filename = argValue + 1; /* open command file */ STD_NAMESPACE wifstream cmdFile(filename, OFopenmode_in_nocreate); if (cmdFile) { wchar_t c, block = 0; STD_NAMESPACE wstring value; /* append command file content to the list of arguments */ while (cmdFile.get(c)) { /* double or single quote */ if ((c == L'"') || (c == L'\'')) { if (block == c) { /* closing current quote block (also empty value) */ OFString tmpString; /* convert current value to UTF-8 */ OFCharacterEncoding::convertFromWideCharString(value.c_str(), value.length(), tmpString, OFCharacterEncoding::CPC_UTF8); /* ... and add it to the list of arguments */ argList.push_back(tmpString); value.clear(); block = 0; } else if (block == 0) { /* starting new quote block */ block = c; } else { /* append character */ value += c; } } /* space, tab, newline or return */ else if ((c == L' ') || (c == L'\t') || (c == L'\n') || (c == L'\r')) { /* outside block quote? */ if (block == 0) { if (!value.empty()) { OFString tmpString; /* convert current value to UTF-8 */ OFCharacterEncoding::convertFromWideCharString(value.c_str(), value.length(), tmpString, OFCharacterEncoding::CPC_UTF8); /* ... and add it to the list of arguments */ argList.push_back(tmpString); value.clear(); } } else value += c; } else value += c; } /* append remaining argument (if any) */ if (!value.empty()) { OFString tmpString; /* convert current value to UTF-8 */ OFCharacterEncoding::convertFromWideCharString(value.c_str(), value.length(), tmpString, OFCharacterEncoding::CPC_UTF8); /* ... and add it to the list of arguments */ argList.push_back(tmpString); } #ifdef DEBUG if (block != 0) { ofConsole.lockCerr() << "WARNING: closing quotation mark (" << block << ") missing in command file " << strValue << OFendl; ofConsole.unlockCerr(); } #endif result = PS_Normal; } else result = PS_CannotOpenCommandFile; } return result; } #endif // DCMTK_USE_WCHAR_T OFCommandLine::E_ParseStatus OFCommandLine::parseArgumentList(OFList &argList, const int /*flags*/) { ArgumentList.clear(); // initialize lists/positions ParamPosList.clear(); OptionPosList.clear(); ParamPosNumber = 0; ExclusiveOption = OFFalse; int directOption = 0; // number of direct predecessor int i = OFstatic_cast(int, argList.size()); OFListIterator(OFString) argIter = argList.begin(); const OFListIterator(OFString) argEnd = argList.end(); /* iterate over all command line arguments */ while (argIter != argEnd) { if (!checkOption(*argIter, OFFalse)) // arg = parameter { storeParameter(*argIter, directOption); directOption = 0; } else { // arg = option const OFCmdOption *opt = findCmdOption(*argIter); if (opt != NULL) { ArgumentList.push_back(OFstatic_cast(OFString, opt->LongOption)); // convert argument to long format OptionPosList.push_back(--ArgumentList.end()); if (opt->Flags & AF_Exclusive) // check for an "exclusive" option ExclusiveOption = OFTrue; directOption++; int j = opt->ValueCount; // number of expected values if (j >= i) return PS_MissingValue; // expecting more values than present while (j-- > 0) { ArgumentList.push_back(*(++argIter)); // add values to argument list i--; } } else { // unknown ArgumentList.push_back(*argIter); // store argument return PS_UnknownOption; } } ++argIter; i--; } return checkParamCount(); } OFCommandLine::E_ParseStatus OFCommandLine::parseLine(int argCount, char *argValue[], const int flags, const int startPos) { OFList argList; // "expanded" list of arguments WideCharMode = OFFalse; if (argCount > 0) // determine program name ProgramName = argValue[0]; else ProgramName.clear(); if (argCount > startPos) // any command line arguments? { /* expand command files (if any) */ for (int i = startPos; i < argCount; i++) // skip program name (argValue[0]) { if (flags & PF_NoCommandFiles) // do not try to detect command files argList.push_back(argValue[i]); else { /* parse command file content */ E_ParseStatus status = parseCommandFile(argValue[i], argList); if (status == PS_NoArguments) argList.push_back(argValue[i]); // store parameter as is else if (status != PS_Normal) { ArgumentList.push_back(argValue[i] + 1); // store filename for error message return status; } } } } /* call the real function after the preparatory work is done */ return parseArgumentList(argList, flags); } #ifdef DCMTK_USE_WCHAR_T // Windows-specific version with wide character strings (UTF-16) OFCommandLine::E_ParseStatus OFCommandLine::parseLine(int argCount, wchar_t *argValue[], const int flags, const int startPos) { OFList argList; // "expanded" list of arguments WideCharMode = OFTrue; if (argCount > 0) // determine program name (convert to UTF-8) { OFCharacterEncoding::convertFromWideCharString(argValue[0], wcslen(argValue[0]), ProgramName, OFCharacterEncoding::CPC_UTF8); } else ProgramName.clear(); if (argCount > startPos) // any command line arguments? { OFString strValue; /* expand command files (if any) */ for (int i = startPos; i < argCount; i++) // skip program name (argValue[0]) { OFCharacterEncoding::convertFromWideCharString(argValue[i], wcslen(argValue[i]), strValue, OFCharacterEncoding::CPC_UTF8, OFTrue /* clearMode */); if (flags & PF_NoCommandFiles) // do not try to detect command files argList.push_back(strValue); else { /* parse command file content */ E_ParseStatus status = parseCommandFile(argValue[i], strValue, argList); if (status == PS_NoArguments) argList.push_back(strValue); // store parameter as is else if (status != PS_Normal) { ArgumentList.push_back(strValue.c_str() + 1); // store filename for error message return status; } } } } /* call the real function after the preparatory work is done */ return parseArgumentList(argList, flags); } #endif // DCMTK_USE_WCHAR_T void OFCommandLine::getSyntaxString(OFString &syntaxStr) const { syntaxStr.clear(); if (!ValidOptionList.empty()) syntaxStr += " [options]"; if (!ValidParamList.empty()) { OFListConstIterator(OFCmdParam *) iter = ValidParamList.begin(); OFListConstIterator(OFCmdParam *) last = ValidParamList.end(); while (iter != last) { if (!(*iter)->ParamName.empty()) { switch ((*iter)->ParamMode) { case OFCmdParam::PM_Mandatory: syntaxStr += " "; syntaxStr += (*iter)->ParamName; break; case OFCmdParam::PM_Optional: syntaxStr += " ["; syntaxStr += (*iter)->ParamName; syntaxStr += "]"; break; case OFCmdParam::PM_MultiMandatory: syntaxStr += " "; syntaxStr += (*iter)->ParamName; syntaxStr += "..."; break; case OFCmdParam::PM_MultiOptional: syntaxStr += " ["; syntaxStr += (*iter)->ParamName; syntaxStr += "...]"; break; } } ++iter; } } } void OFCommandLine::getOptionString(OFString &optionStr) const { optionStr.clear(); if (!ValidOptionList.empty()) { OFListConstIterator(OFCmdOption *) iter = ValidOptionList.begin(); OFListConstIterator(OFCmdOption *) last = ValidOptionList.end(); OFString str; int newGrp = 1; unsigned int shortSize = ShortColumn; unsigned int longSize = LongColumn; unsigned int lineIndent = 0; const unsigned int groupIndent = 2; const unsigned int subGrpIndent = 4; const unsigned int columnSpace = 2; while (iter != last) { /* skip internal options */ if (!((*iter)->Flags & OFCommandLine::AF_Internal)) { if (newGrp) { /* determine column width per group */ OFListConstIterator(OFCmdOption *) i = iter; while ((i != last) && !(*i)->LongOption.empty()) { if (!((*i)->Flags & OFCommandLine::AF_Internal)) { if ((*i)->ShortOption.length() > shortSize) shortSize = OFstatic_cast(unsigned int, (*i)->ShortOption.length()); if ((*i)->LongOption.length() > longSize) longSize = OFstatic_cast(unsigned int, (*i)->LongOption.length()); } i++; } newGrp = 0; } if ((*iter)->LongOption.empty()) { /* group entry */ newGrp = 1; unpackColumnValues((*iter)->ValueCount, longSize, shortSize); if (!(*iter)->OptionDescription.empty()) // new group { optionStr += (*iter)->OptionDescription; lineIndent = groupIndent; } else { // new sub group optionStr.append(groupIndent, ' '); optionStr += (*iter)->ValueDescription; lineIndent = subGrpIndent; } optionStr += "\n"; } else { /* regular option */ optionStr.append(lineIndent, ' '); if (shortSize > 0) { str = (*iter)->ShortOption; str.resize(shortSize, ' '); optionStr += str; optionStr.append(columnSpace, ' '); } str = (*iter)->LongOption; str.resize(longSize, ' '); optionStr += str; optionStr.append(columnSpace, ' '); if (!(*iter)->ValueDescription.empty()) { optionStr += (*iter)->ValueDescription; optionStr += "\n"; optionStr.append(lineIndent + shortSize + longSize + columnSpace, ' '); if (shortSize > 0) optionStr.append(columnSpace, ' '); } str = (*iter)->OptionDescription; size_t pos = 0; while (((pos = (str.find('\n', pos))) != OFString_npos) && (pos < str.length())) str.insert(++pos, OFString(lineIndent + shortSize + longSize + 2 * columnSpace, ' ')); optionStr += str; optionStr += "\n"; } } ++iter; } } } void OFCommandLine::getParamString(OFString ¶mStr) const { paramStr.clear(); if (!ValidParamList.empty()) { OFListConstIterator(OFCmdParam *) iter = ValidParamList.begin(); OFListConstIterator(OFCmdParam *) last = ValidParamList.end(); OFString str; unsigned int columnSize = ParamColumn; const unsigned int lineIndent = 2; const unsigned int columnSpace = 2; while ((iter != last) && !(*iter)->ParamDescription.empty()) { if ((*iter)->ParamName.length() > columnSize) // determine maximum column width columnSize = OFstatic_cast(unsigned int, (*iter)->ParamName.length()); ++iter; } iter = ValidParamList.begin(); // reset iterator while (iter != last) { if (!(*iter)->ParamDescription.empty()) { if (paramStr.empty()) paramStr += "parameters:\n"; paramStr.append(lineIndent, ' '); str = (*iter)->ParamName; str.resize(columnSize, ' '); paramStr += str; paramStr.append(columnSpace, ' '); str = (*iter)->ParamDescription; size_t pos = 0; while (((pos = (str.find('\n', pos))) != OFString_npos) && (pos < str.length())) str.insert(++pos, OFString(lineIndent + columnSize + columnSpace, ' ')); paramStr += str; paramStr += "\n"; } ++iter; } } } OFBool OFCommandLine::getMissingParam(OFString ¶m) { if (!ValidParamList.empty() && (getParamCount() < MinParamCount)) { OFListIterator(OFCmdParam *) iter = ValidParamList.begin(); const OFListIterator(OFCmdParam *) last = ValidParamList.end(); int i = getParamCount(); while ((iter != last) && (i-- > 0)) ++iter; if (iter != last) { param = (*iter)->ParamName; return OFTrue; } } return OFFalse; } void OFCommandLine::getStatusString(const E_ParseStatus status, OFString &statusStr) { OFString str; switch (status) { case PS_UnknownOption: statusStr = "Unknown option "; if (getLastArg(str)) statusStr += str; break; case PS_MissingValue: statusStr = "Missing value for option "; if (getLastArg(str)) statusStr += str; break; case PS_MissingParameter: statusStr = "Missing parameter "; if (getMissingParam(str)) statusStr += str; break; case PS_TooManyParameters: statusStr = "Too many parameters"; break; case PS_CannotOpenCommandFile: statusStr = "Cannot open command file"; if (getLastArg(str)) { statusStr += " '"; statusStr += str; statusStr += "'"; } break; default: statusStr.clear(); break; } } void OFCommandLine::getStatusString(const E_ParamValueStatus status, OFString &statusStr) { OFString str; switch (status) { case PVS_Invalid: statusStr = "Invalid parameter value "; if (getCurrentArg(str)) statusStr += str; break; case PVS_CantFind: statusStr = "Can't find parameter"; break; case PVS_Underflow: statusStr = "Invalid parameter value "; if (getCurrentArg(str)) { statusStr += str; statusStr += " (underflow)"; } break; case PVS_Overflow: statusStr = "Invalid parameter value "; if (getCurrentArg(str)) { statusStr += str; statusStr += " (overflow)"; } break; default: statusStr.clear(); break; } } void OFCommandLine::getStatusString(const E_ValueStatus status, OFString &statusStr) { OFString str; switch (status) { case VS_Normal: statusStr.clear(); break; case VS_Invalid: statusStr = "Invalid value for option "; if (getCurrentOption(str)) { statusStr += str; if (getCurrentArg(str)) { statusStr += " ("; statusStr += str; statusStr += ")"; } } break; case VS_Underflow: statusStr = "Invalid value for option "; if (getCurrentOption(str)) { statusStr += str; if (getCurrentArg(str)) { statusStr += " (underflow: "; statusStr += str; statusStr += ")"; } } break; case VS_Overflow: statusStr = "Invalid value for option "; if (getCurrentOption(str)) { statusStr += str; if (getCurrentArg(str)) { statusStr += " (overflow: "; statusStr += str; statusStr += ")"; } } break; default: statusStr.clear(); break; } }