Ver Fonte

Improve functionality and reliability of Args

Daniele Bartolini há 12 anos atrás
pai
commit
24a875dee6
2 ficheiros alterados com 258 adições e 180 exclusões
  1. 213 0
      src/core/Args.cpp
  2. 45 180
      src/core/Args.h

+ 213 - 0
src/core/Args.cpp

@@ -0,0 +1,213 @@
+/*
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Args.h"
+#include "Log.h"
+
+namespace crown
+{
+
+//-----------------------------------------------------------------------------
+Args::Args(int argc, char** argv, const char* shortopts, const ArgsOption* longopts) :
+	m_argc(argc),
+	m_argv(argv),
+	m_shortopts(shortopts),
+	m_longopts(longopts),
+	m_optind(1),				// Do not consider argv[0]
+	m_scope(argc),
+	m_optarg(NULL)
+{
+	assert(argv != NULL);
+	assert(shortopts != NULL);
+	// longopts could be NULL
+}
+
+//-----------------------------------------------------------------------------
+Args::~Args()
+{
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::getopt()
+{
+	// Always reset optarg
+	m_optarg = NULL;
+
+	// End of arguments
+	if (m_optind >= m_scope)
+	{
+		return -1;
+	}
+
+	switch (option_type(m_argv[m_optind]))
+	{
+		case AOT_SHORT:
+		{
+			return short_option(m_argv[m_optind]);
+		}
+		case AOT_LONG:
+		{
+			return long_option(m_argv[m_optind]);
+		}
+		case AOT_NOT_OPTION:
+		{
+			not_option();
+			return getopt();
+		}
+		default:
+		{
+			return '?';
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::optind() const
+{
+	return m_optind;
+}
+
+//-----------------------------------------------------------------------------
+const char* Args::optarg() const
+{
+	return m_optarg;
+}
+
+//-----------------------------------------------------------------------------
+void Args::set_optind(int32_t index)
+{
+	m_optind = index;
+}
+
+//-----------------------------------------------------------------------------
+ArgsOptionType Args::option_type(const char* option)
+{
+	const size_t option_len = string::strlen(option);
+
+	if (option_len == 2 && option[0] == '-' && option[1] != '-')
+	{
+		return AOT_SHORT;
+	}
+	else if (option_len > 2 && option[0] == '-' && option[1] == '-')
+	{
+		return AOT_LONG;
+	}
+
+	return AOT_NOT_OPTION;
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::long_option(const char* option)
+{
+	// Long argument detected, but it is not so.
+	assert(string::strlen(option) > 2);
+
+	const ArgsOption* current_option = m_longopts;
+
+	// Loop through all the long options
+	while (!end_of_longopts(current_option))
+	{
+		if (string::strcmp(current_option->name, &option[2]) == 0)
+		{
+			// If the option requires an argument
+			if (current_option->has_arg == AOA_REQUIRED_ARGUMENT)
+			{
+				// Read the argument if it exists
+				if ((m_optind + 1) < m_scope)
+				{
+					// Read the argument and skip the following parameter
+					m_optarg = m_argv[m_optind + 1];
+					m_optind += 2;
+				}
+				else
+				{
+					Log::e("%s: option requires an argument -- '%s'", m_argv[0], current_option->name);
+
+					// Missing option
+					m_optind += 1;
+					return '?';
+				}
+			}
+			// If the option does not require an argument
+			else
+			{
+				m_optind++;
+			}
+
+			if (current_option->flag == NULL)
+			{
+				return current_option->val;
+			}
+			else
+			{
+				(*current_option->flag) = current_option->val;
+
+				return 0;
+			}
+		}
+
+		current_option++;
+	}
+
+	// Found a long option but was not included in longopts
+	Log::e("%s: invalid option -- '%s'", m_argv[0], &option[2]);
+	m_optind++;
+	return '?';
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::short_option(const char* option)
+{
+	(void)option;
+
+	Log::e("%s: invalid option -- '%s'", m_argv[0], &option[1]);
+	m_optind++;
+	return '?';
+}
+
+//-----------------------------------------------------------------------------
+void Args::not_option()
+{
+	char* current_option = m_argv[m_optind];
+
+	for (int32_t i = m_optind; i < (m_argc - 1); i++)
+	{
+		m_argv[i] = m_argv[i + 1];
+	}
+
+	m_argv[m_argc - 1] = current_option;
+
+	// Reduce the number of true arguments
+	m_scope--;
+}
+
+//-----------------------------------------------------------------------------
+bool Args::end_of_longopts(const ArgsOption* option) const
+{
+	return (option->name == NULL && option->has_arg == 0 && option->flag == NULL && option->val == 0);
+}
+
+
+} // namespace crown

+ 45 - 180
src/core/Args.h

@@ -34,15 +34,15 @@ namespace crown
 
 enum ArgsOptionType
 {
-	AOT_SHORT				= 1,
-	AOT_LONG				= 2,
-	AOT_NOT_OPTION			= 3
+	AOT_SHORT,
+	AOT_LONG,
+	AOT_NOT_OPTION
 };
 
 enum ArgsOptionArgument
 {
-	AOA_NO_ARGUMENT			= 1,
-	AOA_REQUIRED_ARGUMENT	= 2
+	AOA_NO_ARGUMENT,
+	AOA_REQUIRED_ARGUMENT
 };
 
 struct ArgsOption
@@ -56,202 +56,67 @@ struct ArgsOption
 /// Parses the command line arguments in a way very similar to GNU getopt.
 class Args
 {
-public:
-
-					
 public:
 
 						Args(int argc, char** argv, const char* shortopts, const ArgsOption* longopts);
 						~Args();
 
-	int32_t				next_option();
-	int32_t				option_index() const;
-	const char*			option_argument() const;
-	
-
-private:
-
-						// Returns the @option type
-						// Returns AOT_SHORT if @option starts with "-"
-						// Returns AOT_LONG if @option starts with "--"
-	ArgsOptionType		option_type(const char* option);
-	int32_t				long_option(const char* option);
-	int32_t				short_option(const char* option);
-
-private:
+	/// Finds the next option character and returns it.
+	/// If there are no more option characters, it returns -1 and optind()
+	/// returns the index in argv[] of the first argv-element that is not
+	/// an option.
+	/// If it finds an option that was not included in shortopts or longopts,
+	/// or if it finds a missing option argument, it returns '?' character.
+	int32_t				getopt();
 
-	int					m_argc;
-	char**				m_argv;
-	const char*			m_shortopts;
-	const ArgsOption*	m_longopts;
-
-	int					m_current_arg;
-
-	int					m_option_index;
-	char*				m_option_argument;
-};
-
-//-----------------------------------------------------------------------------
-inline Args::Args(int argc, char** argv, const char* shortopts, const ArgsOption* longopts) :
-	m_argc(argc),
-	m_argv(argv),
-	m_shortopts(shortopts),
-	m_longopts(longopts),
-	m_current_arg(1),			// Do not consider argv[0]
-	m_option_index(0),
-	m_option_argument(NULL)
-{
-	assert(argv != NULL);
-	assert(shortopts != NULL);
-	// longopts could be NULL
-}
-
-//-----------------------------------------------------------------------------
-inline Args::~Args()
-{
-}
-
-//-----------------------------------------------------------------------------
-inline int32_t Args::next_option()
-{
-	// End of arguments
-	if (m_current_arg >= m_argc)
-	{
-		return -1;
-	}
+	/// Returns the index of the next argument to be processed.
+	int32_t				optind() const;
 
-	const char* current_arg = m_argv[m_current_arg];
+	/// Returns the text of the following argv-element in respect
+	/// to the current optind().
+	const char*			optarg() const;
 
-	ArgsOptionType type = option_type(current_arg);
 
-	int32_t return_value = -1;
+	/// Sets the @index into argv[] from where to start option scanning.
+	/// If @index >= argc nothing will be scanned.
+	void				set_optind(int32_t index);
 
-	switch (type)
-	{
-		case AOT_SHORT:
-		{
-			return_value = short_option(current_arg);
-			break;
-		}
-		case AOT_LONG:
-		{
-			return_value = long_option(current_arg);
-			break;
-		}
-		case AOT_NOT_OPTION:
-		{
-			return_value = '?';
-			break;
-		}
-		default:
-		{
-			return_value = '?';
-			break;
-		}
-	}
-
-	m_current_arg++;
-
-	return return_value;
-}
-
-//-----------------------------------------------------------------------------
-inline int32_t Args::option_index() const
-{
-	return m_option_index;
-}
-
-//-----------------------------------------------------------------------------
-inline const char* Args::option_argument() const
-{
-	return m_option_argument;
-}
-
-//-----------------------------------------------------------------------------
-inline ArgsOptionType Args::option_type(const char* option)
-{
-	size_t option_len = string::strlen(option);
-
-	if (option_len > 2)
-	{
-		if (option[0] == '-' && option[1] == '-')
-		{
-			return AOT_LONG;
-		}
-	}
-
-	if (option_len > 1)
-	{
-		if (option[0] == '-')
-		{
-			return AOT_SHORT;
-		}
-	}
+private:
 
-	return AOT_NOT_OPTION;
-}
+	// Returns the @option type
+	// Returns AOT_SHORT if option is of the form "-x" where 'x' is the option.
+	// Returns AOT_LONG if option is of the form "--option" where "option" is the option.
+	// Returns AOT_NOT_OPTION in all other cases.
+	ArgsOptionType		option_type(const char* option);
 
-//-----------------------------------------------------------------------------
-inline int32_t Args::long_option(const char* option)
-{
-	int32_t option_index = 0;
-	const ArgsOption* current_option = m_longopts;
+	// Parses a long option
+	int32_t				long_option(const char* option);
 
-	while (1)
-	{
-		if (current_option->name == NULL && current_option->has_arg == 0 &&
-			current_option->flag == NULL && current_option->val == 0)
-		{
-			break;
-		}
+	// Parses a short option
+	int32_t				short_option(const char* option);
 
-		if (string::strcmp(current_option->name, &option[2]) == 0)
-		{
-			m_option_index = option_index;
+	void				not_option();
 
-			if (current_option->has_arg == AOA_REQUIRED_ARGUMENT)
-			{
-				if (m_argc > m_current_arg + 1)
-				{
-					m_option_argument = m_argv[m_current_arg + 1];
-				}
-				else
-				{
-					m_option_argument = NULL;
-				}
+	// Returns whether the given option is the last one
+	bool				end_of_longopts(const ArgsOption* option) const;
 
-				m_current_arg += 1;
-			}
-			else
-			{
-				m_option_argument = NULL;
-			}
+private:
 
-			if (current_option->flag == NULL)
-			{
-				return current_option->val;
-			}
-			else
-			{
-				(*current_option->flag) = current_option->val;
+	int					m_argc;
+	char**				m_argv;
 
-				return 0;
-			}
-		}
+	const char*			m_shortopts;
+	const ArgsOption*	m_longopts;
 
-		option_index++;
-		current_option++;
-	}
+	// Index of the next argument to be processed
+	int32_t				m_optind;
 
-	return -1;
-}
+	// Number of "true" arguments
+	int32_t				m_scope;
 
-//-----------------------------------------------------------------------------
-inline int32_t Args::short_option(const char* option)
-{
-	(void)option;
-	return 0;
-}
+	// The text of the following argv-element to argv[optind]
+	char*				m_optarg;
+};
 
 } // namespace crown