PEP 55 Update license on source files to current license text and date
[tpot/pegasus/.git] / src / Pegasus / Common / OptionManager.cpp
1 //%2003////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2000, 2001, 2002  BMC Software, Hewlett-Packard Development
4 // Company, L. P., IBM Corp., The Open Group, Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L. P.;
6 // IBM Corp.; EMC Corporation, The Open Group.
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 // 
15 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
16 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
17 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
18 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
19 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //==============================================================================
25 //\r
26 // Author: Mike Brasher (mbrasher@bmc.com)\r
27 //\r
28 // Modified By: Karl Schopmeyer(k.schopmeyer@opengroup.org)\r
29 //                  June 2001 - Extend help and print to include help description\r
30 //                  Feb 2002 - ad IsTrue\r
31 //\r
32 //%/////////////////////////////////////////////////////////////////////////////\r
33 \r
34 #include <cstdlib>\r
35 #include <cctype>\r
36 #include <fstream>\r
37 #include <cstdio>\r
38 #include "OptionManager.h"\r
39 #include "FileSystem.h"\r
40 #include "Destroyer.h"\r
41 \r
42 PEGASUS_USING_STD;\r
43 \r
44 PEGASUS_NAMESPACE_BEGIN\r
45 \r
46 ////////////////////////////////////////////////////////////////////////////////\r
47 //\r
48 // TODO: expand variables in the configuration file. For example:\r
49 //\r
50 //     provider_dir = "${home}/providers"\r
51 //\r
52 ////////////////////////////////////////////////////////////////////////////////\r
53 \r
54 ////////////////////////////////////////////////////////////////////////////////\r
55 //\r
56 // Option\r
57 //\r
58 ////////////////////////////////////////////////////////////////////////////////\r
59 \r
60 const Array<String>& Option::getDomain() const\r
61 {\r
62     return _domain;\r
63 }\r
64 \r
65 void Option::setDomain(const Array<String>& domain)\r
66 {\r
67     _domain = domain;\r
68 }\r
69 \r
70 ////////////////////////////////////////////////////////////////////////////////\r
71 //\r
72 // OptionManager\r
73 //\r
74 ////////////////////////////////////////////////////////////////////////////////\r
75 \r
76 OptionManager::OptionManager()\r
77 {\r
78 \r
79 }\r
80 \r
81 OptionManager::~OptionManager()\r
82 {\r
83     // Delete all options in the list:\r
84 \r
85     for (Uint32 i = 0; i < _options.size(); i++)\r
86         delete _options[i];\r
87 }\r
88 \r
89 void OptionManager::registerOption(Option* option) \r
90         //throw(NullPointer, OMDuplicateOption)\r
91 {\r
92     if (!option)\r
93         throw NullPointer();\r
94 \r
95     if (lookupOption(option->getOptionName()))\r
96         throw OMDuplicateOption(option->getOptionName());\r
97 \r
98     _options.append(option);\r
99 }\r
100 \r
101 \r
102 void OptionManager::registerOptions(OptionRow* optionRow, Uint32 numOptions)\r
103     throw (NullPointer)\r
104 {\r
105     for (Uint32 i = 0; i < numOptions; i++)\r
106     {\r
107         // Get option name:\r
108 \r
109         if (!optionRow[i].optionName)\r
110             throw NullPointer();\r
111 \r
112         String optionName = optionRow[i].optionName;\r
113 \r
114         // Get default value:\r
115 \r
116         String defaultValue;\r
117 \r
118         if (optionRow[i].defaultValue)\r
119             defaultValue = optionRow[i].defaultValue;\r
120 \r
121         // Get the required flag:\r
122 \r
123         Boolean required = optionRow[i].required != 0;\r
124 \r
125         // Get the type:\r
126 \r
127         Option::Type type = optionRow[i].type;\r
128 \r
129         // Get the domain:\r
130 \r
131         Array<String> domain;\r
132 \r
133         if (optionRow[i].domain)\r
134         {\r
135             Uint32 domainSize = optionRow[i].domainSize;\r
136 \r
137             for (Uint32 j = 0; j < domainSize; j++)\r
138                 domain.append(optionRow[i].domain[j]);\r
139         }\r
140 \r
141         // Get commandLineOptionName:\r
142 \r
143         String commandLineOptionName;\r
144 \r
145         if (optionRow[i].commandLineOptionName)\r
146             commandLineOptionName = optionRow[i].commandLineOptionName;\r
147 \r
148         // get optionHelp Message String\r
149 \r
150         String optionHelpMessage;\r
151 \r
152         if (optionRow[i].optionHelpMessage)\r
153             optionHelpMessage = optionRow[i].optionHelpMessage;\r
154 \r
155         // Add the option:\r
156 \r
157         Option* option = new Option(\r
158             optionName,\r
159             defaultValue,\r
160             required,\r
161             type,\r
162             domain,\r
163             commandLineOptionName,\r
164             optionHelpMessage);\r
165 \r
166         registerOption(option);\r
167     }\r
168 }\r
169 \r
170 void OptionManager::mergeCommandLine(int& argc, char**& argv, Boolean abortOnErr)\r
171 {\r
172     for (int i = 0; i < argc; )\r
173     {\r
174         // Check for -option:\r
175 \r
176         const char* arg = argv[i];\r
177 \r
178         if (*arg == '-')\r
179         {\r
180             // Look for the option:\r
181 \r
182             Option* option = _lookupOptionByCommandLineOptionName(arg + 1);\r
183 \r
184             if (!option)\r
185             {\r
186                 if (abortOnErr)\r
187                 {\r
188                     throw OMMBadCmdLineOption(arg);    \r
189                 }\r
190                 else\r
191                 {\r
192                     i++;\r
193                     continue;\r
194                 }\r
195             }\r
196 \r
197             // Get the option argument if any:\r
198 \r
199             const char* optionArgument = "true";\r
200 \r
201             if (option->getType() != Option::BOOLEAN)\r
202             {\r
203                 if (i + 1 == argc)\r
204                     throw OMMissingCommandLineOptionArgument(arg);\r
205 \r
206                 optionArgument = argv[i + 1];\r
207             }\r
208 \r
209             // Validate the value:\r
210 \r
211             if (!option->isValid(optionArgument))\r
212                 throw OMInvalidOptionValue(arg, optionArgument);\r
213 \r
214             // Set the value:\r
215 \r
216             option->setValue(optionArgument);\r
217 \r
218             // Remove the option and its argument from the command line:\r
219 \r
220             if (option->getType() == Option::BOOLEAN)\r
221             {\r
222                 memmove(&argv[i], &argv[i + 1], (argc-i) * sizeof(char*));\r
223                 argc--;\r
224             }\r
225             else\r
226             {\r
227                 memmove(&argv[i], &argv[i + 2], (argc-i-1) * sizeof(char*));\r
228                 argc -= 2;\r
229             }\r
230         }\r
231         else\r
232             i++;\r
233     }\r
234 }\r
235 \r
236 void OptionManager::mergeFile(const String& fileName)\r
237 {\r
238    // Open the input file:\r
239 #if defined(PEGASUS_OS_OS400)\r
240     ifstream is(fileName.getCStringUTF8(),PEGASUS_STD(_CCSID_T(1208)));\r
241 #else\r
242     ifstream is(fileName.getCStringUTF8());\r
243 #endif\r
244 \r
245     if (!is)\r
246         throw NoSuchFile(fileName);\r
247 \r
248     // For each line of the file:\r
249 \r
250     String line;\r
251 \r
252     for (Uint32 lineNumber = 1; GetLine(is, line); lineNumber++)\r
253     {\r
254         // -- Get the identifier and value:\r
255 \r
256         if (line[0] == '#')\r
257             continue;\r
258 \r
259         // Skip leading whitespace:\r
260 \r
261         const Char16* p = line.getChar16Data();\r
262 \r
263         while (*p && isspace(*p))\r
264             p++;\r
265 \r
266         if (!*p)\r
267             continue;\r
268 \r
269         if (*p == '#')\r
270             continue;\r
271 \r
272         // Get the identifier:\r
273 \r
274         String ident;\r
275 \r
276         if (!(isalpha(*p) || *p == '_'))\r
277             throw OMConfigFileSyntaxError(fileName, lineNumber);\r
278 \r
279         ident.append(*p++);\r
280 \r
281         while (isalnum(*p) || *p == '_')\r
282             ident.append(*p++);\r
283 \r
284         // Skip whitespace after identifier:\r
285 \r
286         while (*p && isspace(*p))\r
287             p++;\r
288 \r
289         // Expect an equal sign:\r
290 \r
291         if (*p != '=')\r
292             throw OMConfigFileSyntaxError(fileName, lineNumber);\r
293         p++;\r
294 \r
295         // Skip whitespace after equal sign:\r
296 \r
297         while (*p && isspace(*p))\r
298             p++;\r
299 \r
300         // Expect open quote:\r
301 \r
302         if (*p != '"')\r
303             throw OMConfigFileSyntaxError(fileName, lineNumber);\r
304         p++;\r
305 \r
306         // Get the value:\r
307 \r
308         String value;\r
309 \r
310         while (*p && *p != '"')\r
311         {\r
312             if (*p == '\\')\r
313             {\r
314                 p++;\r
315 \r
316                 switch (*p)\r
317                 {\r
318                     case 'n': \r
319                         value.append('\n'); \r
320                         break;\r
321                     \r
322                     case 'r':\r
323                         value.append('\r');\r
324                         break;\r
325 \r
326                     case 't':\r
327                         value.append('\t');\r
328                         break;\r
329 \r
330                     case 'f':\r
331                         value.append('\f');\r
332                         break;\r
333 \r
334                     case '"':\r
335                         value.append('"');\r
336                         break;\r
337 \r
338                     case '\0':\r
339                         throw OMConfigFileSyntaxError(fileName, lineNumber);\r
340 \r
341                     default:\r
342                         value.append(*p);\r
343                 }\r
344                 p++;\r
345             }\r
346             else\r
347                 value.append(*p++);\r
348         }\r
349 \r
350 \r
351         // Expect close quote:\r
352 \r
353         if (*p != '"')\r
354             throw OMConfigFileSyntaxError(fileName, lineNumber);\r
355         p++;\r
356 \r
357         // Skip whitespace through end of line:\r
358 \r
359         while (*p && isspace(*p))\r
360             p++;\r
361 \r
362         if (*p)\r
363             throw OMConfigFileSyntaxError(fileName, lineNumber);\r
364 \r
365         // Now that we have the identifier and value, merge it:\r
366 \r
367         Option* option = (Option*)lookupOption(ident);\r
368 \r
369         if (!option)\r
370             throw OMUnrecognizedConfigFileOption(ident);\r
371 \r
372         if (!option->isValid(value))\r
373             throw OMInvalidOptionValue(ident, value);\r
374 \r
375         option->setValue(value);\r
376     }\r
377 }\r
378 \r
379 void OptionManager::checkRequiredOptions() const\r
380 {\r
381     for (Uint32 i = 0; i < _options.size(); i++)\r
382     {\r
383         const Option* option = _options[i];\r
384 \r
385         if (option->getRequired() && !option->isResolved())\r
386             throw OMMissingRequiredOptionValue(option->getOptionName());\r
387     }\r
388 }\r
389 \r
390 const Option* OptionManager::lookupOption(const String& name) const\r
391 {\r
392     for (Uint32 i = 0; i < _options.size(); i++)\r
393     {\r
394         if (_options[i]->getOptionName() == name)\r
395             return _options[i];\r
396     }\r
397 \r
398     return 0;\r
399 }\r
400 \r
401 Boolean OptionManager::lookupValue(const String& name, String& value) const\r
402 {\r
403     const Option* option = lookupOption(name);\r
404 \r
405     if (!option)\r
406         return false;\r
407 \r
408     value = option->getValue();\r
409     return true;\r
410 }\r
411 \r
412 Boolean OptionManager::lookupIntegerValue(const String& name, Uint32& value) const\r
413 {\r
414     //ATTN: KS P1 7 May 2002 - Add test for Integer type in om table.\r
415     String valueString;\r
416     if (lookupValue(name, valueString))\r
417     {\r
418         value = atol(valueString.getCString());\r
419         return true;\r
420     }\r
421     else\r
422     {\r
423         return false;\r
424     }\r
425 \r
426 }\r
427 \r
428 Boolean OptionManager::valueEquals(const String& name, const String& value) \r
429     const\r
430 {\r
431     String optionString;\r
432 \r
433     return (lookupValue(name, optionString) && optionString == value) ? true : false;\r
434 }\r
435 \r
436 Boolean OptionManager::isTrue(const String& name) const\r
437 {\r
438     //ATTN: KS 7 May 2002 P3 Add test to confirm boolean type \r
439     return valueEquals(name, "true") ? true: false;\r
440 }\r
441 /*  ATTN: P3 MB 2001 Buried this one for the moment to think about it.\r
442 Uint32 OptionManager::isStringInOptionMask(const String& option, \r
443                                            const String& entry) \r
444 {\r
445     String optionString;\r
446 \r
447     if (lookupValue(name, optionString) && optionString == value)\r
448         if (optionString.find(entry)\r
449             return 1;\r
450     else\r
451         return PEG_NOT_FOUND;\r
452 }\r
453 */\r
454 \r
455 Option* OptionManager::_lookupOptionByCommandLineOptionName(const String& name)\r
456 {\r
457     for (Uint32 i = 0; i < _options.size(); i++)\r
458     {\r
459         if (_options[i]->getCommandLineOptionName() == name)\r
460             return _options[i];\r
461     }\r
462 \r
463     return 0;\r
464 }\r
465 \r
466 void OptionManager::print() const\r
467 {\r
468     for (Uint32 i = 0; i < _options.size(); i++)\r
469     {\r
470         Option* option = _options[i];\r
471         cout << option->getOptionName() << "=\"";\r
472         cout << option->getValue() << "\" ";\r
473         cout << option->getOptionHelpMessage() << "\n";\r
474     }\r
475     cout << endl;\r
476 }\r
477 \r
478 void OptionManager::printOptionsHelp() const\r
479 {\r
480     for (Uint32 i = 0; i < _options.size(); i++)\r
481     {\r
482         Option* option = _options[i];\r
483         cout << " -";\r
484         cout << option->getCommandLineOptionName() << "  ";\r
485         cout << option->getOptionName() << ". ";\r
486         cout << option->getOptionHelpMessage();\r
487         cout << ". Default(" << option->getDefaultValue() << ")\n";\r
488     }\r
489     cout << endl;\r
490 \r
491 }\r
492 \r
493 void OptionManager::printOptionsHelpTxt(const String& header, const String& trailer) const\r
494 {\r
495     cout << "\n" << header << "\n";\r
496     printOptionsHelp();\r
497     cout << trailer << "\n";\r
498 }\r
499 \r
500 ////////////////////////////////////////////////////////////////////////////////\r
501 //\r
502 // Option\r
503 //\r
504 ////////////////////////////////////////////////////////////////////////////////\r
505 \r
506 Option::Option(\r
507     const String& optionName,\r
508     const String& defaultValue,\r
509     Boolean required,\r
510     Type type,\r
511     const Array<String>& domain,\r
512     const String& commandLineOptionName,\r
513     const String& optionHelpMessage)\r
514     :\r
515     _optionName(optionName),\r
516     _defaultValue(defaultValue),\r
517     _value(defaultValue),\r
518     _required(required),\r
519     _type(type),\r
520     _domain(domain),\r
521     _commandLineOptionName(commandLineOptionName),\r
522     _optionHelpMessage(optionHelpMessage),\r
523     _resolved(false)\r
524 {\r
525     if (!isValid(_value))\r
526         throw OMInvalidOptionValue(_optionName, _value);\r
527 }\r
528 \r
529 Option::Option(const Option& x) \r
530     :\r
531     _optionName(x._optionName),\r
532     _defaultValue(x._defaultValue),\r
533     _value(x._value),\r
534     _required(x._required),\r
535     _type(x._type),\r
536     _domain(x._domain),\r
537     _commandLineOptionName(x._commandLineOptionName),\r
538     _optionHelpMessage(x._optionHelpMessage)\r
539 \r
540 {\r
541 }\r
542 \r
543 Option::~Option()\r
544 {\r
545 \r
546 }\r
547 \r
548 Option& Option::operator=(const Option& x)\r
549 {\r
550     if (this != &x)\r
551     {\r
552         _optionName = x._optionName;\r
553         _defaultValue = x._defaultValue;\r
554         _value = x._value;\r
555         _required = x._required;\r
556         _type = x._type;\r
557         _domain = x._domain;\r
558         _commandLineOptionName = x._commandLineOptionName;\r
559         _optionHelpMessage = x._optionHelpMessage;\r
560     }\r
561     return *this;\r
562 }\r
563 \r
564 Boolean Option::isValid(const String& value) const\r
565 {\r
566     // Check to see that the value is in the domain (if a domain was given)\r
567 \r
568     Uint32 domainSize = _domain.size();\r
569 \r
570     if (domainSize)\r
571     {\r
572         Boolean found = false;\r
573 \r
574         for (Uint32 i = 0; i < domainSize; i++)\r
575         {\r
576             if (value == _domain[i])\r
577                 found = true;\r
578         }\r
579 \r
580         if (!found)\r
581             return false;\r
582     }\r
583 \r
584     // Check the type:\r
585 \r
586     switch (_type)\r
587     {\r
588         case BOOLEAN:\r
589         {\r
590             if (value == "true" || value == "false")\r
591                 return true;\r
592         }\r
593 \r
594         case STRING:\r
595             return true;\r
596 \r
597         case INTEGER:\r
598         case NATURAL_NUMBER:\r
599         case WHOLE_NUMBER:\r
600         {\r
601             CString tmp = value.getCString();\r
602             char* end = 0;\r
603             long x = strtol(tmp, &end, 10);\r
604 \r
605             if (!end || *end != '\0')\r
606                 return false;\r
607 \r
608             switch (_type)\r
609             {\r
610                 case INTEGER: \r
611                     return true;\r
612 \r
613                 case NATURAL_NUMBER:\r
614                     return x >= 1;\r
615 \r
616                 case WHOLE_NUMBER:\r
617                     return x >= 0;\r
618 \r
619                 default:\r
620                     break;\r
621             }\r
622         }\r
623     }\r
624 \r
625     // Unreachable!\r
626     return true;\r
627 }\r
628 \r
629 ////////////////////////////////////////////////////////////////////////////////\r
630 //\r
631 // ConfigFileSyntaxError\r
632 //\r
633 ////////////////////////////////////////////////////////////////////////////////\r
634 \r
635 String OMConfigFileSyntaxError::_formatMessage(\r
636     const String& file, Uint32 line)\r
637 {\r
638     char buffer[32];\r
639     sprintf(buffer, "%d", line);\r
640          //l10n\r
641          MessageLoaderParms parms("Common.OptionManager.SYNTAX_ERR_CONFIG_FILE",\r
642                                                                          "Syntax error in configuration file: ");\r
643          String result = MessageLoader::getMessage(parms);\r
644     //String result = "Syntax error in configuration file: ";\r
645     result.append(file);\r
646     result.append("(");\r
647     result.append(buffer);\r
648     result.append(")");\r
649     return result;\r
650 }\r
651 \r
652 PEGASUS_NAMESPACE_END\r