You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

386 lines
12 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: winstl/system/commandline_parser.hpp (originally MLCmdArg.h, ::SynesisStd)
  3. *
  4. * Purpose: commandline_parser class.
  5. *
  6. * Created: 20th May 2000
  7. * Updated: 10th August 2009
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2000-2009, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file winstl/system/commandline_parser.hpp
  40. *
  41. * \brief [C++ only] Definition of the winstl::basic_commandline_parser
  42. * class template
  43. * (\ref group__library__system "System" Library).
  44. */
  45. #ifndef WINSTL_INCL_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER
  46. #define WINSTL_INCL_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER
  47. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  48. # define WINSTL_VER_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER_MAJOR 2
  49. # define WINSTL_VER_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER_MINOR 1
  50. # define WINSTL_VER_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER_REVISION 2
  51. # define WINSTL_VER_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER_EDIT 36
  52. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  53. /* /////////////////////////////////////////////////////////////////////////
  54. * Includes
  55. */
  56. #ifndef WINSTL_INCL_WINSTL_H_WINSTL
  57. # include <winstl/winstl.h>
  58. #endif /* !WINSTL_INCL_WINSTL_H_WINSTL */
  59. #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER
  60. # include <stlsoft/memory/auto_buffer.hpp>
  61. #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER */
  62. #ifndef WINSTL_INCL_WINSTL_MEMORY_HPP_PROCESSHEAP_ALLOCATOR
  63. # include <winstl/memory/processheap_allocator.hpp>
  64. #endif /* !WINSTL_INCL_WINSTL_MEMORY_HPP_PROCESSHEAP_ALLOCATOR */
  65. #ifndef STLSOFT_INCL_STLSOFT_STRING_HPP_CHAR_TRAITS
  66. # include <stlsoft/string/char_traits.hpp>
  67. #endif /* !STLSOFT_INCL_STLSOFT_STRING_HPP_CHAR_TRAITS */
  68. #ifndef STLSOFT_INCL_STLSOFT_SHIMS_ACCESS_HPP_STRING
  69. # include <stlsoft/shims/access/string.hpp>
  70. #endif /* !STLSOFT_INCL_STLSOFT_SHIMS_ACCESS_HPP_STRING */
  71. #ifndef STLSOFT_INCL_H_CTYPE
  72. # define STLSOFT_INCL_H_CTYPE
  73. # include <ctype.h> // for isspace()
  74. #endif /* !STLSOFT_INCL_H_CTYPE */
  75. /* /////////////////////////////////////////////////////////////////////////
  76. * Namespace
  77. */
  78. #ifndef _WINSTL_NO_NAMESPACE
  79. # if defined(_STLSOFT_NO_NAMESPACE) || \
  80. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  81. /* There is no stlsoft namespace, so must define ::winstl */
  82. namespace winstl
  83. {
  84. # else
  85. /* Define stlsoft::winstl_project */
  86. namespace stlsoft
  87. {
  88. namespace winstl_project
  89. {
  90. # endif /* _STLSOFT_NO_NAMESPACE */
  91. #endif /* !_WINSTL_NO_NAMESPACE */
  92. /* /////////////////////////////////////////////////////////////////////////
  93. * Classes
  94. */
  95. /** Parses a Windows (<code>WinMain()</code>) command line into
  96. * parts, and provides array semantics for their access.
  97. *
  98. \code
  99. winstl::commandline_parser cp("abc \"d e f\" ghi");
  100. assert(3 == cp.size());
  101. assert(0 == ::strcmp("abc", cp[0]));
  102. assert(0 == ::strcmp("d e f", cp[1]));
  103. assert(0 == ::strcmp("ghi", cp[2]));
  104. \endcode
  105. *
  106. * \ingroup group__library__system
  107. */
  108. template< ss_typename_param_k C
  109. , ss_typename_param_k T = stlsoft_ns_qual(stlsoft_char_traits)<C>
  110. >
  111. class basic_commandline_parser
  112. {
  113. /// \name Member Types
  114. /// @{
  115. public:
  116. /// The character type
  117. typedef C char_type;
  118. /// The traits type
  119. typedef T traits_type;
  120. /// The current instantiation of the type
  121. typedef basic_commandline_parser<C, T> class_type;
  122. private:
  123. typedef char_type* pointer_type_;
  124. typedef processheap_allocator<char_type> allocator_type_;
  125. typedef processheap_allocator<pointer_type_> pointers_allocator_type_;
  126. typedef stlsoft_ns_qual(auto_buffer)<
  127. char_type
  128. , 256
  129. , allocator_type_
  130. > buffer_type_;
  131. typedef stlsoft_ns_qual(auto_buffer)<
  132. pointer_type_
  133. , 20
  134. , pointers_allocator_type_
  135. > pointers_type_;
  136. typedef ss_typename_type_k buffer_type_::iterator iterator;
  137. public:
  138. /// The value type
  139. typedef ss_typename_type_k pointers_type_::value_type value_type;
  140. /// The non-mutating (const) iterator type
  141. typedef ss_typename_type_k pointers_type_::const_iterator const_iterator;
  142. /// The size type
  143. typedef ss_size_t size_type;
  144. /// @}
  145. /// \name Construction
  146. /// @{
  147. public:
  148. /** Parses the given command-line and creates an internal array
  149. * of pointers to the arguments.
  150. */
  151. ss_explicit_k basic_commandline_parser(char_type const* cmdLine)
  152. : m_buffer(1 + stlsoft_ns_qual(c_str_len)(cmdLine))
  153. , m_pointers(0)
  154. {
  155. init_(cmdLine, m_buffer.size() - 1);
  156. }
  157. /** Parses the given command-line and creates an internal array
  158. * of pointers to the arguments.
  159. */
  160. ss_explicit_k basic_commandline_parser(char_type const* cmdLine, size_type len)
  161. : m_buffer(1 + len)
  162. , m_pointers(0)
  163. {
  164. init_(cmdLine, len);
  165. }
  166. private:
  167. void init_(char_type const* cmdLine, size_type len)
  168. {
  169. WINSTL_MESSAGE_ASSERT("command-line may not be NULL, unless length is 0", (0 == len) || (NULL != cmdLine));
  170. traits_type::copy(&m_buffer[0], cmdLine, len);
  171. m_buffer[len] = '\0';
  172. // Here's the algorithm:
  173. //
  174. // Walk the string, mindful of quotes, remembering the start of an
  175. // argument, and writing the nul-character into spaces.
  176. enum state_t
  177. {
  178. argument
  179. , quotedArgumentStart
  180. , quotedArgument
  181. , space
  182. };
  183. state_t state = space;
  184. iterator b = m_buffer.begin();
  185. const iterator e = m_buffer.end() - 1;
  186. for(; b != e; ++b)
  187. {
  188. const char_type ch = *b;
  189. WINSTL_ASSERT('\0' != ch);
  190. if('"' == ch)
  191. {
  192. if(quotedArgumentStart == state)
  193. {
  194. state = space;
  195. }
  196. else if(quotedArgument == state)
  197. {
  198. *b = '\0';
  199. state = space;
  200. }
  201. else if(space == state)
  202. {
  203. state = quotedArgumentStart;
  204. }
  205. else
  206. {
  207. }
  208. }
  209. else if(isspace(ch))
  210. {
  211. if(quotedArgumentStart == state)
  212. {
  213. state = quotedArgument;
  214. add_pointer(&*b);
  215. }
  216. else if(quotedArgument == state)
  217. {
  218. }
  219. else if(space == state)
  220. {
  221. }
  222. else
  223. {
  224. WINSTL_ASSERT(argument == state);
  225. *b = '\0';
  226. state = space;
  227. }
  228. }
  229. else
  230. {
  231. if(quotedArgumentStart == state)
  232. {
  233. state = quotedArgument;
  234. add_pointer(&*b);
  235. }
  236. else if(space == state)
  237. {
  238. state = argument;
  239. add_pointer(&*b);
  240. }
  241. }
  242. }
  243. }
  244. /// @}
  245. /// \name Accessors
  246. /// @{
  247. public:
  248. /// The number of arguments
  249. size_type size() const
  250. {
  251. return m_pointers.size();
  252. }
  253. /** Returns a non-mutating (const) pointer to each argument
  254. * string.
  255. *
  256. * \param index The index of the argument
  257. *
  258. * \note The behaviour is undefined if <code>index</code> is greater
  259. * than <code>size()</code>. If <code>index</code> is equal to
  260. * <code>size()</code>, then the returned reference may not be used,
  261. * other than to take its address (which is the address of the
  262. * <a href="http://www.extendedstl.com/glossary.html#end-element">"end-element"</a>).
  263. */
  264. value_type const& operator [](size_type index) const
  265. {
  266. WINSTL_ASSERT(index <= size());
  267. return m_pointers[index];
  268. }
  269. /// @}
  270. /// \name Iteration
  271. /// @{
  272. public:
  273. /// An iterator representing the start of the sequence
  274. const_iterator begin() const
  275. {
  276. return m_pointers.begin();
  277. }
  278. /// An iterator representing the end of the sequence
  279. const_iterator end() const
  280. {
  281. return m_pointers.end();
  282. }
  283. /// @}
  284. /// \name Implementation
  285. /// @{
  286. private:
  287. ws_bool_t add_pointer(pointer_type_ p)
  288. {
  289. if(!m_pointers.resize(1 + m_pointers.size()))
  290. {
  291. return false;
  292. }
  293. m_pointers[m_pointers.size() - 1] = p;
  294. return true;
  295. }
  296. /// @}
  297. /// \name Members
  298. /// @{
  299. private:
  300. buffer_type_ m_buffer;
  301. pointers_type_ m_pointers;
  302. /// @}
  303. };
  304. /* /////////////////////////////////////////////////////////////////////////
  305. * Typedefs for commonly encountered types
  306. */
  307. /** Specialisation of the basic_commandline_parser template for the ANSI character type \c char
  308. *
  309. * \ingroup group__library__system
  310. */
  311. typedef basic_commandline_parser<ws_char_a_t> commandline_parser_a;
  312. /** Specialisation of the basic_commandline_parser template for the Unicode character type \c wchar_t
  313. *
  314. * \ingroup group__library__system
  315. */
  316. typedef basic_commandline_parser<ws_char_w_t> commandline_parser_w;
  317. /** Specialisation of the basic_commandline_parser template for the Win32 character type \c TCHAR
  318. *
  319. * \ingroup group__library__system
  320. */
  321. typedef basic_commandline_parser<TCHAR> commandline_parser;
  322. ////////////////////////////////////////////////////////////////////////////
  323. // Unit-testing
  324. #ifdef STLSOFT_UNITTEST
  325. # include "./unittest/commandline_parser_unittest_.h"
  326. #endif /* STLSOFT_UNITTEST */
  327. /* ////////////////////////////////////////////////////////////////////// */
  328. #ifndef _WINSTL_NO_NAMESPACE
  329. # if defined(_STLSOFT_NO_NAMESPACE) || \
  330. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  331. } // namespace winstl
  332. # else
  333. } // namespace winstl_project
  334. } // namespace stlsoft
  335. # endif /* _STLSOFT_NO_NAMESPACE */
  336. #endif /* !_WINSTL_NO_NAMESPACE */
  337. /* ////////////////////////////////////////////////////////////////////// */
  338. #endif /* WINSTL_INCL_WINSTL_SYSTEM_HPP_COMMANDLINE_PARSER */
  339. /* ///////////////////////////// end of file //////////////////////////// */