1 ////////////////////////////////////////////////////////////////////////////////
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // http://www.opensource.org/licenses/mit-license.php
25 ////////////////////////////////////////////////////////////////////////////////
37 #include <sys/select.h>
45 ///////////////////////////////////////////////////////////////////////////////
47 std::vector <std::string>& lines,
48 const std::string& text,
53 unsigned int offset = 0;
54 while (extractLine (line, text, width, hyphenate, offset))
55 lines.push_back (line);
58 ////////////////////////////////////////////////////////////////////////////////
59 // Split in a separator. Two adjacent separators means empty token.
60 std::vector <std::string> split (const std::string& input, const char delimiter)
62 std::vector <std::string> results;
63 std::string::size_type start = 0;
64 std::string::size_type i;
65 while ((i = input.find (delimiter, start)) != std::string::npos)
67 results.push_back (input.substr (start, i - start));
72 results.push_back (input.substr (start));
77 ////////////////////////////////////////////////////////////////////////////////
78 // Split on words. Adjacent separators collapsed.
79 std::vector <std::string> split (const std::string& input)
81 static std::string delims = " \t\n\f\r";
82 std::vector <std::string> results;
84 std::string::size_type start = 0;
85 std::string::size_type end;
86 while ((start = input.find_first_not_of (delims, start)) != std::string::npos)
88 if ((end = input.find_first_of (delims, start)) != std::string::npos)
90 results.push_back (input.substr (start, end - start));
95 results.push_back (input.substr (start));
96 start = std::string::npos;
103 ////////////////////////////////////////////////////////////////////////////////
105 const std::string& separator,
106 const std::vector<int>& items)
109 auto size = items.size ();
110 for (unsigned int i = 0; i < size; ++i)
121 ////////////////////////////////////////////////////////////////////////////////
123 const std::string& separator,
124 const std::vector<std::string>& items)
127 auto size = items.size ();
128 for (unsigned int i = 0; i < size; ++i)
139 ////////////////////////////////////////////////////////////////////////////////
140 std::string str_replace (
141 const std::string &str,
142 const std::string& search,
143 const std::string& replacement)
145 std::string modified {str};
146 std::string::size_type pos = 0;
147 while ((pos = modified.find (search, pos)) != std::string::npos)
149 modified.replace (pos, search.length (), replacement);
150 pos += replacement.length ();
156 ////////////////////////////////////////////////////////////////////////////////
157 std::string trim (const std::string& input, const std::string& edible)
159 auto start = input.find_first_not_of (edible);
160 auto end = input.find_last_not_of (edible);
162 if (start == std::string::npos)
165 if (end == std::string::npos)
166 return input.substr (start);
168 return input.substr (start, end - start + 1);
171 ////////////////////////////////////////////////////////////////////////////////
172 std::string ltrim (const std::string& input, const std::string& edible)
174 auto start = input.find_first_not_of (edible);
175 if (start == std::string::npos)
178 return input.substr (start);
181 ////////////////////////////////////////////////////////////////////////////////
182 std::string rtrim (const std::string& input, const std::string& edible)
184 if (input.find_first_not_of (edible) == std::string::npos)
187 auto end = input.find_last_not_of (edible);
188 if (end == std::string::npos)
191 return input.substr (0, end + 1);
194 ////////////////////////////////////////////////////////////////////////////////
195 int longestWord (const std::string& input)
199 std::string::size_type i = 0;
202 while ((character = utf8_next_char (input, i)))
204 if (character == ' ')
206 if (length > longest)
212 length += mk_wcwidth (character);
215 if (length > longest)
221 ////////////////////////////////////////////////////////////////////////////////
222 int longestLine (const std::string& input)
226 std::string::size_type i = 0;
229 while ((character = utf8_next_char (input, i)))
231 if (character == '\n')
233 if (length > longest)
239 length += mk_wcwidth (character);
242 if (length > longest)
248 ////////////////////////////////////////////////////////////////////////////////
249 // Walk the input text looking for a break point. A break point is one of:
252 // - last space before 'length' characters
253 // - last punctuation (, ; . :) before 'length' characters, even if not
254 // followed by a space
255 // - first 'length' characters
257 // text "one two three\n four"
258 // bytes 0123456789012 3456789
259 // characters 1234567890a23 4567890
267 const std::string& text,
270 unsigned int& offset)
272 // Terminate processing.
273 // Note: bytes vs bytes.
274 if (offset >= text.length ())
277 std::string::size_type last_last_bytes = offset;
278 std::string::size_type last_bytes = offset;
279 std::string::size_type bytes = offset;
280 unsigned int last_ws = 0;
286 last_last_bytes = last_bytes;
288 character = utf8_next_char (text, bytes);
290 if (character == 0 ||
293 line = text.substr (offset, last_bytes - offset);
297 else if (character == ' ')
298 last_ws = last_bytes;
300 char_width = mk_wcwidth (character);
301 if (line_width + char_width > width)
303 int last_last_character = text[last_last_bytes];
304 int last_character = text[last_bytes];
306 // [case 1] one| two --> last_last != 32, last == 32, ws == 0
307 if (last_last_character != ' ' &&
308 last_character == ' ')
310 line = text.substr (offset, last_bytes - offset);
311 offset = last_bytes + 1;
315 // [case 2] one |two --> last_last == 32, last != 32, ws != 0
316 else if (last_last_character == ' ' &&
317 last_character != ' ' &&
320 line = text.substr (offset, last_bytes - offset - 1);
325 else if (last_last_character != ' ' &&
326 last_character != ' ')
328 // [case 3] one t|wo --> last_last != 32, last != 32, ws != 0
331 line = text.substr (offset, last_ws - offset);
332 offset = last_ws + 1;
335 // [case 4] on|e two --> last_last != 32, last != 32, ws == 0
340 line = text.substr (offset, last_bytes - offset - 1) + '-';
341 offset = last_last_bytes;
345 line = text.substr (offset, last_bytes - offset);
354 line_width += char_width;
361 TODO Resolve above against below, which is from Taskwarrior 2.6.0, and known to
363 ////////////////////////////////////////////////////////////////////////////////
364 // Break UTF8 text into chunks no more than width characters.
367 const std::string& text,
370 unsigned int& offset)
372 // Terminate processing.
373 if (offset >= text.length ())
378 std::string::size_type lastWordEnd {std::string::npos};
379 bool something {false};
380 std::string::size_type cursor {offset};
381 std::string::size_type prior_cursor {offset};
382 while ((character = utf8_next_char (text, cursor)))
385 if (character == '\n')
387 line = text.substr (offset, line_length);
392 if (! Lexer::isWhitespace (character))
395 if (! text[cursor] || Lexer::isWhitespace (text[cursor]))
396 lastWordEnd = prior_cursor;
399 line_length += mk_wcwidth (character);
401 if (line_length >= width)
403 // Backtrack to previous word end.
404 if (lastWordEnd != std::string::npos)
406 // Eat one WS after lastWordEnd.
407 std::string::size_type lastBreak = lastWordEnd;
408 utf8_next_char (text, lastBreak);
410 // Position offset at following char.
411 std::string::size_type nextStart = lastBreak;
412 utf8_next_char (text, nextStart);
414 line = text.substr (offset, lastBreak - offset);
419 // No backtrack, possible hyphenation.
422 line = text.substr (offset, prior_cursor - offset) + '-';
423 offset = prior_cursor;
427 // No hyphenation, just truncation.
430 line = text.substr (offset, cursor - offset);
437 prior_cursor = cursor;
443 line = text.substr (offset, cursor - offset);
452 ////////////////////////////////////////////////////////////////////////////////
454 const std::string& left,
455 const std::string& right,
456 bool sensitive /*= true*/)
458 // Use strcasecmp if required.
460 return strcasecmp (left.c_str (), right.c_str ()) == 0 ? true : false;
462 // Otherwise, just use std::string::operator==.
463 return left == right;
466 ////////////////////////////////////////////////////////////////////////////////
468 const std::string& reference,
469 const std::string& attempt,
470 unsigned int minLength /* = 0 */)
472 // An exact match is accepted first.
473 if (compare (reference, attempt, false))
476 // A partial match will suffice.
477 if (attempt.length () < reference.length () &&
478 attempt.length () >= minLength)
479 return compare (reference.substr (0, attempt.length ()), attempt, false);
484 ////////////////////////////////////////////////////////////////////////////////
486 const std::string& left,
487 const std::string& right)
492 left[pos] == right[pos])
498 ////////////////////////////////////////////////////////////////////////////////
499 std::string::size_type find (
500 const std::string& text,
501 const std::string& pattern,
504 return find (text, pattern, 0, sensitive);
507 ////////////////////////////////////////////////////////////////////////////////
508 std::string::size_type find (
509 const std::string& text,
510 const std::string& pattern,
511 std::string::size_type begin,
514 // Implement a sensitive find, which is really just a loop withing a loop,
515 // comparing lower-case versions of each character in turn.
518 // Handle empty pattern.
519 const char* p = pattern.c_str ();
520 size_t len = pattern.length ();
525 if (begin >= text.length ())
526 return std::string::npos;
528 // Evaluate these once, for performance reasons.
529 const char* start = text.c_str ();
530 const char* t = start + begin;
531 const char* end = start + text.size ();
533 for (; t <= end - len; ++t)
536 for (size_t i = 0; i < len; ++i)
537 if ((diff = tolower (t[i]) - tolower (p[i])))
540 // diff == 0 means there was no break from the loop, which only occurs
541 // when a difference is detected. Therefore, the loop terminated, and
547 return std::string::npos;
550 // Otherwise, just use std::string::find.
551 return text.find (pattern, begin);
554 ////////////////////////////////////////////////////////////////////////////////
555 std::string lowerCase (const std::string& input)
557 std::string output {input};
558 std::transform (output.begin (), output.end (), output.begin (), tolower);
562 ////////////////////////////////////////////////////////////////////////////////
563 std::string upperCase (const std::string& input)
565 std::string output {input};
566 std::transform (output.begin (), output.end (), output.begin (), toupper);
570 ////////////////////////////////////////////////////////////////////////////////
571 std::string upperCaseFirst (const std::string& input)
573 std::string output {input};
574 output[0] = toupper (output[0]);
578 ////////////////////////////////////////////////////////////////////////////////
580 const std::string& partial,
581 const std::vector<std::string>& list,
582 std::vector<std::string>& matches,
587 // Handle trivial case.
588 unsigned int length = partial.length ();
591 for (auto& item : list)
593 // An exact match is a special case. Assume there is only one exact match
594 // and return immediately.
598 matches.push_back (item);
602 // Maintain a list of partial matches.
603 else if (length >= (unsigned) minimum &&
604 length <= item.length () &&
605 partial == item.substr (0, length))
606 matches.push_back (item);
610 return matches.size ();
613 ////////////////////////////////////////////////////////////////////////////////
614 // Uses std::getline, because std::cin eats leading whitespace, and that means
615 // that if a newline is entered, std::cin eats it and never returns from the
616 // "std::cin >> answer;" line, but it does display the newline. This way, with
617 // std::getline, the newline can be detected, and the prompt re-written.
618 static void signal_handler (int s)
622 std::cout << "\n\nInterrupted: No changes made.\n";
627 bool confirm (const std::string& question)
629 std::vector <std::string> options {"yes", "no"};
630 std::vector <std::string> matches;
632 signal (SIGINT, signal_handler);
636 std::cout << question
639 std::string answer {""};
640 std::getline (std::cin, answer);
641 answer = std::cin.eof () ? "no" : lowerCase (trim (answer));
643 autoComplete (answer, options, matches, 1); // Hard-coded 1.
645 while (! std::cin.eof () && matches.size () != 1);
647 signal (SIGINT, SIG_DFL);
648 return matches.size () == 1 && matches[0] == "yes" ? true : false;
651 ////////////////////////////////////////////////////////////////////////////////
652 // Run a binary with args, capturing output.
654 const std::string& executable,
655 const std::vector <std::string>& args,
656 const std::string& input,
663 int select_retval, read_retval, write_retval;
665 unsigned int written;
666 const char* input_cstr = input.c_str ();
668 if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) // Handled locally with EPIPE.
669 throw std::string (std::strerror (errno));
671 if (pipe (pin) == -1)
672 throw std::string (std::strerror (errno));
674 if (pipe (pout) == -1)
675 throw std::string (std::strerror (errno));
677 if ((pid = fork ()) == -1)
678 throw std::string (std::strerror (errno));
682 // This is only reached in the child
683 close (pin[1]); // Close the write end of the input pipe.
684 close (pout[0]); // Close the read end of the output pipe.
686 // Parent writes to pin[1]. Set read end pin[0] as STDIN for child.
687 if (dup2 (pin[0], STDIN_FILENO) == -1)
688 throw std::string (std::strerror (errno));
691 // Parent reads from pout[0]. Set write end pout[1] as STDOUT for child.
692 if (dup2 (pout[1], STDOUT_FILENO) == -1)
693 throw std::string (std::strerror (errno));
696 // Add executable as argv[0] and NULL-terminate the array for execvp().
697 char** argv = new char* [args.size () + 2];
698 argv[0] = (char*) executable.c_str ();
699 for (unsigned int i = 0; i < args.size (); ++i)
700 argv[i+1] = (char*) args[i].c_str ();
702 argv[args.size () + 1] = NULL;
703 _exit (execvp (executable.c_str (), argv));
706 // This is only reached in the parent
707 close (pin[0]); // Close the read end of the input pipe.
708 close (pout[1]); // Close the write end of the output pipe.
710 if (input.size () == 0)
712 // Nothing to send to the child, close the pipe early.
719 while (read_retval != 0 || input.size () != written)
722 if (read_retval != 0)
723 FD_SET (pout[0], &rfds);
726 if (input.size () != written)
727 FD_SET (pin[1], &wfds);
729 // On Linux, tv may be overwritten by select(). Reset it each time.
730 // NOTE: Timeout chosen arbitrarily - we don't time out execute() calls.
731 // select() is run over and over again unless the child exits or closes
736 select_retval = select (std::max (pout[0], pin[1]) + 1, &rfds, &wfds, NULL, &tv);
738 if (select_retval == -1)
739 throw std::string (std::strerror (errno));
741 // Write data to child's STDIN
742 if (FD_ISSET (pin[1], &wfds))
744 write_retval = write (pin[1], input_cstr + written, input.size () - written);
745 if (write_retval == -1)
749 // Child died (or closed the pipe) before reading all input.
750 // We don't really care; pretend we wrote it all.
751 write_retval = input.size () - written;
755 throw std::string (std::strerror (errno));
758 written += write_retval;
760 if (written == input.size ())
762 // Let the child know that no more input is coming by closing the pipe.
767 // Read data from child's STDOUT
768 if (FD_ISSET (pout[0], &rfds))
770 read_retval = read (pout[0], &buf, sizeof (buf) - 1);
771 if (read_retval == -1)
772 throw std::string (std::strerror (errno));
774 buf[read_retval] = '\0';
779 close (pout[0]); // Close the read end of the output pipe.
782 if (wait (&status) == -1)
783 throw std::string (std::strerror (errno));
785 if (WIFEXITED (status))
787 status = WEXITSTATUS (status);
791 throw std::string ("Error: Could not get Hook exit status!");
794 if (signal (SIGPIPE, SIG_DFL) == SIG_ERR) // We're done, return to default.
795 throw std::string (std::strerror (errno));
800 ////////////////////////////////////////////////////////////////////////////////
801 std::string osName ()
805 #elif defined (SOLARIS)
807 #elif defined (CYGWIN)
809 #elif defined (HAIKU)
811 #elif defined (OPENBSD)
813 #elif defined (FREEBSD)
815 #elif defined (NETBSD)
817 #elif defined (LINUX)
819 #elif defined (KFREEBSD)
820 return "GNU/kFreeBSD";
821 #elif defined (GNUHURD)
828 ////////////////////////////////////////////////////////////////////////////////
829 // 16.8 Predefined macro names [cpp.predefined]
831 // The following macro names shall be defined by the implementation:
834 // The name __cplusplus is defined to the value 201402L when compiling a C++
835 // translation unit.156
838 // 156) It is intended that future versions of this standard will replace the
839 // value of this macro with a greater value. Non-conforming compilers should
840 // use a value with at most five decimal digits.
841 std::string cppCompliance ()
844 auto level = __cplusplus;
846 if (level == 199711) return "C++98/03";
847 else if (level == 201103) return "C++11";
848 else if (level == 201402) return "C++14";
850 // This is a hack. Replace with correct value on standard publication.
851 else if (level > 201700) return "C++17";
853 // Unknown, just show the value.
854 else if (level > 99999) return format (__cplusplus);
858 return "non-compliant";
861 ////////////////////////////////////////////////////////////////////////////////