1 #include "precomp.hpp"
2 #include <sstream>
3 
4 namespace cv
5 {
6 
7 struct CommandLineParserParams
8 {
9 public:
10     String help_message;
11     String def_value;
12     std::vector<String> keys;
13     int number;
14 };
15 
16 
17 struct CommandLineParser::Impl
18 {
19     bool error;
20     String error_message;
21     String about_message;
22 
23     String path_to_app;
24     String app_name;
25 
26     std::vector<CommandLineParserParams> data;
27 
28     std::vector<String> split_range_string(const String& str, char fs, char ss) const;
29     std::vector<String> split_string(const String& str, char symbol = ' ', bool create_empty_item = false) const;
30     String cat_string(const String& str) const;
31 
32     void apply_params(const String& key, const String& value);
33     void apply_params(int i, String value);
34 
35     void sort_params();
36     int refcount;
37 };
38 
39 
get_type_name(int type)40 static String get_type_name(int type)
41 {
42     if( type == Param::INT )
43         return "int";
44     if( type == Param::BOOLEAN )
45         return "bool";
46     if( type == Param::UNSIGNED_INT )
47         return "unsigned";
48     if( type == Param::UINT64 )
49         return "unsigned long long";
50     if( type == Param::FLOAT )
51         return "float";
52     if( type == Param::REAL )
53         return "double";
54     if( type == Param::STRING )
55         return "string";
56     return "unknown";
57 }
58 
from_str(const String & str,int type,void * dst)59 static void from_str(const String& str, int type, void* dst)
60 {
61     std::stringstream ss(str.c_str());
62     if( type == Param::INT )
63         ss >> *(int*)dst;
64     else if( type == Param::BOOLEAN )
65     {
66         std::string temp;
67         ss >> temp;
68         *(bool*) dst = temp == "true";
69     }
70     else if( type == Param::UNSIGNED_INT )
71         ss >> *(unsigned*)dst;
72     else if( type == Param::UINT64 )
73         ss >> *(uint64*)dst;
74     else if( type == Param::FLOAT )
75         ss >> *(float*)dst;
76     else if( type == Param::REAL )
77         ss >> *(double*)dst;
78     else if( type == Param::STRING )
79         *(String*)dst = str;
80     else
81         throw cv::Exception(CV_StsBadArg, "unknown/unsupported parameter type", "", __FILE__, __LINE__);
82 
83     if (ss.fail())
84     {
85         String err_msg = "can not convert: [" + str +
86         + "] to [" + get_type_name(type) + "]";
87 
88         throw cv::Exception(CV_StsBadArg, err_msg, "", __FILE__, __LINE__);
89     }
90 }
91 
getByName(const String & name,bool space_delete,int type,void * dst) const92 void CommandLineParser::getByName(const String& name, bool space_delete, int type, void* dst) const
93 {
94     try
95     {
96         for (size_t i = 0; i < impl->data.size(); i++)
97         {
98             for (size_t j = 0; j < impl->data[i].keys.size(); j++)
99             {
100                 if (name.compare(impl->data[i].keys[j]) == 0)
101                 {
102                     String v = impl->data[i].def_value;
103                     if (space_delete)
104                         v = impl->cat_string(v);
105                     from_str(v, type, dst);
106                     return;
107                 }
108             }
109         }
110         impl->error = true;
111         impl->error_message = impl->error_message + "Unknown parameter " + name + "\n";
112     }
113     catch (std::exception& e)
114     {
115         impl->error = true;
116         impl->error_message = impl->error_message + "Exception: " + String(e.what()) + "\n";
117     }
118 }
119 
120 
getByIndex(int index,bool space_delete,int type,void * dst) const121 void CommandLineParser::getByIndex(int index, bool space_delete, int type, void* dst) const
122 {
123     try
124     {
125         for (size_t i = 0; i < impl->data.size(); i++)
126         {
127             if (impl->data[i].number == index)
128             {
129                 String v = impl->data[i].def_value;
130                 if (space_delete == true) v = impl->cat_string(v);
131                 from_str(v, type, dst);
132                 return;
133             }
134         }
135         impl->error = true;
136         impl->error_message = impl->error_message + "Unknown parameter #" + format("%d", index) + "\n";
137     }
138     catch(std::exception & e)
139     {
140         impl->error = true;
141         impl->error_message = impl->error_message + "Exception: " + String(e.what()) + "\n";
142     }
143 }
144 
cmp_params(const CommandLineParserParams & p1,const CommandLineParserParams & p2)145 static bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2)
146 {
147     if (p1.number < p2.number)
148         return true;
149 
150     if (p1.number > p2.number)
151         return false;
152 
153     return p1.keys[0].compare(p2.keys[0]) < 0;
154 }
155 
CommandLineParser(int argc,const char * const argv[],const String & keys)156 CommandLineParser::CommandLineParser(int argc, const char* const argv[], const String& keys)
157 {
158     impl = new Impl;
159     impl->refcount = 1;
160 
161     // path to application
162     size_t pos_s = String(argv[0]).find_last_of("/\\");
163     if (pos_s == String::npos)
164     {
165         impl->path_to_app = "";
166         impl->app_name = String(argv[0]);
167     }
168     else
169     {
170         impl->path_to_app = String(argv[0]).substr(0, pos_s);
171         impl->app_name = String(argv[0]).substr(pos_s + 1, String(argv[0]).length() - pos_s);
172     }
173 
174     impl->error = false;
175     impl->error_message = "";
176 
177     // parse keys
178     std::vector<String> k = impl->split_range_string(keys, '{', '}');
179 
180     int jj = 0;
181     for (size_t i = 0; i < k.size(); i++)
182     {
183         std::vector<String> l = impl->split_string(k[i], '|', true);
184         CommandLineParserParams p;
185         p.keys = impl->split_string(l[0]);
186         p.def_value = l[1];
187         p.help_message = impl->cat_string(l[2]);
188         p.number = -1;
189         if (p.keys.size() <= 0)
190         {
191             impl->error = true;
192             impl->error_message = "Field KEYS could not be empty\n";
193         }
194         else
195         {
196             if (p.keys[0][0] == '@')
197             {
198                 p.number = jj;
199                 jj++;
200             }
201 
202             impl->data.push_back(p);
203         }
204     }
205 
206     // parse argv
207     jj = 0;
208     for (int i = 1; i < argc; i++)
209     {
210         String s = String(argv[i]);
211 
212         if (s.find('=') != String::npos && s.find('=') < s.length())
213         {
214             std::vector<String> k_v = impl->split_string(s, '=', true);
215             for (int h = 0; h < 2; h++)
216             {
217                 if (k_v[0][0] == '-')
218                     k_v[0] = k_v[0].substr(1, k_v[0].length() -1);
219             }
220             impl->apply_params(k_v[0], k_v[1]);
221         }
222         else if (s.length() > 2 && s[0] == '-' && s[1] == '-')
223         {
224             impl->apply_params(s.substr(2), "true");
225         }
226         else if (s.length() > 1 && s[0] == '-')
227         {
228             impl->apply_params(s.substr(1), "true");
229         }
230         else
231         {
232             impl->apply_params(jj, s);
233             jj++;
234         }
235     }
236 
237     impl->sort_params();
238 }
239 
~CommandLineParser()240 CommandLineParser::~CommandLineParser()
241 {
242     if (CV_XADD(&impl->refcount, -1) == 1)
243         delete impl;
244 }
245 
CommandLineParser(const CommandLineParser & parser)246 CommandLineParser::CommandLineParser(const CommandLineParser& parser)
247 {
248     impl = parser.impl;
249     CV_XADD(&impl->refcount, 1);
250 }
251 
operator =(const CommandLineParser & parser)252 CommandLineParser& CommandLineParser::operator = (const CommandLineParser& parser)
253 {
254     if( this != &parser )
255     {
256         if(CV_XADD(&impl->refcount, -1) == 1)
257             delete impl;
258         impl = parser.impl;
259         CV_XADD(&impl->refcount, 1);
260     }
261     return *this;
262 }
263 
about(const String & message)264 void CommandLineParser::about(const String& message)
265 {
266     impl->about_message = message;
267 }
268 
apply_params(const String & key,const String & value)269 void CommandLineParser::Impl::apply_params(const String& key, const String& value)
270 {
271     for (size_t i = 0; i < data.size(); i++)
272     {
273         for (size_t k = 0; k < data[i].keys.size(); k++)
274         {
275             if (key.compare(data[i].keys[k]) == 0)
276             {
277                 data[i].def_value = value;
278                 break;
279             }
280         }
281     }
282 }
283 
apply_params(int i,String value)284 void CommandLineParser::Impl::apply_params(int i, String value)
285 {
286     for (size_t j = 0; j < data.size(); j++)
287     {
288         if (data[j].number == i)
289         {
290             data[j].def_value = value;
291             break;
292         }
293     }
294 }
295 
sort_params()296 void CommandLineParser::Impl::sort_params()
297 {
298     for (size_t i = 0; i < data.size(); i++)
299     {
300         std::sort(data[i].keys.begin(), data[i].keys.end());
301     }
302 
303     std::sort (data.begin(), data.end(), cmp_params);
304 }
305 
cat_string(const String & str) const306 String CommandLineParser::Impl::cat_string(const String& str) const
307 {
308     int left = 0, right = (int)str.length();
309     while( left <= right && str[left] == ' ' )
310         left++;
311     while( right > left && str[right-1] == ' ' )
312         right--;
313     return left >= right ? String("") : str.substr(left, right-left);
314 }
315 
getPathToApplication() const316 String CommandLineParser::getPathToApplication() const
317 {
318     return impl->path_to_app;
319 }
320 
has(const String & name) const321 bool CommandLineParser::has(const String& name) const
322 {
323     for (size_t i = 0; i < impl->data.size(); i++)
324     {
325         for (size_t j = 0; j < impl->data[i].keys.size(); j++)
326         {
327             if (name.compare(impl->data[i].keys[j]) == 0 && String("true").compare(impl->data[i].def_value) == 0)
328             {
329                 return true;
330             }
331         }
332     }
333     return false;
334 }
335 
check() const336 bool CommandLineParser::check() const
337 {
338     return impl->error == false;
339 }
340 
printErrors() const341 void CommandLineParser::printErrors() const
342 {
343     if (impl->error)
344     {
345         printf("\nERRORS:\n%s\n", impl->error_message.c_str());
346         fflush(stdout);
347     }
348 }
349 
printMessage() const350 void CommandLineParser::printMessage() const
351 {
352     if (impl->about_message != "")
353         printf("%s\n", impl->about_message.c_str());
354 
355     printf("Usage: %s [params] ", impl->app_name.c_str());
356 
357     for (size_t i = 0; i < impl->data.size(); i++)
358     {
359         if (impl->data[i].number > -1)
360         {
361             String name = impl->data[i].keys[0].substr(1, impl->data[i].keys[0].length() - 1);
362             printf("%s ", name.c_str());
363         }
364     }
365 
366     printf("\n\n");
367 
368     for (size_t i = 0; i < impl->data.size(); i++)
369     {
370         if (impl->data[i].number == -1)
371         {
372             printf("\t");
373             for (size_t j = 0; j < impl->data[i].keys.size(); j++)
374             {
375                 String k = impl->data[i].keys[j];
376                 if (k.length() > 1)
377                 {
378                     printf("--");
379                 }
380                 else
381                 {
382                     printf("-");
383                 }
384                 printf("%s", k.c_str());
385 
386                 if (j != impl->data[i].keys.size() - 1)
387                 {
388                     printf(", ");
389                 }
390             }
391             String dv = impl->cat_string(impl->data[i].def_value);
392             if (dv.compare("") != 0)
393             {
394                 printf(" (value:%s)", dv.c_str());
395             }
396             printf("\n\t\t%s\n", impl->data[i].help_message.c_str());
397         }
398     }
399     printf("\n");
400 
401     for (size_t i = 0; i < impl->data.size(); i++)
402     {
403         if (impl->data[i].number != -1)
404         {
405             printf("\t");
406             String k = impl->data[i].keys[0];
407             k = k.substr(1, k.length() - 1);
408 
409             printf("%s", k.c_str());
410 
411             String dv = impl->cat_string(impl->data[i].def_value);
412             if (dv.compare("") != 0)
413             {
414                 printf(" (value:%s)", dv.c_str());
415             }
416             printf("\n\t\t%s\n", impl->data[i].help_message.c_str());
417         }
418     }
419 }
420 
split_range_string(const String & _str,char fs,char ss) const421 std::vector<String> CommandLineParser::Impl::split_range_string(const String& _str, char fs, char ss) const
422 {
423     String str = _str;
424     std::vector<String> vec;
425     String word = "";
426     bool begin = false;
427 
428     while (!str.empty())
429     {
430         if (str[0] == fs)
431         {
432             if (begin == true)
433             {
434                 throw cv::Exception(CV_StsParseError,
435                          String("error in split_range_string(")
436                          + str
437                          + String(", ")
438                          + String(1, fs)
439                          + String(", ")
440                          + String(1, ss)
441                          + String(")"),
442                          "", __FILE__, __LINE__
443                          );
444             }
445             begin = true;
446             word = "";
447             str = str.substr(1, str.length() - 1);
448         }
449 
450         if (str[0] == ss)
451         {
452             if (begin == false)
453             {
454                 throw cv::Exception(CV_StsParseError,
455                          String("error in split_range_string(")
456                          + str
457                          + String(", ")
458                          + String(1, fs)
459                          + String(", ")
460                          + String(1, ss)
461                          + String(")"),
462                          "", __FILE__, __LINE__
463                          );
464             }
465             begin = false;
466             vec.push_back(word);
467         }
468 
469         if (begin == true)
470         {
471             word = word + str[0];
472         }
473         str = str.substr(1, str.length() - 1);
474     }
475 
476     if (begin == true)
477     {
478         throw cv::Exception(CV_StsParseError,
479                  String("error in split_range_string(")
480                  + str
481                  + String(", ")
482                  + String(1, fs)
483                  + String(", ")
484                  + String(1, ss)
485                  + String(")"),
486                  "", __FILE__, __LINE__
487                 );
488     }
489 
490     return vec;
491 }
492 
split_string(const String & _str,char symbol,bool create_empty_item) const493 std::vector<String> CommandLineParser::Impl::split_string(const String& _str, char symbol, bool create_empty_item) const
494 {
495     String str = _str;
496     std::vector<String> vec;
497     String word = "";
498 
499     while (!str.empty())
500     {
501         if (str[0] == symbol)
502         {
503             if (!word.empty() || create_empty_item)
504             {
505                 vec.push_back(word);
506                 word = "";
507             }
508         }
509         else
510         {
511             word = word + str[0];
512         }
513         str = str.substr(1, str.length() - 1);
514     }
515 
516     if (word != "" || create_empty_item)
517     {
518         vec.push_back(word);
519     }
520 
521     return vec;
522 }
523 
524 }
525