21 #include <libxml++/libxml++.h>
25 template<
typename InputIterator,
typename ValueType>
inline std::pair<
bool,InputIterator>
26 BestStringMatch(
const InputIterator &matches_begin,
const InputIterator &matches_end,
const ValueType &str) {
27 typedef std::map<
typename ValueType::size_type,std::vector<InputIterator> > comparison_sets_type;
28 comparison_sets_type comparison_sets;
30 for (InputIterator i(matches_begin);i!=matches_end;++i) {
31 const ValueType tmp(*i);
32 const typename comparison_sets_type::key_type edit_dist=tmp.compare(str);
34 if (edit_dist<tmp.size()) {
36 comparison_sets[edit_dist].push_back(i);
40 if (comparison_sets.begin()->first==0) {
41 assert(!comparison_sets.begin()->second.empty());
44 return std::make_pair(comparison_sets.begin()->second.size()>1,*comparison_sets.begin()->second.begin());
48 const typename comparison_sets_type::mapped_type &best_matches=(
typename comparison_sets_type::const_iterator(comparison_sets.end()))->second;
50 assert(!best_matches.empty());
51 return std::make_pair(best_matches.size()>1,*best_matches.begin());
89 template<
typename ProgOpts,
class Except_>
115 shared_ptr<Logger<exception>,exception> logger;
124 virtual bool __fastcall ProcessParam(
const unsigned long recognised_param,
const tchar *
const *
const argc,
unsigned long &i,
tostream &o)=0;
127 virtual bool __fastcall PostProcessParams(
tostream &) {
131 static tstring MakeLogLevelHelpStr(
void);
132 bool ProcessLogLevel(
const unsigned int,
const tchar *
const *
const,
unsigned long &,
tostream &);
133 void DumpUnknownArg(
const tchar *
const arg,
tostream &o)
const;
134 void DumpLogHelp(
tostream &o)
const;
135 void DumpVersion(
tostream &o)
const;
139 template<
typename ProgOpts,
class Except_>
155 typedef typename file<
tifstream, Except_::thread_traits::api_params_type::api_type,
typename Except_::thread_traits::model_type> config_file_type;
158 const ParamType config_param;
165 virtual bool __fastcall PostProcessConfigFileItems(
const xmlpp::NodeSet &,
tostream &) = 0;
167 bool __fastcall PostProcessParams(
tostream &);
168 xmlpp::NodeSet
__fastcall CheckConfigFile(xmlpp::DomParser &,tostream &)
const;
169 xmlpp::NodeSet
__fastcall CheckConfigFileValid(config_file_type &,xmlpp::DomParser &,tostream &)
const;
171 tstring __fastcall DefaultConfigFilePath(
void)
const;
177 version_param(
_T(
"--version"),
_T(
"Display the version & copyright information.")) {
180 template<
typename ProgOpts,
class Except_>
inline
184 assert(!help_param.param.empty() && !help_param.description.empty());
185 assert(!log_param.param.empty() && !log_param.description.empty());
186 assert(!version_param.param.empty() && !version_param.description.empty());
189 assert(contact_details);
193 assert(
sizeof(log_level_strs)/
sizeof(tchar *)==
static_cast<size_t>(log_info+1));
196 template<
typename ProgOpts,
class Except_>
inline bool
199 assert(argc && argc[0]);
202 logger.reset(
new Logger<exception>(
_T(
"/var/log/")+tstring(app_name).substr(tstring(app_name).rfind(
_T(
"/"))+1,tstring::npos)+
_T(
".log"),app_name));
203 for (
unsigned long i=1;i<argv;++i) {
209 return PostProcessParams(o);
212 template<
typename ProgOpts,
class Except_>
inline bool
216 std::vector<std::string> args(3+
sizeof(params)/
sizeof(ParamType));
217 args[0]=help_param.param;
218 args[1]=log_param.param;
219 args[2]=version_param.param;
220 for (std::vector<std::string>::size_type j=3;j<args.size();++j) {
221 args[j]=params[j-3].param;
223 const std::pair<
bool,std::vector<std::string>::const_iterator> result(BestStringMatch(args.begin(),args.end(),tstring(argc[i])));
224 JMMCG_TRACE(_T(
"Matching parameter: ")<<*result.second);
225 if (*result.second==help_param.param) {
227 }
else if (*result.second==log_param.param) {
228 if (!ProcessLogLevel(argv,argc,i,o)) {
232 }
else if (*result.second==version_param.param) {
234 }
else if (!ProcessParam(result.second-args.begin()-3,argc,i,o)) {
235 DumpUnknownArg(argc[i],o);
241 template<
typename ProgOpts,
class Except_>
inline void
244 const tchar options_str[]=
_T(
"[options]");
245 o<<
_T(
"Usage is:\n");
247 o<<
_T(
" ")<<options_str<<
_T(
"\nWhere ")<<options_str<<
_T(
" must unambiguously match one (or more) of the following strings:\n");
251 for (
unsigned long i=0;i<(
sizeof(params)/
sizeof(ParamType));++i) {
252 assert(!params[i].param.empty() && !params[i].description.empty());
253 o<<
_T(
"\t")<<params[i].param<<
_T(
"\t")<<params[i].description<<std::endl;
257 template<
typename ProgOpts,
class Except_>
inline tstring
259 const tchar param_str[]=
_T(
"level");
262 ss<<
_T(
" ")<<param_str<<
_T(
"\tSet the logging level to '")<<param_str<<
_T(
"'={");
263 for (
unsigned long i=0;i<penultimate;++i) {
266 ss<<
log_level_strs[penultimate]<<
_T(
"}, which must match unambiguously, the default is '")<<
log_level_strs[0]<<
_T(
"'; each level logs increasingly more information. The log file is: '");
270 template<
typename ProgOpts,
class Except_>
inline bool
272 if (++i<=argv && argc[i] && !tstring(argc[i]).empty()) {
273 const std::pair<
bool,
const tchar *
const *> result(BestStringMatch(&(log_level_strs[0]),&(log_level_strs[
sizeof(log_level_strs)/
sizeof(tchar*)]),tstring(argc[i])));
275 o<<
_T(
"Ambiguous match for supplied 'level' parameter: '")<<argc[i]<<
_T(
"'.\n");
279 logger->Level(
static_cast<log_level_type>(result.second-&(log_level_strs[0])));
283 o<<
_T(
"Unrecognised 'level' parameter: '")<<argc[i]<<
_T(
"' for ")<<log_param
.param<<
_T(
" option.\n");
287 o<<
_T(
"Missing 'level' parameter for ")<<log_param
.param<<
_T(
" option.\n");
292 template<
typename ProgOpts,
class Except_>
inline void
294 o<<
_T(
"Unrecognised command line parameter '")<<arg<<
_T(
"'.\n");
298 template<
typename ProgOpts,
class Except_>
inline void
300 o<<
_T(
"\t")<<log_param.param<<log_param.description<<logger->Path()<<
_T(
"'.\n");
303 template<
typename ProgOpts,
class Except_>
inline void
307 o<<
_T(
"Author(s):\n");
308 for (
unsigned long i=0;i<(
sizeof(authors)/
sizeof(tstring *));++i) {
310 o<<
_T(
"\t")<<authors[i]<<
_T(
"\n");
316 assert(contact_details);
322 template<
typename ProgOpts,
class Except_>
inline
324 :
CmdLineProcessorBase<
ProgramOptions,Except_>(p),
config_param(
_T(
"--config"),
_T(
" file_path\tSpecify the location (file_path) of the configuration file. The configuration file should be in XML format with a valid XDR schema (of the same name, in the same location, but with an '.xdr' extension). This schema referenced by the XML will be used as one of the tests to verify the appropriateness of the specified configuration file for this program. By default it is called: '")) {
327 template<
typename ProgOpts,
class Except_>
inline bool
329 const tstring::size_type hc=config_param.param.compare(argc[i]);
330 if (hc>=0 && hc<config_param.param.size()-2) {
331 if (++i<=argv && argc[i] && !tstring(argc[i]).empty()) {
332 config_file_path=argc[i];
335 o<<
_T(
"Missing 'file_path' parameter for ")<<config_param
.param<<
_T(
" option.\n");
344 template<
typename ProgOpts,
class Except_>
inline void
350 template<
typename ProgOpts,
class Except_>
inline bool
352 if (config_file_path.empty()) {
353 config_file_path=DefaultConfigFilePath();
355 xmlpp::DomParser parser;
356 const xmlpp::NodeSet data_items(CheckConfigFile(parser,o));
357 if (!data_items.empty()) {
359 JMMCG_TRACE(_T(
"Process ")<<tostring(data_items.size())+_T(
" node(s)..."));
360 JMMCG_LOG((ConfigFile<ProgOpts,Except_>::Log()),log_info,
_T(
"Process ")+tostring(data_items.size())+
_T(
" node(s)..."));
361 if (!PostProcessConfigFileItems(data_items,o)) {
362 o<<
_T(
"There was an error whilst processing the configuration file.\n");
373 template<
typename ProgOpts,
class Except_>
inline xmlpp::NodeSet
374 ConfigFile<ProgOpts,Except_>::CheckConfigFile(xmlpp::DomParser &parser,tostream &o)
const {
377 config_file_type config_file(config_file_path,
std::ios_base::in,
false);
378 return CheckConfigFileValid(config_file,parser,o);
379 }
catch (
const typename config_file_type::exception &e) {
381 o<<
_T(
"The configuration file '")<<config_file_path<<
_T(
"' was not found in the specified location.\n");
384 return xmlpp::NodeSet();
387 template<
typename ProgOpts,
class Except_>
inline xmlpp::NodeSet
388 ConfigFile<ProgOpts,Except_>::CheckConfigFileValid(config_file_type &config_file,xmlpp::DomParser &parser,tostream &o)
const {
391 #warning TODO JMG: Seems to only like DTD validation: parser.set_validate();
392 parser.parse_stream(config_file);
394 assert(
dynamic_cast<
const xmlpp::Element *>(parser.get_document()->get_root_node()));
395 const xmlpp::NodeSet data_items(parser.get_document()->get_root_node()->find(
_T(
"/Config/Data/*")));
397 if (data_items.empty()) {
398 o<<
_T(
"The configuration file '")<<config_file_path<<
_T(
"' is not valid for this program.\n");
402 }
catch (
const std::exception &e) {
403 o<<
_T(
"Error processing the configuration file: '")<<e.what()<<
_T(
"'\n");
405 return xmlpp::NodeSet();
408 template<
typename ProgOpts,
class Except_>
inline tstring
409 ConfigFile<ProgOpts,Except_>::DefaultConfigFilePath(
void)
const {
410 return _T(
"~/.")+tstring(ConfigFile<ProgOpts,Except_>::app_name).substr(tstring(ConfigFile<ProgOpts,Except_>::app_name).rfind(
_T(
"/"))+1,tstring::npos)+
_T(
"rc.xml");