1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_command_line_params.cpp
28 #include "vogl_core.h"
29 #include "vogl_command_line_params.h"
30 #include "vogl_console.h"
31 #include "vogl_cfile_stream.h"
32 #include "vogl_strutils.h"
35 #define VOGL_CMD_LINE_ALLOW_SLASH_PARAMS 1
38 #ifdef VOGL_USE_WIN32_API
39 #include "vogl_winhdr.h"
42 #ifdef VOGL_USE_LINUX_API
43 #include <sys/types.h>
49 command_line_params g_command_line_params;
51 dynamic_string_array get_command_line_params(int argc, char *argv[])
53 dynamic_string_array params;
54 for (int i = 0; i < argc; i++)
55 params.push_back(dynamic_string(argv[i]));
59 dynamic_string_array get_command_line_params()
61 dynamic_string_array params;
63 #ifdef VOGL_USE_WIN32_API
64 dynamic_string cmd_line;
65 split_command_line_params(get_command_line(), params);
66 #elif defined(VOGL_USE_LINUX_API)
67 pid_t proc_id = getpid();
69 char cmdline_filename[256];
70 sprintf(cmdline_filename, "/proc/%i/cmdline", proc_id);
72 FILE *pFile = vogl_fopen(cmdline_filename, "rb");
75 console::error("Failed reading command line parameters from file \"%s\"!\n", cmdline_filename);
83 int c = vogl_fgetc(pFile);
88 tmp.append_char(static_cast<char>(c));
94 params.push_back(tmp);
101 #warning Unimplemented!
107 dynamic_string get_command_line()
109 dynamic_string cmd_line;
111 #ifdef VOGL_USE_WIN32_API
112 cmd_line.set(GetCommandLineA());
113 #elif defined(VOGL_USE_LINUX_API)
114 dynamic_string_array params(get_command_line_params());
116 for (uint i = 0; i < params.size(); i++)
118 dynamic_string tmp(params[i]);
120 // If the param is not already quoted, and it has any whitespace, then quote it. (The goal here is to ensure the split_command_line_params() method,
121 // which was unfortunately written for Windows where it's trivial to get the full unparsed cmd line as a string, doesn't split up this parameter.)
122 if ((tmp.front() != '\"') && (tmp.contains(' ') || tmp.contains('\t')))
124 tmp = "\"" + tmp + "\"";
127 if (cmd_line.get_len())
133 #warning Unimplemented!
138 dynamic_string get_command_line(int argc, char *argv[])
140 (void)argc, (void)argv;
142 dynamic_string cmd_line;
144 #ifdef VOGL_USE_WIN32_API
145 cmd_line.set(GetCommandLineA());
148 for (int i = 0; i < argc; i++)
150 dynamic_string tmp(argv[i]);
152 // If the param is not already quoted, and it has any whitespace, then quote it.
153 if ((tmp.front() != '\"') && (tmp.contains(' ') || tmp.contains('\t')))
155 tmp = "\"" + tmp + "\"";
158 if (cmd_line.get_len())
166 bool check_for_command_line_param(const char *pParam)
168 #ifdef VOGL_USE_WIN32_API
169 return (strstr(GetCommandLineA(), pParam) != NULL);
170 #elif defined(VOGL_USE_LINUX_API)
171 pid_t proc_id = getpid();
173 char cmdline_filename[256];
174 sprintf(cmdline_filename, "/proc/%i/cmdline", proc_id);
176 FILE *pFile = vogl_fopen(cmdline_filename, "rb");
180 vogl::vector<char> buf;
184 int c = vogl_fgetc(pFile);
187 buf.push_back(static_cast<char>(c));
191 const char *pSrc = buf.get_ptr();
194 if (!strcmp(pParam, pSrc))
196 pSrc += strlen(pSrc) + 1;
199 #warning Unimplemented!
205 bool split_command_line_params(const char *p, dynamic_string_array ¶ms)
207 bool within_param = false;
208 bool within_quote = false;
215 const char c = p[ofs];
222 within_quote = false;
226 else if ((c == ' ') || (c == '\t'))
230 params.push_back(str);
233 within_param = false;
243 else if ((c != ' ') && (c != '\t'))
258 console::error("Unmatched quote in command line \"%s\"\n", p);
263 params.push_back(str);
267 void dump_command_line_info(uint n, const command_line_param_desc *pDesc, const char *prefix)
271 for (uint i = 0; i < n; i++)
273 console::message("%s%s", prefix, pDesc[i].m_pName);
274 for (uint j = 0; j < pDesc[i].m_num_values; j++)
275 console::message(" [value]");
277 if (pDesc[i].m_pDesc)
278 console::printf(": %s", pDesc[i].m_pDesc);
280 console::printf("\n");
284 command_line_params::command_line_params()
288 void command_line_params::clear()
295 bool command_line_params::load_string_file(const char *pFilename, dynamic_string_array &strings)
297 cfile_stream in_stream;
298 if (!in_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable))
300 console::error("Unable to open file \"%s\" for reading!\n", pFilename);
304 dynamic_string ansi_str;
308 if (!in_stream.read_line(ansi_str))
312 if (ansi_str.is_empty())
315 strings.push_back(dynamic_string(ansi_str.get_ptr()));
321 bool command_line_params::parse(const dynamic_string_array ¶ms, uint total_param_descs, const command_line_param_desc *pParam_desc, const command_line_params::parse_config &config)
325 command_line_param_desc desc;
326 desc.m_num_values = 0;
327 desc.m_support_listing_file = false;
332 while (arg_index < params.size())
334 const uint cur_arg_index = arg_index;
335 const dynamic_string &src_param = params[arg_index++];
337 if (src_param.is_empty())
340 bool is_param = false;
341 uint param_prefix_chars = 1;
343 #if VOGL_CMD_LINE_ALLOW_SLASH_PARAMS
344 is_param = (src_param[0] == '/');
346 if ((src_param[0] == '-') && ((config.m_single_minus_params) || (config.m_double_minus_params)))
350 bool double_minus = (src_param[1] == '-');
353 if (config.m_double_minus_params)
355 param_prefix_chars = 2;
359 if (config.m_ignore_unrecognized_params)
362 console::error("Unrecognized command line parameter: \"%s\"\n", src_param.get_ptr());
370 if (src_param.get_len() < (param_prefix_chars + 1))
372 console::warning("Skipping invalid command line parameter: \"%s\"\n", src_param.get_ptr());
376 dynamic_string key_str(src_param);
378 key_str.right(param_prefix_chars);
380 if (config.m_pParam_ignore_prefix)
382 if (key_str.begins_with(config.m_pParam_ignore_prefix, true))
386 if (config.m_pParam_accept_prefix)
388 if (!key_str.begins_with(config.m_pParam_accept_prefix, true))
393 char c = key_str[key_str.get_len() - 1];
400 key_str.left(key_str.get_len() - 1);
402 if (total_param_descs)
405 for (param_index = 0; param_index < total_param_descs; param_index++)
406 if (key_str == pParam_desc[param_index].m_pName)
409 if (param_index == total_param_descs)
411 if (config.m_ignore_unrecognized_params)
414 console::error("Unrecognized command line parameter: \"%s\"\n", src_param.get_ptr());
418 desc = pParam_desc[param_index];
421 const uint cMaxValues = 16;
422 dynamic_string val_str[cMaxValues];
423 uint num_val_strs = 0;
424 if (desc.m_num_values)
426 VOGL_ASSERT(desc.m_num_values <= cMaxValues);
428 if ((arg_index + desc.m_num_values) > params.size())
430 console::error("Expected %u value(s) after command line parameter: \"%s\"\n", desc.m_num_values, src_param.get_ptr());
434 for (uint v = 0; v < desc.m_num_values; v++)
436 val_str[num_val_strs++] = params[arg_index++];
440 dynamic_string_array strings;
442 if ((desc.m_support_listing_file) && (val_str[0].get_len() >= 2) && (val_str[0][0] == '@'))
444 dynamic_string filename(val_str[0]);
448 if (!load_string_file(filename.get_ptr(), strings))
450 console::error("Failed loading listing file \"%s\"!\n", filename.get_ptr());
456 for (uint v = 0; v < num_val_strs; v++)
458 val_str[v].unquote();
459 strings.push_back(val_str[v]);
464 pv.m_values.swap(strings);
465 pv.m_split_param_index = cur_arg_index;
466 pv.m_modifier = (int8)modifier;
467 m_param_map.insert(std::make_pair(key_str, pv));
469 else if (!config.m_ignore_non_params)
471 if ((config.m_fail_on_non_params) && (cur_arg_index))
473 console::error("Unrecognized command line argument: \"%s\"!\n", src_param.get_ptr());
478 pv.m_values.push_back(src_param);
479 pv.m_values.back().unquote();
480 pv.m_split_param_index = cur_arg_index;
481 m_param_map.insert(std::make_pair(g_empty_dynamic_string, pv));
488 bool command_line_params::parse(const char *pCmd_line, uint total_param_descs, const command_line_param_desc *pParam_desc, const command_line_params::parse_config &config)
490 dynamic_string_array p;
491 if (!split_command_line_params(pCmd_line, p))
497 if (config.m_skip_first_param)
500 return parse(p, total_param_descs, pParam_desc, config);
503 bool command_line_params::is_split_param_an_option(uint split_param_array_index) const
505 VOGL_ASSERT(split_param_array_index < m_params.size());
506 if (split_param_array_index >= m_params.size())
509 const dynamic_string &w = m_params[split_param_array_index];
513 #if VOGL_CMD_LINE_ALLOW_SLASH_PARAMS
514 return (w.get_len() >= 2) && ((w[0] == '-') || (w[0] == '/'));
516 return (w.get_len() >= 2) && (w[0] == '-');
520 bool command_line_params::find(const char *pKey, param_map_const_iterator &begin, param_map_const_iterator &end) const
522 dynamic_string key(pKey);
523 begin = m_param_map.lower_bound(key);
524 end = m_param_map.upper_bound(key);
528 uint command_line_params::get_count(const char *pKey) const
530 param_map_const_iterator begin, end;
531 find(pKey, begin, end);
544 command_line_params::param_map_const_iterator command_line_params::get_param(const char *pKey, uint key_index) const
546 param_map_const_iterator begin, end;
547 find(pKey, begin, end);
550 return m_param_map.end();
554 while ((begin != end) && (n != key_index))
561 return m_param_map.end();
566 bool command_line_params::has_value(const char *pKey, uint key_index) const
568 return get_num_values(pKey, key_index) != 0;
571 uint command_line_params::get_num_values(const char *pKey, uint key_index) const
573 param_map_const_iterator it = get_param(pKey, key_index);
578 return it->second.m_values.size();
581 bool command_line_params::get_value_as_bool(const char *pKey, uint key_index, bool def) const
583 param_map_const_iterator it = get_param(pKey, key_index);
587 if (it->second.m_modifier)
588 return it->second.m_modifier > 0;
593 int command_line_params::get_value_as_int(const char *pKey, uint key_index, int def, int l, int h, uint value_index, bool *pSuccess) const
598 param_map_const_iterator it = get_param(pKey, key_index);
601 if (value_index >= it->second.m_values.size())
603 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
608 const char *p = it->second.m_values[value_index].get_ptr();
609 if (!string_ptr_to_int(p, val))
612 vogl::console::warning("Non-integer value specified for parameter at index %u, using default value of %i\n", key_index, def);
614 vogl::console::warning("Non-integer value specified for parameter \"%s\" at index %u, using default value of %i\n", pKey, key_index, def);
620 vogl::console::warning("Value %i for parameter \"%s\" at index %u is out of range, clamping to %i\n", val, pKey, key_index, l);
625 vogl::console::warning("Value %i for parameter \"%s\" at index %u is out of range, clamping to %i\n", val, pKey, key_index, h);
635 int64_t command_line_params::get_value_as_int64(const char *pKey, uint key_index, int64_t def, int64_t l, int64_t h, uint value_index, bool *pSuccess) const
640 param_map_const_iterator it = get_param(pKey, key_index);
643 if (value_index >= it->second.m_values.size())
645 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
650 const char *p = it->second.m_values[value_index].get_ptr();
651 if (!string_ptr_to_int64(p, val))
654 vogl::console::warning("Non-integer value specified for parameter at index %u, using default value of %" PRIi64 "\n", key_index, def);
656 vogl::console::warning("Non-integer value specified for parameter \"%s\" at index %u, using default value of %" PRIi64 "\n", pKey, key_index, def);
662 vogl::console::warning("Value %" PRIi64 " for parameter \"%s\" at index %u is out of range, clamping to %" PRIi64 "\n", val, pKey, key_index, l);
667 vogl::console::warning("Value %" PRIi64 " for parameter \"%s\" at index %u is out of range, clamping to %" PRIi64 "\n", val, pKey, key_index, h);
677 uint command_line_params::get_value_as_uint(const char *pKey, uint key_index, uint def, uint l, uint h, uint value_index, bool *pSuccess) const
682 param_map_const_iterator it = get_param(pKey, key_index);
685 if (value_index >= it->second.m_values.size())
687 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
692 const char *p = it->second.m_values[value_index].get_ptr();
693 if (!string_ptr_to_uint(p, val))
696 vogl::console::warning("Non-integer value specified for parameter at index %u, using default value of %u\n", key_index, def);
698 vogl::console::warning("Non-integer value specified for parameter \"%s\" at index %u, using default value of %u\n", pKey, key_index, def);
704 vogl::console::warning("Value %u for parameter \"%s\" at index %u is out of range, clamping to %u\n", val, pKey, key_index, l);
709 vogl::console::warning("Value %u for parameter \"%s\" at index %u is out of range, clamping to %u\n", val, pKey, key_index, h);
719 uint64_t command_line_params::get_value_as_uint64(const char *pKey, uint key_index, uint64_t def, uint64_t l, uint64_t h, uint value_index, bool *pSuccess) const
724 param_map_const_iterator it = get_param(pKey, key_index);
727 if (value_index >= it->second.m_values.size())
729 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
734 const char *p = it->second.m_values[value_index].get_ptr();
735 if (!string_ptr_to_uint64(p, val))
738 vogl::console::warning("Non-integer value specified for parameter at index %u, using default value of %" PRIu64 "\n", key_index, def);
740 vogl::console::warning("Non-integer value specified for parameter \"%s\" at index %u, using default value of %" PRIu64 "\n", pKey, key_index, def);
746 vogl::console::warning("Value %" PRIu64 " for parameter \"%s\" at index %u is out of range, clamping to %" PRIu64 "\n", val, pKey, key_index, l);
751 vogl::console::warning("Value %" PRIu64 " for parameter \"%s\" at index %u is out of range, clamping to %" PRIu64 "\n", val, pKey, key_index, h);
761 float command_line_params::get_value_as_float(const char *pKey, uint key_index, float def, float l, float h, uint value_index, bool *pSuccess) const
766 param_map_const_iterator it = get_param(pKey, key_index);
769 if (value_index >= it->second.m_values.size())
771 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
776 const char *p = it->second.m_values[value_index].get_ptr();
777 if (!string_ptr_to_float(p, val))
779 vogl::console::warning("Invalid value specified for float parameter \"%s\", using default value of %f\n", pKey, def);
783 // Let's assume +-cNearlyInfinite implies no clamping.
784 if ((l != -math::cNearlyInfinite) && (val < l))
786 vogl::console::warning("Value %f for parameter \"%s\" is out of range, clamping to %f\n", val, pKey, l);
789 else if ((h != math::cNearlyInfinite) && (val > h))
791 vogl::console::warning("Value %f for parameter \"%s\" is out of range, clamping to %f\n", val, pKey, h);
801 bool command_line_params::get_value_as_string(dynamic_string &value, const char *pKey, uint key_index, const char *pDef, uint value_index) const
803 param_map_const_iterator it = get_param(pKey, key_index);
806 if (value_index >= it->second.m_values.size())
808 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
813 value = it->second.m_values[value_index];
817 dynamic_string command_line_params::get_value_as_string(const char *pKey, uint key_index, const char *pDef, uint value_index) const
819 param_map_const_iterator it = get_param(pKey, key_index);
821 return dynamic_string(pDef);
822 if (value_index >= it->second.m_values.size())
824 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
825 return dynamic_string(pDef);
828 return it->second.m_values[value_index];
831 const dynamic_string &command_line_params::get_value_as_string_or_empty(const char *pKey, uint key_index, uint value_index) const
833 param_map_const_iterator it = get_param(pKey, key_index);
835 return g_empty_dynamic_string;
837 if (value_index >= it->second.m_values.size())
839 vogl::console::debug("%s: Trying to retrieve value %u of command line parameter %s, but this parameter only has %u values\n", VOGL_METHOD_NAME, value_index, pKey, it->second.m_values.size());
840 return g_empty_dynamic_string;
843 return it->second.m_values[value_index];