1717#include < vector>
1818// [CLI11:public_includes:end]
1919
20+ #include " Macros.hpp"
21+
2022namespace CLI {
2123
2224// [CLI11:string_tools_hpp:verbatim]
@@ -43,21 +45,7 @@ namespace detail {
4345constexpr int expected_max_vector_size{1 << 29 };
4446// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
4547// / Split a string by a delim
46- inline std::vector<std::string> split (const std::string &s, char delim) {
47- std::vector<std::string> elems;
48- // Check to see if empty string, give consistent result
49- if (s.empty ()) {
50- elems.emplace_back ();
51- } else {
52- std::stringstream ss;
53- ss.str (s);
54- std::string item;
55- while (std::getline (ss, item, delim)) {
56- elems.push_back (item);
57- }
58- }
59- return elems;
60- }
48+ CLI11_INLINE std::vector<std::string> split (const std::string &s, char delim);
6149
6250// / Simple function to join a string
6351template <typename T> std::string join (const T &v, std::string delim = " ," ) {
@@ -106,33 +94,16 @@ template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
10694// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
10795
10896// / Trim whitespace from left of string
109- inline std::string <rim (std::string &str) {
110- auto it = std::find_if (str.begin (), str.end (), [](char ch) { return !std::isspace<char >(ch, std::locale ()); });
111- str.erase (str.begin (), it);
112- return str;
113- }
97+ CLI11_INLINE std::string <rim (std::string &str);
11498
11599// / Trim anything from left of string
116- inline std::string <rim (std::string &str, const std::string &filter) {
117- auto it = std::find_if (str.begin (), str.end (), [&filter](char ch) { return filter.find (ch) == std::string::npos; });
118- str.erase (str.begin (), it);
119- return str;
120- }
100+ CLI11_INLINE std::string <rim (std::string &str, const std::string &filter);
121101
122102// / Trim whitespace from right of string
123- inline std::string &rtrim (std::string &str) {
124- auto it = std::find_if (str.rbegin (), str.rend (), [](char ch) { return !std::isspace<char >(ch, std::locale ()); });
125- str.erase (it.base (), str.end ());
126- return str;
127- }
103+ CLI11_INLINE std::string &rtrim (std::string &str);
128104
129105// / Trim anything from right of string
130- inline std::string &rtrim (std::string &str, const std::string &filter) {
131- auto it =
132- std::find_if (str.rbegin (), str.rend (), [&filter](char ch) { return filter.find (ch) == std::string::npos; });
133- str.erase (it.base (), str.end ());
134- return str;
135- }
106+ CLI11_INLINE std::string &rtrim (std::string &str, const std::string &filter);
136107
137108// / Trim whitespace from string
138109inline std::string &trim (std::string &str) { return ltrim (rtrim (str)); }
@@ -147,72 +118,25 @@ inline std::string trim_copy(const std::string &str) {
147118}
148119
149120// / remove quotes at the front and back of a string either '"' or '\''
150- inline std::string &remove_quotes (std::string &str) {
151- if (str.length () > 1 && (str.front () == ' "' || str.front () == ' \' ' )) {
152- if (str.front () == str.back ()) {
153- str.pop_back ();
154- str.erase (str.begin (), str.begin () + 1 );
155- }
156- }
157- return str;
158- }
121+ CLI11_INLINE std::string &remove_quotes (std::string &str);
159122
160123// / Add a leader to the beginning of all new lines (nothing is added
161124// / at the start of the first line). `"; "` would be for ini files
162125// /
163126// / Can't use Regex, or this would be a subs.
164- inline std::string fix_newlines (const std::string &leader, std::string input) {
165- std::string::size_type n = 0 ;
166- while (n != std::string::npos && n < input.size ()) {
167- n = input.find (' \n ' , n);
168- if (n != std::string::npos) {
169- input = input.substr (0 , n + 1 ) + leader + input.substr (n + 1 );
170- n += leader.size ();
171- }
172- }
173- return input;
174- }
127+ CLI11_INLINE std::string fix_newlines (const std::string &leader, std::string input);
175128
176129// / Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
177130inline std::string trim_copy (const std::string &str, const std::string &filter) {
178131 std::string s = str;
179132 return trim (s, filter);
180133}
181134// / Print a two part "help" string
182- inline std::ostream &format_help (std::ostream &out, std::string name, const std::string &description, std::size_t wid) {
183- name = " " + name;
184- out << std::setw (static_cast <int >(wid)) << std::left << name;
185- if (!description.empty ()) {
186- if (name.length () >= wid)
187- out << " \n " << std::setw (static_cast <int >(wid)) << " " ;
188- for (const char c : description) {
189- out.put (c);
190- if (c == ' \n ' ) {
191- out << std::setw (static_cast <int >(wid)) << " " ;
192- }
193- }
194- }
195- out << " \n " ;
196- return out;
197- }
135+ CLI11_INLINE std::ostream &
136+ format_help (std::ostream &out, std::string name, const std::string &description, std::size_t wid);
198137
199138// / Print subcommand aliases
200- inline std::ostream &format_aliases (std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
201- if (!aliases.empty ()) {
202- out << std::setw (static_cast <int >(wid)) << " aliases: " ;
203- bool front = true ;
204- for (const auto &alias : aliases) {
205- if (!front) {
206- out << " , " ;
207- } else {
208- front = false ;
209- }
210- out << detail::fix_newlines (" " , alias);
211- }
212- out << " \n " ;
213- }
214- return out;
215- }
139+ CLI11_INLINE std::ostream &format_aliases (std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid);
216140
217141// / Verify the first character of an option
218142// / - is a trigger character, ! has special meaning and new lines would just be annoying to deal with
@@ -227,16 +151,7 @@ template <typename T> bool valid_later_char(T c) {
227151}
228152
229153// / Verify an option/subcommand name
230- inline bool valid_name_string (const std::string &str) {
231- if (str.empty () || !valid_first_char (str[0 ])) {
232- return false ;
233- }
234- auto e = str.end ();
235- for (auto c = str.begin () + 1 ; c != e; ++c)
236- if (!valid_later_char (*c))
237- return false ;
238- return true ;
239- }
154+ CLI11_INLINE bool valid_name_string (const std::string &str);
240155
241156// / Verify an app name
242157inline bool valid_alias_name_string (const std::string &str) {
@@ -270,66 +185,20 @@ inline std::string remove_underscore(std::string str) {
270185}
271186
272187// / Find and replace a substring with another substring
273- inline std::string find_and_replace (std::string str, std::string from, std::string to) {
274-
275- std::size_t start_pos = 0 ;
276-
277- while ((start_pos = str.find (from, start_pos)) != std::string::npos) {
278- str.replace (start_pos, from.length (), to);
279- start_pos += to.length ();
280- }
281-
282- return str;
283- }
188+ CLI11_INLINE std::string find_and_replace (std::string str, std::string from, std::string to);
284189
285190// / check if the flag definitions has possible false flags
286191inline bool has_default_flag_values (const std::string &flags) {
287192 return (flags.find_first_of (" {!" ) != std::string::npos);
288193}
289194
290- inline void remove_default_flag_values (std::string &flags) {
291- auto loc = flags.find_first_of (' {' , 2 );
292- while (loc != std::string::npos) {
293- auto finish = flags.find_first_of (" }," , loc + 1 );
294- if ((finish != std::string::npos) && (flags[finish] == ' }' )) {
295- flags.erase (flags.begin () + static_cast <std::ptrdiff_t >(loc),
296- flags.begin () + static_cast <std::ptrdiff_t >(finish) + 1 );
297- }
298- loc = flags.find_first_of (' {' , loc + 1 );
299- }
300- flags.erase (std::remove (flags.begin (), flags.end (), ' !' ), flags.end ());
301- }
195+ CLI11_INLINE void remove_default_flag_values (std::string &flags);
302196
303197// / Check if a string is a member of a list of strings and optionally ignore case or ignore underscores
304- inline std::ptrdiff_t find_member (std::string name,
305- const std::vector<std::string> names,
306- bool ignore_case = false ,
307- bool ignore_underscore = false ) {
308- auto it = std::end (names);
309- if (ignore_case) {
310- if (ignore_underscore) {
311- name = detail::to_lower (detail::remove_underscore (name));
312- it = std::find_if (std::begin (names), std::end (names), [&name](std::string local_name) {
313- return detail::to_lower (detail::remove_underscore (local_name)) == name;
314- });
315- } else {
316- name = detail::to_lower (name);
317- it = std::find_if (std::begin (names), std::end (names), [&name](std::string local_name) {
318- return detail::to_lower (local_name) == name;
319- });
320- }
321-
322- } else if (ignore_underscore) {
323- name = detail::remove_underscore (name);
324- it = std::find_if (std::begin (names), std::end (names), [&name](std::string local_name) {
325- return detail::remove_underscore (local_name) == name;
326- });
327- } else {
328- it = std::find (std::begin (names), std::end (names), name);
329- }
330-
331- return (it != std::end (names)) ? (it - std::begin (names)) : (-1 );
332- }
198+ CLI11_INLINE std::ptrdiff_t find_member (std::string name,
199+ const std::vector<std::string> names,
200+ bool ignore_case = false ,
201+ bool ignore_underscore = false );
333202
334203// / Find a trigger string and call a modify callable function that takes the current string and starting position of the
335204// / trigger and returns the position in the string to search for the next trigger string
@@ -343,88 +212,23 @@ template <typename Callable> inline std::string find_and_modify(std::string str,
343212
344213// / Split a string '"one two" "three"' into 'one two', 'three'
345214// / Quote characters can be ` ' or "
346- inline std::vector<std::string> split_up (std::string str, char delimiter = ' \0 ' ) {
347-
348- const std::string delims (" \'\" `" );
349- auto find_ws = [delimiter](char ch) {
350- return (delimiter == ' \0 ' ) ? std::isspace<char >(ch, std::locale ()) : (ch == delimiter);
351- };
352- trim (str);
353-
354- std::vector<std::string> output;
355- bool embeddedQuote = false ;
356- char keyChar = ' ' ;
357- while (!str.empty ()) {
358- if (delims.find_first_of (str[0 ]) != std::string::npos) {
359- keyChar = str[0 ];
360- auto end = str.find_first_of (keyChar, 1 );
361- while ((end != std::string::npos) && (str[end - 1 ] == ' \\ ' )) { // deal with escaped quotes
362- end = str.find_first_of (keyChar, end + 1 );
363- embeddedQuote = true ;
364- }
365- if (end != std::string::npos) {
366- output.push_back (str.substr (1 , end - 1 ));
367- if (end + 2 < str.size ()) {
368- str = str.substr (end + 2 );
369- } else {
370- str.clear ();
371- }
372-
373- } else {
374- output.push_back (str.substr (1 ));
375- str = " " ;
376- }
377- } else {
378- auto it = std::find_if (std::begin (str), std::end (str), find_ws);
379- if (it != std::end (str)) {
380- std::string value = std::string (str.begin (), it);
381- output.push_back (value);
382- str = std::string (it + 1 , str.end ());
383- } else {
384- output.push_back (str);
385- str = " " ;
386- }
387- }
388- // transform any embedded quotes into the regular character
389- if (embeddedQuote) {
390- output.back () = find_and_replace (output.back (), std::string (" \\ " ) + keyChar, std::string (1 , keyChar));
391- embeddedQuote = false ;
392- }
393- trim (str);
394- }
395- return output;
396- }
215+ CLI11_INLINE std::vector<std::string> split_up (std::string str, char delimiter = ' \0 ' );
397216
398217// / This function detects an equal or colon followed by an escaped quote after an argument
399218// / then modifies the string to replace the equality with a space. This is needed
400219// / to allow the split up function to work properly and is intended to be used with the find_and_modify function
401220// / the return value is the offset+1 which is required by the find_and_modify function.
402- inline std::size_t escape_detect (std::string &str, std::size_t offset) {
403- auto next = str[offset + 1 ];
404- if ((next == ' \" ' ) || (next == ' \' ' ) || (next == ' `' )) {
405- auto astart = str.find_last_of (" -/ \"\' `" , offset - 1 );
406- if (astart != std::string::npos) {
407- if (str[astart] == ((str[offset] == ' =' ) ? ' -' : ' /' ))
408- str[offset] = ' ' ; // interpret this as a space so the split_up works properly
409- }
410- }
411- return offset + 1 ;
412- }
221+ CLI11_INLINE std::size_t escape_detect (std::string &str, std::size_t offset);
413222
414223// / Add quotes if the string contains spaces
415- inline std::string &add_quotes_if_needed (std::string &str) {
416- if ((str.front () != ' "' && str.front () != ' \' ' ) || str.front () != str.back ()) {
417- char quote = str.find (' "' ) < str.find (' \' ' ) ? ' \' ' : ' "' ;
418- if (str.find (' ' ) != std::string::npos) {
419- str.insert (0 , 1 , quote);
420- str.append (1 , quote);
421- }
422- }
423- return str;
424- }
224+ CLI11_INLINE std::string &add_quotes_if_needed (std::string &str);
425225
426226} // namespace detail
427227
428228// [CLI11:string_tools_hpp:end]
429229
430230} // namespace CLI
231+
232+ #ifndef CLI11_COMPILE
233+ #include " impl/StringTools_inl.hpp"
234+ #endif
0 commit comments