1 /*
2  * H.265 video codec.
3  * Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
4  *
5  * Authors: struktur AG, Dirk Farin <farin@struktur.de>
6  *
7  * This file is part of libde265.
8  *
9  * libde265 is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation, either version 3 of
12  * the License, or (at your option) any later version.
13  *
14  * libde265 is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "configparam.h"
24 
25 #include <string.h>
26 #include <ctype.h>
27 #include <sstream>
28 #include <iomanip>
29 #include <iostream>
30 #include <algorithm>
31 #include <typeinfo>
32 
33 #ifndef RTTI_ENABLED
34 #error "Need to compile with RTTI enabled."
35 #endif
36 
remove_option(int * argc,char ** argv,int idx,int n=1)37 static void remove_option(int* argc,char** argv,int idx, int n=1)
38 {
39   for (int i=idx+n;i<*argc;i++) {
40     argv[i-n] = argv[i];
41   }
42 
43   *argc-=n;
44 }
45 
46 
processCmdLineArguments(char ** argv,int * argc,int idx)47 bool option_string::processCmdLineArguments(char** argv, int* argc, int idx)
48 {
49   if (argv==NULL)   { return false; }
50   if (idx >= *argc) { return false; }
51 
52   value = argv[idx];
53   value_set = true;
54 
55   remove_option(argc,argv,idx,1);
56 
57   return true;
58 }
59 
60 
set_range(int mini,int maxi)61 void option_int::set_range(int mini,int maxi)
62 {
63   have_low_limit =true;
64   have_high_limit=true;
65   low_limit =mini;
66   high_limit=maxi;
67 }
68 
getTypeDescr() const69 std::string option_int::getTypeDescr() const
70 {
71   std::stringstream sstr;
72   sstr << "(int)";
73 
74   if (have_low_limit || have_high_limit) { sstr << " "; }
75   if (have_low_limit) { sstr << low_limit << " <= "; }
76   if (have_low_limit || have_high_limit) { sstr << "x"; }
77   if (have_high_limit) { sstr << " <= " << high_limit; }
78 
79   if (!valid_values_set.empty()) {
80     sstr << " {";
81     bool first=true;
82     FOR_LOOP(int, v, valid_values_set) {
83       if (!first) sstr << ","; else first=false;
84       sstr << v;
85     }
86     sstr << "}";
87   }
88 
89   return sstr.str();
90 }
91 
processCmdLineArguments(char ** argv,int * argc,int idx)92 bool option_int::processCmdLineArguments(char** argv, int* argc, int idx)
93 {
94   if (argv==NULL)   { return false; }
95   if (idx >= *argc) { return false; }
96 
97   int v = atoi(argv[idx]);
98   if (!is_valid(v)) { return false; }
99 
100   value = v;
101   value_set = true;
102 
103   remove_option(argc,argv,idx,1);
104 
105   return true;
106 }
107 
is_valid(int v) const108 bool option_int::is_valid(int v) const
109 {
110   if (have_low_limit  && v<low_limit)  { return false; }
111   if (have_high_limit && v>high_limit) { return false; }
112 
113   if (!valid_values_set.empty()) {
114     auto iter = std::find(valid_values_set.begin(), valid_values_set.end(), v);
115     if (iter==valid_values_set.end()) { return false; }
116   }
117 
118   return true;
119 }
120 
get_default_string() const121 std::string option_int::get_default_string() const
122 {
123   std::stringstream sstr;
124   sstr << default_value;
125   return sstr.str();
126 }
127 
128 
getTypeDescr() const129 std::string choice_option_base::getTypeDescr() const
130 {
131   std::vector<std::string> choices = get_choice_names();
132 
133   std::stringstream sstr;
134   sstr << "{";
135 
136   bool first=true;
137 #ifdef FOR_LOOP_AUTO_SUPPORT
138   FOR_LOOP(auto, c, choices) {
139 #else
140   FOR_LOOP(std::string, c, choices) {
141 #endif
142     if (first) { first=false; }
143     else { sstr << ","; }
144 
145     sstr << c;
146   }
147 
148   sstr << "}";
149   return sstr.str();
150 }
151 
152 
153 bool choice_option_base::processCmdLineArguments(char** argv, int* argc, int idx)
154 {
155   if (argv==NULL)   { return false; }
156   if (idx >= *argc) { return false; }
157 
158   std::string value = argv[idx];
159 
160   std::cout << "set " << value << "\n";
161   bool success = set_value(value);
162   std::cout << "success " << success << "\n";
163 
164   remove_option(argc,argv,idx,1);
165 
166   return success;
167 }
168 
169 
170 static char* fill_strings_into_memory(const std::vector<std::string>& strings_list)
171 {
172   // calculate memory requirement
173 
174   int totalStringLengths = 0;
175 #ifdef FOR_LOOP_AUTO_SUPPORT
176   FOR_LOOP(auto, str, strings_list) {
177 #else
178   FOR_LOOP(std::string, str, strings_list) {
179 #endif
180     totalStringLengths += str.length() +1; // +1 for null termination
181   }
182 
183   int numStrings = strings_list.size();
184 
185   int pointersSize = (numStrings+1) * sizeof(const char*);
186 
187   char* memory = new char[pointersSize + totalStringLengths];
188 
189 
190   // copy strings to memory area
191 
192   char* stringPtr = memory + (numStrings+1) * sizeof(const char*);
193   const char** tablePtr = (const char**)memory;
194 
195 #ifdef FOR_LOOP_AUTO_SUPPORT
196   FOR_LOOP(auto, str, strings_list) {
197 #else
198   FOR_LOOP(std::string, str, strings_list) {
199 #endif
200     *tablePtr++ = stringPtr;
201 
202     strcpy(stringPtr, str.c_str());
203     stringPtr += str.length()+1;
204   }
205 
206   *tablePtr = NULL;
207 
208   return memory;
209 }
210 
211 
212 const char** choice_option_base::get_choices_string_table() const
213 {
214   if (choice_string_table==NULL) {
215     choice_string_table = fill_strings_into_memory(get_choice_names());
216   }
217 
218   return (const char**)choice_string_table;
219 }
220 
221 
222 
223 bool config_parameters::parse_command_line_params(int* argc, char** argv, int* first_idx_ptr,
224                                                   bool ignore_unknown_options)
225 {
226   int first_idx=1;
227   if (first_idx_ptr) { first_idx = *first_idx_ptr; }
228 
229   for (int i=first_idx;i < *argc;i++) {
230 
231     if (argv[i][0]=='-') {
232       // option
233 
234       if (argv[i][1]=='-') {
235         // long option
236 
237         bool option_found=false;
238 
239         for (int o=0;o<mOptions.size();o++) {
240           if (mOptions[o]->hasLongOption() && strcmp(mOptions[o]->getLongOption().c_str(),
241                                                      argv[i]+2)==0) {
242             option_found=true;
243 
244             bool success = mOptions[o]->processCmdLineArguments(argv,argc, i+1);
245             if (!success) {
246               if (first_idx_ptr) { *first_idx_ptr = i; }
247               return false;
248             }
249 
250             remove_option(argc,argv,i);
251             i--;
252 
253             break;
254           }
255         }
256 
257         if (option_found == false && !ignore_unknown_options) {
258           return false;
259         }
260       }
261       else {
262         // short option
263 
264         bool is_single_option = (argv[i][1] != 0 && argv[i][2]==0);
265         bool do_remove_option = true;
266 
267         for (int n=1; argv[i][n]; n++) {
268           char option = argv[i][n];
269 
270           bool option_found=false;
271 
272           for (int o=0;o<mOptions.size();o++) {
273             if (mOptions[o]->getShortOption() == option) {
274               option_found=true;
275 
276               bool success;
277               if (is_single_option) {
278                 success = mOptions[o]->processCmdLineArguments(argv,argc, i+1);
279               }
280               else {
281                 success = mOptions[o]->processCmdLineArguments(NULL,NULL, 0);
282               }
283 
284               if (!success) {
285                 if (first_idx_ptr) { *first_idx_ptr = i; }
286                 return false;
287               }
288 
289               break;
290             }
291           }
292 
293           if (!option_found) {
294             if (!ignore_unknown_options) {
295               fprintf(stderr, "unknown option -%c\n",option);
296               return false;
297             }
298             else {
299               do_remove_option=false;
300             }
301           }
302 
303         } // all short options
304 
305         if (do_remove_option) {
306           remove_option(argc,argv,i);
307           i--;
308         }
309       } // is short option
310     } // is option
311   } // all command line arguments
312 
313   return true;
314 }
315 
316 
317 void config_parameters::print_params() const
318 {
319   for (int i=0;i<mOptions.size();i++) {
320     const option_base* o = mOptions[i];
321 
322     std::stringstream sstr;
323     sstr << "  ";
324     if (o->hasShortOption()) {
325       sstr << '-' << o->getShortOption();
326     } else {
327       sstr << "  ";
328     }
329 
330     if (o->hasShortOption() && o->hasLongOption()) {
331       sstr << ", ";
332     } else {
333       sstr << "  ";
334     }
335 
336     if (o->hasLongOption()) {
337       sstr << "--" << std::setw(12) << std::left << o->getLongOption();
338     } else {
339       sstr << "              ";
340     }
341 
342     sstr << " ";
343     sstr << o->getTypeDescr();
344 
345     if (o->has_default()) {
346       sstr << ", default=" << o->get_default_string();
347     }
348 
349     sstr << "\n";
350 
351     std::cerr << sstr.str();
352   }
353 }
354 
355 
356 void config_parameters::add_option(option_base* o)
357 {
358   mOptions.push_back(o);
359   delete[] param_string_table; // delete old table, since we got a new parameter
360   param_string_table = NULL;
361 }
362 
363 
364 std::vector<std::string> config_parameters::get_parameter_IDs() const
365 {
366   std::vector<std::string> ids;
367 
368 #ifdef FOR_LOOP_AUTO_SUPPORT
369   FOR_LOOP(auto, option, mOptions) {
370 #else
371   FOR_LOOP(option_base*, option, mOptions) {
372 #endif
373     ids.push_back(option->get_name());
374   }
375 
376   return ids;
377 }
378 
379 
380 enum en265_parameter_type config_parameters::get_parameter_type(const char* param) const
381 {
382   option_base* option = find_option(param);
383   assert(option);
384 
385   if (dynamic_cast<option_int*>   (option)) { return en265_parameter_int; }
386   if (dynamic_cast<option_bool*>  (option)) { return en265_parameter_bool; }
387   if (dynamic_cast<option_string*>(option)) { return en265_parameter_string; }
388   if (dynamic_cast<choice_option_base*>(option)) { return en265_parameter_choice; }
389 
390   assert(false);
391   return en265_parameter_bool;
392 }
393 
394 
395 std::vector<std::string> config_parameters::get_parameter_choices(const char* param) const
396 {
397   option_base* option = find_option(param);
398   assert(option);
399 
400   choice_option_base* o = dynamic_cast<choice_option_base*>(option);
401   assert(o);
402 
403   return o->get_choice_names();
404 }
405 
406 
407 option_base* config_parameters::find_option(const char* param) const
408 {
409 #ifdef FOR_LOOP_AUTO_SUPPORT
410   FOR_LOOP(auto, o, mOptions) {
411 #else
412   FOR_LOOP(option_base*, o, mOptions) {
413 #endif
414     if (strcmp(o->get_name().c_str(), param)==0) { return o; }
415   }
416 
417   return NULL;
418 }
419 
420 
421 bool config_parameters::set_bool(const char* param, bool value)
422 {
423   option_base* option = find_option(param);
424   assert(option);
425 
426   option_bool* o = dynamic_cast<option_bool*>(option);
427   assert(o);
428 
429   return o->set(value);
430 }
431 
432 bool config_parameters::set_int(const char* param, int value)
433 {
434   option_base* option = find_option(param);
435   assert(option);
436 
437   option_int* o = dynamic_cast<option_int*>(option);
438   assert(o);
439 
440   return o->set(value);
441 }
442 
443 bool config_parameters::set_string(const char* param, const char* value)
444 {
445   option_base* option = find_option(param);
446   assert(option);
447 
448   option_string* o = dynamic_cast<option_string*>(option);
449   assert(o);
450 
451   return o->set(value);
452 }
453 
454 bool config_parameters::set_choice(const char* param, const char* value)
455 {
456   option_base* option = find_option(param);
457   assert(option);
458 
459   choice_option_base* o = dynamic_cast<choice_option_base*>(option);
460   assert(o);
461 
462   return o->set(value);
463 }
464 
465 
466 
467 const char** config_parameters::get_parameter_choices_table(const char* param) const
468 {
469   option_base* option = find_option(param);
470   assert(option);
471 
472   choice_option_base* o = dynamic_cast<choice_option_base*>(option);
473   assert(o);
474 
475   return o->get_choices_string_table();
476 }
477 
478 const char** config_parameters::get_parameter_string_table() const
479 {
480   if (param_string_table==NULL) {
481     param_string_table = fill_strings_into_memory(get_parameter_IDs());
482   }
483 
484   return (const char**)param_string_table;
485 }
486