├── .gitignore ├── download.png ├── preshell.png ├── README.md ├── .travis.yml ├── lib ├── CMakeLists.txt └── core │ ├── default_include.hpp │ ├── default_sysinclude.hpp │ ├── default_defines.hpp │ ├── version_desc.hpp │ ├── CMakeLists.txt │ ├── macro_map.cpp │ ├── util.cpp │ ├── macro_definition.cpp │ ├── config.cpp │ ├── version.cpp │ ├── indenter.cpp │ ├── wave_context.cpp │ ├── context.cpp │ ├── preshell_preprocessing_hooks.cpp │ ├── parse_config.cpp │ ├── shell.cpp │ └── preshell.cpp ├── test ├── main.cpp ├── test_util.cpp ├── test_util.hpp ├── CMakeLists.txt ├── temp_header.hpp ├── is_pragma_usage.cpp ├── temp_header.cpp ├── test_shell.hpp ├── parse_config.cpp ├── join.cpp ├── test_shell.cpp ├── definition_logging.cpp ├── history.cpp ├── clang_compatibility.cpp ├── replay_history.cpp ├── shell.cpp └── preshell.cpp ├── include ├── preshell │ ├── string.hpp │ ├── version.hpp │ ├── file_position.hpp │ ├── if_state.hpp │ ├── token.hpp │ ├── macro_map.hpp │ ├── parse_config.hpp │ ├── util.hpp │ ├── result.hpp │ ├── context.hpp │ ├── indenter.hpp │ ├── config.hpp │ ├── wave_context_workaround.hpp │ ├── wave_context.hpp │ ├── shell.hpp │ ├── macro_definition.hpp │ ├── preshell.hpp │ └── preshell_preprocessing_hooks.hpp └── just │ ├── console.hpp │ ├── test.hpp │ └── process.hpp ├── app ├── interrupt_handler_override.hpp ├── override_guard.hpp ├── CMakeLists.txt ├── main.cpp ├── readline_shell.hpp ├── interrupt_handler_override.cpp └── readline_shell.cpp ├── tools ├── gcc_builtin_macros ├── gcc_default_path └── gen_toc ├── cmake └── Modules │ └── FindReadline.cmake └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sabel83/preshell/master/download.png -------------------------------------------------------------------------------- /preshell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sabel83/preshell/master/preshell.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Preshell 2 | 3 | Preshell is no longer maintained. Its functionality has been migrated to 4 | [Metashell](https://github.com/sabel83/metashell). You can find a tutorial for 5 | getting started with Macro debugging in Metashell at 6 | [http://metashell.readthedocs.org/en/latest/manual/getting_started/#testing-macros](http://metashell.readthedocs.org/en/latest/manual/getting_started/#testing-macros) 7 | 8 | The last commit of Preshell is 9 | [here](https://github.com/sabel83/preshell/tree/b659d2b6f709128a4754a47c63476d81e067e378). 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get install libboost1.48-dev libboost-test1.48-dev libboost-program-options1.48-dev libboost-wave1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-filesystem1.48-dev libreadline-dev 7 | script: 8 | # Test the code 9 | - tools/gcc_builtin_macros > lib/core/default_defines.hpp 10 | - tools/gcc_default_path > lib/core/default_sysinclude.hpp 11 | - mkdir bin 12 | - cd bin 13 | - cmake .. -DCMAKE_CXX_FLAGS:STRING="-Wall -Werror -pedantic" 14 | - make 15 | - make test 16 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2015, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | subdirs(core) 18 | 19 | -------------------------------------------------------------------------------- /lib/core/default_include.hpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | -------------------------------------------------------------------------------- /lib/core/default_sysinclude.hpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | -------------------------------------------------------------------------------- /lib/core/default_defines.hpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | "" 18 | 19 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | JUST_TEST_DEFINE_MAIN 20 | 21 | -------------------------------------------------------------------------------- /lib/core/version_desc.hpp: -------------------------------------------------------------------------------- 1 | // Metashell - Interactive C++ template metaprogramming shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // You can put a message here that appears under the version number 18 | // in the splash message. 19 | "" 20 | 21 | -------------------------------------------------------------------------------- /lib/core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | aux_source_directory(. SOURCES) 18 | add_library(preshell_core_lib STATIC ${SOURCES}) 19 | 20 | # Readline 21 | include_directories(${READLINE_INCLUDE_DIR}) 22 | 23 | -------------------------------------------------------------------------------- /test/test_util.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_util.hpp" 18 | 19 | #include 20 | 21 | void should_not_be_called(std::string) 22 | { 23 | JUST_ASSERT(false); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /test/test_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_TEST_UTIL_HPP 2 | #define PRESHELL_TEST_UTIL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | void should_not_be_called(std::string); 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /include/preshell/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_STRING_HPP 2 | #define PRESHELL_STRING_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | namespace preshell 23 | { 24 | typedef token::string_type string; 25 | } 26 | 27 | #endif 28 | 29 | -------------------------------------------------------------------------------- /include/preshell/version.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_VERSION_HPP 2 | #define PRESHELL_VERSION_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | namespace preshell 23 | { 24 | std::string version(); 25 | 26 | std::string wave_version(); 27 | std::string readline_version(); 28 | } 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /include/preshell/file_position.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_FILE_POSTIION_HPP 2 | #define PRESHELL_FILE_POSTIION_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | namespace preshell 23 | { 24 | typedef boost::wave::util::file_position_type file_position; 25 | } 26 | 27 | #endif 28 | 29 | -------------------------------------------------------------------------------- /include/preshell/if_state.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_IF_STATE_HPP 2 | #define PRESHELL_IF_STATE_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | namespace preshell 21 | { 22 | enum if_state 23 | { 24 | if0_then = 0, // 00 25 | if0_else = 1, // 01 26 | if1_then = 2, // 10 27 | if1_else = 3 // 11 28 | }; 29 | } 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /include/preshell/token.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_TOKEN_HPP 2 | #define PRESHELL_TOKEN_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | 24 | namespace preshell 25 | { 26 | typedef boost::wave::cpplexer::lex_token token; 27 | } 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /app/interrupt_handler_override.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERRUPT_HANDLER_OVERRIDE_HPP 2 | #define INTERRUPT_HANDLER_OVERRIDE_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | 23 | class interrupt_handler_override : boost::noncopyable 24 | { 25 | public: 26 | interrupt_handler_override(const boost::function& handler_); 27 | ~interrupt_handler_override(); 28 | }; 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /include/preshell/macro_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_MACRO_MAP_HPP 2 | #define PRESHELL_MACRO_MAP_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace preshell 28 | { 29 | typedef std::map macro_map; 30 | 31 | std::ostream& operator<<(std::ostream& o_, const macro_map& m_); 32 | } 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /lib/core/macro_map.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | std::ostream& preshell::operator<<(std::ostream& o_, const macro_map& m_) 24 | { 25 | o_ << "macro_map{" << std::endl; 26 | 27 | for (macro_map::const_iterator i = m_.begin(), e = m_.end(); i != e; ++i) 28 | { 29 | o_ << " {" << i->first << ", " << i->second << "}" << std::endl; 30 | } 31 | 32 | return o_ << "}"; 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | aux_source_directory(. SOURCES) 18 | add_executable(preshell_test ${SOURCES}) 19 | 20 | target_link_libraries(preshell_test 21 | preshell_core_lib 22 | ${Boost_SYSTEM_LIBRARY} 23 | ${Boost_THREAD_LIBRARY} 24 | ${Boost_FILESYSTEM_LIBRARY} 25 | ${Boost_WAVE_LIBRARY} 26 | ${CMAKE_THREAD_LIBS_INIT} 27 | ) 28 | 29 | # Program_options 30 | target_link_libraries(preshell_test ${Boost_PROGRAM_OPTIONS_LIBRARIES}) 31 | 32 | add_test(preshell_unit_tests preshell_test) 33 | 34 | -------------------------------------------------------------------------------- /app/override_guard.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_OVERRIDE_GUARD_HPP 2 | #define PRESHELL_OVERRIDE_GUARD_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | template 23 | class override_guard : boost::noncopyable 24 | { 25 | public: 26 | override_guard(T& var_, T new_value_) : 27 | _var(var_), 28 | _old_value(_var) 29 | { 30 | _var = new_value_; 31 | } 32 | 33 | ~override_guard() 34 | { 35 | _var = _old_value; 36 | } 37 | private: 38 | T& _var; 39 | T _old_value; 40 | }; 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /test/temp_header.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_TEST_TEMP_HEADER_HPP 2 | #define PRESHELL_TEST_TEMP_HEADER_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | class temp_header : boost::noncopyable 27 | { 28 | public: 29 | temp_header(); 30 | ~temp_header(); 31 | 32 | void create(const std::string& content_); 33 | 34 | std::string include_dir() const; 35 | std::string include_command() const; 36 | private: 37 | boost::filesystem::path _path; 38 | 39 | std::string filename() const; 40 | }; 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | aux_source_directory(. SOURCES) 18 | add_executable(preshell ${SOURCES}) 19 | 20 | # Readline 21 | include_directories(${READLINE_INCLUDE_DIR}) 22 | target_link_libraries(preshell ${READLINE_LIBRARY}) 23 | 24 | # Preshell 25 | target_link_libraries(preshell preshell_core_lib) 26 | 27 | # Wave 28 | target_link_libraries(preshell 29 | ${Boost_SYSTEM_LIBRARIES} 30 | ${Boost_THREAD_LIBRARIES} 31 | ${Boost_FILESYSTEM_LIBRARIES} 32 | ${Boost_WAVE_LIBRARIES} 33 | ${CMAKE_THREAD_LIBS_INIT} 34 | ) 35 | 36 | # Program_options 37 | target_link_libraries(preshell ${Boost_PROGRAM_OPTIONS_LIBRARIES}) 38 | 39 | install(TARGETS preshell DESTINATION bin) 40 | 41 | -------------------------------------------------------------------------------- /include/preshell/parse_config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_PARSE_CONFIG_HPP 2 | #define PRESHELL_PARSE_CONFIG_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace preshell 27 | { 28 | enum parse_config_result 29 | { 30 | run_shell, 31 | exit_with_error, 32 | exit_without_error 33 | }; 34 | 35 | parse_config_result parse_config( 36 | config& cfg_, 37 | std::vector& macros_, 38 | std::vector& preprocess_, 39 | 40 | int argc_, 41 | const char* argv_[], 42 | 43 | std::ostream* out_ = 0, 44 | std::ostream* err_ = 0 45 | ); 46 | } 47 | 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /include/preshell/util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_UTIL_HPP 2 | #define PRESHELL_UTIL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace preshell 25 | { 26 | template 27 | unsigned int always() 28 | { 29 | return N; 30 | } 31 | 32 | void throw_away(std::string); 33 | 34 | template 35 | static std::string token_list_to_string(Iterator begin_, Iterator end_) 36 | { 37 | std::ostringstream s; 38 | for (Iterator i = begin_; i != end_; ++i) 39 | { 40 | s << i->get_value(); 41 | } 42 | return s.str(); 43 | } 44 | 45 | bool is_pragma_usage(const std::string& s_); 46 | } 47 | 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /test/is_pragma_usage.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | JUST_TEST_CASE(test_no_pragma_usage) 24 | { 25 | JUST_ASSERT(!is_pragma_usage("hello")); 26 | } 27 | 28 | JUST_TEST_CASE(test_pragma_usage) 29 | { 30 | JUST_ASSERT(is_pragma_usage("#pragma hello")); 31 | } 32 | 33 | JUST_TEST_CASE(test_pragma_with_space) 34 | { 35 | JUST_ASSERT(is_pragma_usage("# pragma hello")); 36 | JUST_ASSERT(is_pragma_usage(" #pragma hello")); 37 | } 38 | 39 | JUST_TEST_CASE(test_pragma_in_comment) 40 | { 41 | JUST_ASSERT(!is_pragma_usage("/* #pragma hello */")); 42 | JUST_ASSERT(!is_pragma_usage("// #pragma hello")); 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /include/preshell/result.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_RESULT_HPP 2 | #define PRESHELL_RESULT_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | namespace preshell 27 | { 28 | struct result 29 | { 30 | std::string output; 31 | std::string error; 32 | std::string info; 33 | 34 | context pp_context; 35 | 36 | bool replay_history; 37 | 38 | result() : replay_history(false) {} 39 | result(const context& ctx_) : 40 | output(), 41 | error(), 42 | pp_context(ctx_), 43 | replay_history(false) 44 | {} 45 | }; 46 | 47 | typedef boost::shared_ptr result_ptr; 48 | } 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /test/temp_header.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "temp_header.hpp" 18 | 19 | #include 20 | #include 21 | 22 | temp_header::temp_header() : 23 | _path(absolute(boost::filesystem::unique_path())) 24 | {} 25 | 26 | temp_header::~temp_header() 27 | { 28 | remove(_path); 29 | } 30 | 31 | void temp_header::create(const std::string& content_) 32 | { 33 | std::ofstream f(filename().c_str()); 34 | f << content_ << std::endl; 35 | } 36 | 37 | std::string temp_header::include_dir() const 38 | { 39 | return _path.parent_path().native(); 40 | } 41 | 42 | std::string temp_header::filename() const 43 | { 44 | return _path.filename().native(); 45 | } 46 | 47 | std::string temp_header::include_command() const 48 | { 49 | return "#include <" + filename() + ">\n"; 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /lib/core/util.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | using namespace preshell; 25 | 26 | void preshell::throw_away(std::string) 27 | {} 28 | 29 | bool preshell::is_pragma_usage(const std::string& s_) 30 | { 31 | using boost::wave::cpplexer::lex_iterator; 32 | using boost::wave::language_support; 33 | 34 | const file_position pos(""); 35 | const language_support lng = boost::wave::support_cpp; 36 | const lex_iterator e; 37 | const std::string s(s_ + "\n"); 38 | return 39 | std::find( 40 | lex_iterator(s.begin(), s.end(), pos, lng), 41 | e, 42 | boost::wave::T_PP_PRAGMA 43 | ) != e; 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/core/macro_definition.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | using namespace preshell; 26 | 27 | std::ostream& preshell::operator<<(std::ostream& o_, const macro_definition& m_) 28 | { 29 | using boost::algorithm::join; 30 | using boost::adaptors::transformed; 31 | using boost::bind; 32 | 33 | if (m_.has_parameters()) 34 | { 35 | o_ 36 | << "(" 37 | << join(m_.parameters() | transformed(bind(&token::get_value, _1)), ", ") 38 | << ") "; 39 | } 40 | 41 | return o_ 42 | << join(m_.definition() | transformed(bind(&token::get_value, _1)), "") 43 | << " @ " << m_.position(); 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/main.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "readline_shell.hpp" 18 | 19 | #include 20 | 21 | #include 22 | 23 | int main(int argc_, const char* argv_[]) 24 | { 25 | preshell::config config = preshell::config::default_config; 26 | std::vector macros; 27 | std::vector preprocess; 28 | 29 | const preshell::parse_config_result r = 30 | preshell::parse_config( 31 | config, 32 | macros, 33 | preprocess, 34 | 35 | argc_, argv_, 36 | &std::cout, &std::cerr 37 | ); 38 | 39 | if (r == preshell::run_shell) 40 | { 41 | readline_shell shell(config, macros); 42 | 43 | shell.display_splash(); 44 | 45 | BOOST_FOREACH(const std::string& s, preprocess) 46 | { 47 | shell.line_available(s); 48 | } 49 | 50 | shell.run(); 51 | } 52 | 53 | return r == preshell::exit_with_error ? 1 : 0; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /lib/core/config.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | using namespace preshell; 20 | 21 | namespace 22 | { 23 | const char* default_includes[] = 24 | { 25 | "" 26 | #include "default_include.hpp" 27 | }; 28 | 29 | const char* default_sysincludes[] = 30 | { 31 | "" 32 | #include "default_sysinclude.hpp" 33 | }; 34 | } 35 | 36 | config::config() : 37 | log_macro_definitions(false), 38 | enable_warnings(true), 39 | suppress_empty_lines_in_output(false), 40 | enable_save_history(false) 41 | {} 42 | 43 | const config config::empty; 44 | 45 | const config 46 | config::default_config( 47 | default_includes + 1, 48 | default_includes + sizeof(default_includes) / sizeof(const char*), 49 | default_sysincludes + 1, 50 | default_sysincludes + sizeof(default_sysincludes) / sizeof(const char*), 51 | #include "default_defines.hpp" 52 | ); 53 | 54 | -------------------------------------------------------------------------------- /app/readline_shell.hpp: -------------------------------------------------------------------------------- 1 | #ifndef READLINE_SHELL_HPP 2 | #define READLINE_SHELL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | class readline_shell : public preshell::shell 26 | { 27 | public: 28 | readline_shell( 29 | const preshell::config& config_, 30 | const std::vector& macros_ 31 | ); 32 | virtual ~readline_shell(); 33 | 34 | virtual void add_history(const std::string& s_); 35 | 36 | virtual void display_normal(const std::string& s_) const; 37 | virtual void display_info(const std::string& s_) const; 38 | virtual void display_error(const std::string& s_) const; 39 | 40 | virtual unsigned int width() const; 41 | 42 | void run(); 43 | private: 44 | static char* tab_generator(const char* text_, int state_); 45 | static char** tab_completion(const char* text_, int start_, int end_); 46 | 47 | static readline_shell* _instance; 48 | }; 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /test/test_shell.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_TEST_TEST_SHELL_HPP 2 | #define PRESHELL_TEST_TEST_SHELL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | class test_shell : public preshell::shell 27 | { 28 | public: 29 | test_shell(const preshell::config& config_); 30 | test_shell(std::vector& history_); 31 | 32 | virtual void add_history(const std::string& s_); 33 | 34 | virtual void display_normal(const std::string& s_) const; 35 | virtual void display_info(const std::string& s_) const; 36 | virtual void display_error(const std::string& s_) const; 37 | 38 | virtual unsigned int width() const; 39 | 40 | std::string normal() const; 41 | std::string info() const; 42 | std::string error() const; 43 | private: 44 | mutable std::ostringstream _normal; 45 | mutable std::ostringstream _info; 46 | mutable std::ostringstream _error; 47 | 48 | std::vector* _history; // no ownership 49 | }; 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /test/parse_config.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace 22 | { 23 | template 24 | preshell::parse_config_result parse_config( 25 | preshell::config& cfg_, 26 | const char* (&args_)[Len] 27 | ) 28 | { 29 | std::vector ignore; 30 | return preshell::parse_config(cfg_, ignore, ignore, Len, args_); 31 | } 32 | } 33 | 34 | JUST_TEST_CASE(test_save_history_is_disabled_by_default) 35 | { 36 | const char* args[] = {"preshell"}; 37 | 38 | preshell::config cfg = preshell::config::empty; 39 | parse_config(cfg, args); 40 | 41 | JUST_ASSERT(!preshell::config::empty.enable_save_history); 42 | JUST_ASSERT(!preshell::config::default_config.enable_save_history); 43 | JUST_ASSERT(!cfg.enable_save_history); 44 | } 45 | 46 | JUST_TEST_CASE(test_enable_save_history) 47 | { 48 | const char* args[] = {"preshell", "-H"}; 49 | 50 | preshell::config cfg = preshell::config::empty; 51 | parse_config(cfg, args); 52 | 53 | JUST_ASSERT(cfg.enable_save_history); 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/interrupt_handler_override.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "interrupt_handler_override.hpp" 18 | 19 | #include 20 | 21 | #ifndef _MSC_VER 22 | #include 23 | #endif 24 | 25 | namespace 26 | { 27 | boost::function handler_function; 28 | 29 | void call_handler() 30 | { 31 | assert(handler_function); 32 | handler_function(); 33 | } 34 | 35 | #ifdef _MSC_VER 36 | void install_callback() 37 | { 38 | } 39 | 40 | void remove_callback() 41 | { 42 | } 43 | #else 44 | void (*old_handler)(int); 45 | 46 | void sigint_handler(int) 47 | { 48 | call_handler(); 49 | } 50 | 51 | void install_callback() 52 | { 53 | old_handler = signal(SIGINT, sigint_handler); 54 | } 55 | 56 | void remove_callback() 57 | { 58 | signal(SIGINT, old_handler); 59 | } 60 | #endif 61 | } 62 | 63 | interrupt_handler_override::interrupt_handler_override( 64 | const boost::function& handler_ 65 | ) 66 | { 67 | assert(!handler_function); 68 | handler_function = handler_; 69 | install_callback(); 70 | } 71 | 72 | interrupt_handler_override::~interrupt_handler_override() 73 | { 74 | remove_callback(); 75 | } 76 | 77 | -------------------------------------------------------------------------------- /include/preshell/context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_CONTEXT_HPP 2 | #define PRESHELL_CONTEXT_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace preshell 30 | { 31 | struct context 32 | { 33 | macro_map macros; 34 | std::list if_states; 35 | 36 | std::string filename; 37 | int line; 38 | 39 | context() : 40 | macros(), 41 | if_states(), 42 | filename(""), 43 | line(1) 44 | {} 45 | context(const macro_map& macros_, const std::string& filename_, int line_) : 46 | macros(macros_), 47 | if_states(), 48 | filename(filename_), 49 | line(line_) 50 | {} 51 | 52 | bool tokens_skipped() const; 53 | 54 | static context initial( 55 | const config& config_, 56 | const std::vector& macros_, 57 | indenter& info_indenter_, 58 | indenter& error_indenter_, 59 | const std::list& history_ 60 | ); 61 | }; 62 | } 63 | 64 | #endif 65 | 66 | -------------------------------------------------------------------------------- /test/join.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | using namespace preshell; 28 | 29 | namespace 30 | { 31 | std::string parse_and_output(const std::string& s_) 32 | { 33 | const boost::wave::cpplexer::lex_iterator 34 | b(s_.begin(), s_.end(), token::position_type(), boost::wave::support_cpp), 35 | e; 36 | const bool never_cancel = false; 37 | std::vector* const no_warnings = 0; 38 | return join(b, e, no_warnings, never_cancel, true); 39 | } 40 | } 41 | 42 | JUST_TEST_CASE(test_join_suppressing_empty_line) 43 | { 44 | JUST_ASSERT_EQUAL("", parse_and_output("\n")); 45 | } 46 | 47 | JUST_TEST_CASE(test_join_not_suppressing_nonempty_line) 48 | { 49 | JUST_ASSERT_EQUAL("a\n", parse_and_output("a\n")); 50 | } 51 | 52 | JUST_TEST_CASE(test_suppressing_with_cpp_comment) 53 | { 54 | JUST_ASSERT_EQUAL("// comment \nx", parse_and_output("// comment \n\nx")); 55 | } 56 | 57 | JUST_TEST_CASE(test_suppressing_with_c_comment) 58 | { 59 | JUST_ASSERT_EQUAL("// comment \nx", parse_and_output("// comment \n\nx")); 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /include/preshell/indenter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_INDENTER_HPP 2 | #define PRESHELL_INDENTER_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace preshell 27 | { 28 | class indenter 29 | { 30 | public: 31 | typedef boost::function get_width_t; 32 | typedef boost::function output_t; 33 | 34 | indenter(const get_width_t& get_width_, const output_t& out_); 35 | indenter(const indenter& base_, const output_t& out_); 36 | ~indenter(); 37 | 38 | indenter& left_align( 39 | const std::string& s_, 40 | const std::string& line_prefix_, 41 | const std::string& first_line_prefix_ 42 | ); 43 | 44 | indenter& left_align( 45 | const std::string& s_, 46 | const std::string& line_prefix_ 47 | ); 48 | 49 | indenter& raw(const std::string& s_); 50 | 51 | indenter& left_align(const std::string& s_); 52 | 53 | indenter& empty_line(); 54 | 55 | // return type is void since this should be the final call of a chain of 56 | // method calls 57 | void flush(); 58 | private: 59 | get_width_t _get_width; 60 | output_t _out; 61 | std::ostringstream _buff; 62 | }; 63 | } 64 | 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /include/preshell/config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_CONFIG_HPP 2 | #define PRESHELL_CONFIG_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | 23 | namespace preshell 24 | { 25 | class config 26 | { 27 | public: 28 | std::vector include_path; 29 | std::vector sysinclude_path; 30 | std::string builtin_macro_definitions; 31 | bool log_macro_definitions; 32 | bool enable_warnings; 33 | bool suppress_empty_lines_in_output; 34 | bool enable_save_history; 35 | 36 | static const config empty; 37 | static const config default_config; 38 | private: 39 | config(); 40 | 41 | template 42 | config( 43 | InputIt include_path_begin_, 44 | InputIt include_path_end_, 45 | InputIt sysinclude_path_begin_, 46 | InputIt sysinclude_path_end_, 47 | const char* builtin_macro_definitions_ 48 | ) : 49 | include_path(include_path_begin_, include_path_end_), 50 | sysinclude_path(sysinclude_path_begin_, sysinclude_path_end_), 51 | builtin_macro_definitions(builtin_macro_definitions_), 52 | log_macro_definitions(false), 53 | enable_warnings(true), 54 | suppress_empty_lines_in_output(false), 55 | enable_save_history(false) 56 | {} 57 | }; 58 | } 59 | 60 | #endif 61 | 62 | -------------------------------------------------------------------------------- /include/preshell/wave_context_workaround.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_WAVE_CONTEXT_WORKAROUND_HPP 2 | #define PRESHELL_WAVE_CONTEXT_WORKAROUND_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | 22 | namespace preshell 23 | { 24 | template < 25 | class Iterator, 26 | class LexIterator, 27 | class InputPolicy, 28 | class ContextPolicy 29 | > 30 | class wave_context_workaround : 31 | public boost::wave::context< 32 | Iterator, 33 | LexIterator, 34 | InputPolicy, 35 | ContextPolicy, 36 | wave_context_workaround 37 | > 38 | { 39 | private: 40 | typedef 41 | boost::wave::context< 42 | Iterator, 43 | LexIterator, 44 | InputPolicy, 45 | ContextPolicy, 46 | wave_context_workaround 47 | > 48 | base; 49 | public: 50 | wave_context_workaround( 51 | const Iterator& first_, 52 | const Iterator& last_, 53 | const char* fname_, 54 | const ContextPolicy& ctx_policy_ 55 | ) : 56 | base(first_, last_, fname_, ctx_policy_) 57 | {} 58 | 59 | const typename base::position_type& get_main_pos() const 60 | { 61 | return const_cast(this)->base::get_main_pos(); 62 | } 63 | }; 64 | } 65 | 66 | #endif 67 | 68 | -------------------------------------------------------------------------------- /test/test_shell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_shell.hpp" 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | test_shell::test_shell(const preshell::config& config_) : 24 | shell(config_, std::vector()), 25 | _history(0) 26 | { 27 | display_initialisation_diagnostic(); 28 | } 29 | 30 | test_shell::test_shell(std::vector& history_) : 31 | shell(preshell::config::default_config, std::vector()), 32 | _history(&history_) 33 | { 34 | display_initialisation_diagnostic(); 35 | } 36 | 37 | void test_shell::display_normal(const std::string& s_) const 38 | { 39 | _normal << s_; 40 | } 41 | 42 | void test_shell::display_info(const std::string& s_) const 43 | { 44 | _info << s_; 45 | } 46 | 47 | void test_shell::display_error(const std::string& s_) const 48 | { 49 | _error << s_; 50 | } 51 | 52 | unsigned int test_shell::width() const 53 | { 54 | return 80; 55 | } 56 | 57 | std::string test_shell::normal() const 58 | { 59 | return _normal.str(); 60 | } 61 | 62 | std::string test_shell::info() const 63 | { 64 | return _info.str(); 65 | } 66 | 67 | std::string test_shell::error() const 68 | { 69 | return _error.str(); 70 | } 71 | 72 | void test_shell::add_history(const std::string& s_) 73 | { 74 | if (_history) 75 | { 76 | _history->push_back(s_); 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /test/definition_logging.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | #include "test_util.hpp" 20 | 21 | #include 22 | 23 | #include 24 | 25 | using namespace preshell; 26 | 27 | namespace 28 | { 29 | indenter ind(always<80>, should_not_be_called); 30 | } 31 | 32 | JUST_TEST_CASE(test_definition_is_logged) 33 | { 34 | std::list history; 35 | config cfg = config::empty; 36 | cfg.log_macro_definitions = true; 37 | result_ptr r = precompile("#define foo bar", context(), cfg, ind, history); 38 | JUST_ASSERT_EQUAL(":1:9: #define foo\n", r->info); 39 | } 40 | 41 | JUST_TEST_CASE(test_definition_is_not_logged_when_disabled) 42 | { 43 | std::list history; 44 | config cfg = config::empty; 45 | cfg.log_macro_definitions = false; 46 | result_ptr r = precompile("#define foo bar", context(), cfg, ind, history); 47 | JUST_ASSERT_EQUAL("", r->info); 48 | } 49 | 50 | JUST_TEST_CASE(test_undefinition_is_logged) 51 | { 52 | std::list history; 53 | config cfg = config::empty; 54 | cfg.log_macro_definitions = true; 55 | result_ptr r0 = precompile("#define foo bar", context(), cfg, ind, history); 56 | 57 | result_ptr r = precompile("#undef foo", r0->pp_context, cfg, ind, history); 58 | JUST_ASSERT_EQUAL(":2:1: #undef foo\n", r->info); 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /include/preshell/wave_context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_WAVE_CONTEXT_HPP 2 | #define PRESHELL_WAVE_CONTEXT_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | namespace preshell 35 | { 36 | typedef 37 | wave_context_workaround< 38 | std::string::const_iterator, 39 | boost::wave::cpplexer::lex_iterator, 40 | boost::wave::iteration_context_policies::load_file_to_string, 41 | preshell_preprocessing_hooks 42 | > 43 | wave_context; 44 | 45 | typedef boost::shared_ptr wave_context_ptr; 46 | 47 | wave_context_ptr create_context( 48 | const std::string& input_, 49 | std::list& if_states_, 50 | const config& config_, 51 | indenter& info_indenter_, 52 | indenter& error_indenter_, 53 | const bool& log_macro_definitions_, 54 | const std::list& history_, 55 | const bool& enable_save_history_, 56 | bool& replay_history_ 57 | ); 58 | 59 | macro_map get_macros(const wave_context& context_); 60 | } 61 | 62 | #endif 63 | 64 | -------------------------------------------------------------------------------- /lib/core/version.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | using namespace preshell; 26 | 27 | namespace 28 | { 29 | template 30 | std::string remove(const std::string& s_) 31 | { 32 | std::ostringstream s; 33 | for (std::string::const_iterator i = s_.begin(), e = s_.end(); i != e; ++i) 34 | { 35 | if (*i != C) 36 | { 37 | s << *i; 38 | } 39 | } 40 | return s.str(); 41 | } 42 | } 43 | 44 | std::string preshell::wave_version() 45 | { 46 | return 47 | remove<'"'>( 48 | boost::wave::context< 49 | const char*, 50 | boost::wave::cpplexer::lex_iterator > 51 | >::get_version_string() 52 | ); 53 | } 54 | 55 | std::string preshell::readline_version() 56 | { 57 | return 58 | BOOST_PP_STRINGIZE(RL_VERSION_MAJOR) 59 | "." BOOST_PP_STRINGIZE(RL_VERSION_MINOR); 60 | } 61 | 62 | std::string preshell::version() 63 | { 64 | #ifndef PRESHELL_MAJOR 65 | #error PRESHELL_MAJOR not defined 66 | #endif 67 | 68 | #ifndef PRESHELL_MINOR 69 | #error PRESHELL_MINOR not defined 70 | #endif 71 | 72 | #ifndef PRESHELL_PATCH 73 | #error PRESHELL_PATCH not defined 74 | #endif 75 | 76 | return 77 | BOOST_PP_STRINGIZE(PRESHELL_MAJOR) 78 | "." BOOST_PP_STRINGIZE(PRESHELL_MINOR) 79 | "." BOOST_PP_STRINGIZE(PRESHELL_PATCH); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /test/history.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_shell.hpp" 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | JUST_TEST_CASE(test_empty_history) 24 | { 25 | test_shell sh(config::empty); 26 | 27 | JUST_ASSERT(sh.history().empty()); 28 | } 29 | 30 | JUST_TEST_CASE(test_one_element_history) 31 | { 32 | test_shell sh(config::empty); 33 | 34 | sh.line_available("foo"); 35 | 36 | JUST_ASSERT_EQUAL(1u, sh.history().size()); 37 | JUST_ASSERT_EQUAL("foo", sh.history().front()); 38 | } 39 | 40 | JUST_TEST_CASE(test_two_element_history) 41 | { 42 | test_shell sh(config::empty); 43 | 44 | sh.line_available("foo"); 45 | sh.line_available("bar"); 46 | 47 | JUST_ASSERT_EQUAL(2u, sh.history().size()); 48 | JUST_ASSERT_EQUAL("foo", sh.history().front()); 49 | JUST_ASSERT_EQUAL("bar", sh.history().back()); 50 | } 51 | 52 | JUST_TEST_CASE(test_repetition_in_history) 53 | { 54 | std::vector add_history_callback_log; 55 | 56 | test_shell sh(add_history_callback_log); 57 | 58 | sh.line_available("foo"); 59 | sh.line_available("foo"); 60 | 61 | JUST_ASSERT_EQUAL(2u, sh.history().size()); 62 | JUST_ASSERT_EQUAL(1u, add_history_callback_log.size()); 63 | } 64 | 65 | JUST_TEST_CASE(test_empty_line_in_history) 66 | { 67 | test_shell sh(config::empty); 68 | 69 | sh.line_available(""); 70 | 71 | JUST_ASSERT(sh.history().empty()); 72 | } 73 | 74 | JUST_TEST_CASE(test_pragma_in_history) 75 | { 76 | test_shell sh(config::empty); 77 | 78 | sh.line_available("#pragma wave preshell_help"); 79 | 80 | JUST_ASSERT(sh.history().empty()); 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /test/clang_compatibility.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_shell.hpp" 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | JUST_TEST_CASE(test_has_builtin) 24 | { 25 | test_shell sh(config::default_config); 26 | 27 | sh.line_available("#if __has_builtin(__builtin_trap)"); 28 | 29 | JUST_ASSERT_EQUAL("", sh.error()); 30 | } 31 | 32 | JUST_TEST_CASE(test_has_feature) 33 | { 34 | test_shell sh(config::default_config); 35 | 36 | sh.line_available("#if __has_feature(cxx_rvalue_references)"); 37 | 38 | JUST_ASSERT_EQUAL("", sh.error()); 39 | } 40 | 41 | JUST_TEST_CASE(test_has_extension) 42 | { 43 | test_shell sh(config::default_config); 44 | 45 | sh.line_available("#if __has_extension(cxx_rvalue_references)"); 46 | 47 | JUST_ASSERT_EQUAL("", sh.error()); 48 | } 49 | 50 | JUST_TEST_CASE(test_has_attribute) 51 | { 52 | test_shell sh(config::default_config); 53 | 54 | sh.line_available("#if __has_attribute(always_inline)"); 55 | 56 | JUST_ASSERT_EQUAL("", sh.error()); 57 | } 58 | 59 | JUST_TEST_CASE(test_has_include) 60 | { 61 | test_shell sh(config::default_config); 62 | 63 | sh.line_available("#if __has_include(\"myinclude.h\")"); 64 | 65 | JUST_ASSERT_EQUAL("", sh.error()); 66 | } 67 | 68 | JUST_TEST_CASE(test_has_include_next) 69 | { 70 | test_shell sh(config::default_config); 71 | 72 | sh.line_available("#if __has_include_next(\"myinclude.h\")"); 73 | 74 | JUST_ASSERT_EQUAL("", sh.error()); 75 | } 76 | 77 | JUST_TEST_CASE(test_has_warning) 78 | { 79 | test_shell sh(config::default_config); 80 | 81 | sh.line_available("#if __has_warning(-Wformat)"); 82 | 83 | JUST_ASSERT_EQUAL("", sh.error()); 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /tools/gcc_builtin_macros: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | Utility to query the builtin macros of gcc or clang. 4 | 5 | Supported options: 6 | 7 | -g, --gcc The binary to use as gcc or clang (default: gcc) 8 | -h, --help Display this help message 9 | """ 10 | 11 | # Preshell - Interactive C/C++ preprocessor shell 12 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 13 | # 14 | # This program is free software: you can redistribute it and/or modify 15 | # it under the terms of the GNU General Public License as published by 16 | # the Free Software Foundation, either version 3 of the License, or 17 | # (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU General Public License 25 | # along with this program. If not, see . 26 | 27 | import sys 28 | import subprocess 29 | import getopt 30 | 31 | def run(cmd): 32 | try: 33 | return subprocess.Popen( 34 | ['/bin/sh', '-c', cmd], 35 | stderr=subprocess.PIPE, 36 | stdout=subprocess.PIPE, 37 | stdin=subprocess.PIPE 38 | ).communicate(input='') 39 | except OSError: 40 | raise Exception('%s: %s' % (' '.join(cmd), sys.exc_info()[1])) 41 | 42 | c_escape_map = { 43 | '\\' : '\\\\', 44 | '"' : '\\"', 45 | '\'' : '\\\'', 46 | '\a' : '\\a', 47 | '\b' : '\\b', 48 | '\f' : '\\f', 49 | '\n' : '\\n', 50 | '\r' : '\\r', 51 | '\t' : '\\t', 52 | '\v' : '\\v' 53 | } 54 | 55 | def c_string_escape(c): 56 | if c_escape_map.has_key(c): 57 | return c_escape_map[c] 58 | else: 59 | return c 60 | 61 | def c_string_literal(s): 62 | return '"' + ''.join([c_string_escape(c) for c in s]) + '"' 63 | 64 | def main(): 65 | try: 66 | opts, args = getopt.getopt(sys.argv[1:], 'hg:', ['help', 'gcc=']) 67 | except getopt.error, msg: 68 | print msg 69 | print "Getting help: --help" 70 | sys.exit(1) 71 | 72 | gcc = 'gcc' 73 | 74 | for o, a in opts: 75 | if o in('-h', '--help'): 76 | print __doc__ 77 | sys.exit(0) 78 | elif o in('-g', '--gcc'): 79 | gcc = a 80 | 81 | try: 82 | print c_string_literal(run((gcc + ' -dM -E -'))[0]) 83 | except: 84 | sys.stderr.write('Error: %s\n' % (sys.exc_info()[1])) 85 | 86 | if __name__ == '__main__': 87 | main() 88 | 89 | 90 | -------------------------------------------------------------------------------- /include/preshell/shell.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_SHELL_HPP 2 | #define PRESHELL_SHELL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace preshell 30 | { 31 | class shell 32 | { 33 | public: 34 | shell( 35 | const preshell::config& config_, 36 | const std::vector& macros_ 37 | ); 38 | virtual ~shell(); 39 | 40 | virtual void add_history(const std::string& s_) = 0; 41 | 42 | virtual void display_normal(const std::string& s_) const = 0; 43 | virtual void display_info(const std::string& s_) const = 0; 44 | virtual void display_error(const std::string& s_) const = 0; 45 | 46 | virtual unsigned int width() const = 0; 47 | 48 | void display_splash() const; 49 | void line_available(const std::string& s_); 50 | std::string prompt() const; 51 | 52 | const preshell::config& config() const; 53 | const preshell::context& context() const; 54 | 55 | void cancel_operation(); 56 | 57 | // Precondition: line_available has not been called 58 | void display_initialisation_diagnostic() const; 59 | 60 | static const std::vector directives; 61 | 62 | const std::list& history() const; 63 | 64 | void replay_history(); 65 | private: 66 | indenter _info_indenter; 67 | indenter _error_indenter; 68 | preshell::config _config; 69 | preshell::result_ptr _context; 70 | std::string _buffer; 71 | std::string _prev_line; 72 | std::list _history; 73 | preshell::context _initial_context; 74 | 75 | void precompile_input(const std::string& s_); 76 | 77 | void display_output_if_available() const; 78 | void display_info_if_available() const; 79 | void display_error_if_available() const; 80 | 81 | template 82 | void process_line(const std::string& s_); 83 | }; 84 | } 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /cmake/Modules/FindReadline.cmake: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | # Options for the module 18 | # READLINE_INCLUDEDIR Set this if the module can not find the Readline 19 | # headers 20 | # READLINE_LIBRARYDIR Set this if the module can not find the Readline 21 | # library 22 | # READLINE_DEBUG Set this for verbose output 23 | # 24 | # This module will define the following: 25 | # READLINE_FOUND 26 | # READLINE_INCLUDE_DIR 27 | # READLINE_LIBRARY 28 | 29 | if (NOT $ENV{READLINE_INCLUDEDIR} STREQUAL "" ) 30 | set(READLINE_INCLUDEDIR $ENV{READLINE_INCLUDEDIR}) 31 | endif() 32 | 33 | if (NOT $ENV{READLINE_LIBRARYDIR} STREQUAL "" ) 34 | set(READLINE_LIBRARYDIR $ENV{READLINE_LIBRARYDIR}) 35 | endif() 36 | 37 | if (READLINE_DEBUG) 38 | message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]") 39 | message(STATUS "Searching for Readline") 40 | message(STATUS " READLINE_INCLUDEDIR = ${READLINE_INCLUDEDIR}") 41 | message(STATUS " READLINE_LIBRARYDIR = ${READLINE_LIBRARYDIR}") 42 | endif() 43 | 44 | find_path( 45 | READLINE_INCLUDE_DIR 46 | NAMES readline/readline.h 47 | HINTS ${READLINE_INCLUDEDIR} 48 | ) 49 | find_library( 50 | READLINE_LIBRARY 51 | NAMES readline 52 | HINTS ${READLINE_LIBRARYDIR} 53 | ) 54 | 55 | include(FindPackageHandleStandardArgs) 56 | # handle the QUIETLY and REQUIRED arguments and set READLINE_FOUND to TRUE 57 | # if all listed variables are TRUE 58 | find_package_handle_standard_args( 59 | Readline DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR 60 | ) 61 | 62 | mark_as_advanced(READLINE_INCLUDE_DIR, READLINE_LIBRARY) 63 | 64 | if (READLINE_DEBUG) 65 | message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]") 66 | if (READLINE_FOUND) 67 | message(STATUS "Readline found") 68 | message(STATUS " READLINE_INCLUDE_DIR = ${READLINE_INCLUDE_DIR}") 69 | message(STATUS " READLINE_LIBRARY = ${READLINE_LIBRARY}") 70 | else() 71 | message(STATUS "Readline not found") 72 | endif() 73 | message(STATUS "[${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}]") 74 | endif() 75 | 76 | 77 | -------------------------------------------------------------------------------- /test/replay_history.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_shell.hpp" 18 | #include "temp_header.hpp" 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | using namespace preshell; 27 | 28 | namespace 29 | { 30 | template 31 | void test_changing_header_between_replay_impl(F replay_history_) 32 | { 33 | temp_header header; 34 | 35 | config cfg = config::empty; 36 | cfg.sysinclude_path.push_back(header.include_dir()); 37 | 38 | test_shell sh(cfg); 39 | 40 | header.create("#define FOO bar"); 41 | sh.line_available(header.include_command()); 42 | header.create("#define FOO foo"); 43 | replay_history_(sh); 44 | sh.line_available("FOO"); 45 | 46 | JUST_ASSERT_EQUAL("foo", sh.normal()); 47 | JUST_ASSERT_EQUAL("", sh.error()); 48 | } 49 | } 50 | 51 | JUST_TEST_CASE(test_replay_no_include) 52 | { 53 | test_shell sh(config::empty); 54 | 55 | sh.line_available("#define FOO bar"); 56 | sh.replay_history(); 57 | sh.line_available("FOO"); 58 | 59 | JUST_ASSERT_EQUAL("bar", sh.normal()); 60 | } 61 | 62 | JUST_TEST_CASE(test_temp_header) 63 | { 64 | temp_header header; 65 | 66 | config cfg = config::empty; 67 | cfg.sysinclude_path.push_back(header.include_dir()); 68 | 69 | test_shell sh(cfg); 70 | 71 | header.create("#define FOO bar"); 72 | sh.line_available(header.include_command()); 73 | sh.line_available("FOO"); 74 | 75 | JUST_ASSERT_EQUAL("bar", sh.normal()); 76 | } 77 | 78 | JUST_TEST_CASE(test_changing_header_between_replay) 79 | { 80 | test_changing_header_between_replay_impl( 81 | boost::bind(&test_shell::replay_history, _1) 82 | ); 83 | } 84 | 85 | JUST_TEST_CASE(test_no_output_during_replay_history) 86 | { 87 | test_shell sh(config::empty); 88 | 89 | sh.line_available("foo bar"); 90 | sh.replay_history(); 91 | 92 | JUST_ASSERT_EQUAL("foo bar", sh.normal()); 93 | } 94 | 95 | JUST_TEST_CASE(test_pragma_for_replay_history) 96 | { 97 | test_changing_header_between_replay_impl( 98 | boost::bind(&test_shell::line_available, _1, "#pragma wave replay_history") 99 | ); 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /include/preshell/macro_definition.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_MACRO_DEFINITION_HPP 2 | #define PRESHELL_MACRO_DEFINITION_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace preshell 30 | { 31 | class macro_definition : boost::equality_comparable 32 | { 33 | public: 34 | macro_definition() {} 35 | 36 | macro_definition( 37 | const file_position& position_, 38 | const std::list& definition_ 39 | ) : 40 | _position(position_), 41 | _has_parameters(false), 42 | _parameters(), 43 | _definition(definition_) 44 | {} 45 | 46 | macro_definition( 47 | const file_position& position_, 48 | const std::vector& parameters_, 49 | const std::list& definition_ 50 | ) : 51 | _position(position_), 52 | _has_parameters(true), 53 | _parameters(parameters_), 54 | _definition(definition_) 55 | {} 56 | 57 | template 58 | macro_definition(const String& name_, const Context& context_) 59 | { 60 | bool ignore; 61 | typename Context::token_sequence_type def; 62 | 63 | context_.get_macro_definition( 64 | name_, 65 | _has_parameters, 66 | ignore, 67 | _position, 68 | _parameters, 69 | def 70 | ); 71 | _definition = std::list(def.begin(), def.end()); 72 | } 73 | 74 | const file_position& position() const { return _position; } 75 | bool has_parameters() const { return _has_parameters; } 76 | const std::vector& parameters() const { return _parameters; } 77 | const std::list& definition() const { return _definition; } 78 | 79 | bool operator==(const macro_definition& m_) const 80 | { 81 | return 82 | _position == m_._position && 83 | _has_parameters == m_._has_parameters && 84 | _parameters == m_._parameters && 85 | _definition == m_._definition; 86 | } 87 | private: 88 | file_position _position; 89 | bool _has_parameters; 90 | std::vector _parameters; 91 | std::list _definition; 92 | }; 93 | 94 | std::ostream& operator<<(std::ostream& o_, const macro_definition& m_); 95 | } 96 | 97 | #endif 98 | 99 | -------------------------------------------------------------------------------- /tools/gcc_default_path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | Utility to query the default include PATH of gcc or clang. 4 | 5 | Supported options: 6 | 7 | -g, --gcc The binary to use as gcc or clang (default: gcc) 8 | -h, --help Display this help message 9 | -t, --type sys|normal The type of include path to query (default: sys) 10 | 11 | Depending on its argument, the script queries the default 12 | (system) include path. 13 | """ 14 | 15 | # Preshell - Interactive C/C++ preprocessor shell 16 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 17 | # 18 | # This program is free software: you can redistribute it and/or modify 19 | # it under the terms of the GNU General Public License as published by 20 | # the Free Software Foundation, either version 3 of the License, or 21 | # (at your option) any later version. 22 | # 23 | # This program is distributed in the hope that it will be useful, 24 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | # GNU General Public License for more details. 27 | # 28 | # You should have received a copy of the GNU General Public License 29 | # along with this program. If not, see . 30 | 31 | import sys 32 | import subprocess 33 | import getopt 34 | 35 | def run(cmd): 36 | try: 37 | return subprocess.Popen( 38 | ['/bin/sh', '-c', cmd], 39 | stderr=subprocess.PIPE, 40 | stdout=subprocess.PIPE, 41 | stdin=subprocess.PIPE 42 | ).communicate(input='') 43 | except OSError: 44 | raise Exception('%s: %s' % (' '.join(cmd), sys.exc_info()[1])) 45 | 46 | def get_standard_gcc_includes(gcc): 47 | includes = [] 48 | sysincludes = [] 49 | current = None 50 | for l in run((gcc + ' -v -xc++ -'))[1].split('\n'): 51 | retry = True 52 | while retry: 53 | retry = False 54 | if current == None: 55 | if l.startswith('#include "'): 56 | current = includes 57 | elif l.startswith('#include <'): 58 | current = sysincludes 59 | else: 60 | if l.startswith(' '): 61 | current.append(l.strip()) 62 | else: 63 | current = None 64 | retry = True 65 | return (includes, sysincludes) 66 | 67 | def main(): 68 | try: 69 | opts, args = getopt.getopt(sys.argv[1:], 'ht:g:', ['help', 'type=', 'gcc=']) 70 | except getopt.error, msg: 71 | print msg 72 | print "Getting help: --help" 73 | sys.exit(1) 74 | 75 | type = 'sys' 76 | gcc = 'gcc' 77 | 78 | for o, a in opts: 79 | if o in ('-t', '--type'): 80 | if a in ('sys', 'normal'): 81 | type = a 82 | else: 83 | print 'Invalid type: %s' % (a) 84 | sys.exit(1) 85 | elif o in('-h', '--help'): 86 | print __doc__ 87 | sys.exit(0) 88 | elif o in('-g', '--gcc'): 89 | gcc = a 90 | 91 | try: 92 | (normal, system) = get_standard_gcc_includes(gcc) 93 | if type == 'sys': 94 | paths = system 95 | else: 96 | paths = normal 97 | print '\n'.join([', "%s"' % s for s in paths]) 98 | print '' 99 | except: 100 | sys.stderr.write('Error: %s\n' % (sys.exc_info()[1])) 101 | 102 | if __name__ == '__main__': 103 | main() 104 | 105 | 106 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Preshell - Interactive C/C++ preprocessor shell 2 | # Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | cmake_minimum_required(VERSION 2.6) 18 | 19 | project(preshell) 20 | set(CMAKE_PROJECT_NAME preshell) 21 | set(MAJOR_VERSION 1) 22 | set(MINOR_VERSION 0) 23 | set(PATCH_VERSION 0) 24 | 25 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 26 | 27 | # Version macros 28 | add_definitions(-DPRESHELL_MAJOR=${MAJOR_VERSION}) 29 | add_definitions(-DPRESHELL_MINOR=${MINOR_VERSION}) 30 | add_definitions(-DPRESHELL_PATCH=${PATCH_VERSION}) 31 | 32 | # Boost 33 | # Turn this on to link statically against Boost 34 | # set(Boost_USE_STATIC_LIBS ON) 35 | find_package(Boost COMPONENTS 36 | system # used by Wave 37 | thread # used by Wave 38 | filesystem # used by Wave 39 | wave 40 | program_options 41 | ) 42 | find_package(Threads) # used by Boost.Thread 43 | 44 | include_directories(${Boost_INCLUDE_DIR}) 45 | link_directories(${Boost_LIBRARY_DIRS}) 46 | 47 | set( 48 | BOOST_VER 49 | "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}" 50 | ) 51 | 52 | 53 | include_directories("include") 54 | 55 | # Readline 56 | find_package(Readline) 57 | 58 | # Unit testing 59 | enable_testing() 60 | 61 | # Recursing 62 | subdirs(lib test app) 63 | 64 | # Debian package 65 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 66 | set(CMAKE_INSTALL_PREFIX "/usr") 67 | endif() 68 | 69 | if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") 70 | include(InstallRequiredSystemLibraries) 71 | 72 | set(CPACK_SET_DESTDIR "on") 73 | set(CPACK_PACKAGING_INSTALL_PREFIX "/tmp") 74 | set(CPACK_GENERATOR "DEB") 75 | 76 | set(CPACK_PACKAGE_DESCRIPTION "Preshell") 77 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "It is an interactive C preprocessor shell") 78 | set(CPACK_PACKAGE_VENDOR "Abel Sinkovics") 79 | set(CPACK_PACKAGE_CONTACT "abel@sinkovics.hu") 80 | set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}") 81 | set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") 82 | set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") 83 | set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}") 84 | set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}") 85 | 86 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-system${BOOST_VER}, libboost-thread${BOOST_VER}, libboost-filesystem${BOOST_VER}, libboost-wave${BOOST_VER}, libboost-program-options${BOOST_VER}") 87 | set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") 88 | set(CPACK_DEBIAN_PACKAGE_SECTION "devel") 89 | set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) 90 | 91 | set(CPACK_COMPONENTS_ALL Libraries ApplicationData) 92 | include(CPack) 93 | 94 | endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") 95 | 96 | 97 | -------------------------------------------------------------------------------- /test/shell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "test_shell.hpp" 18 | 19 | #include 20 | 21 | using namespace preshell; 22 | 23 | JUST_TEST_CASE(test_builtin_macro_definitions_are_available) 24 | { 25 | config cfg = config::empty; 26 | cfg.builtin_macro_definitions = "#define FOO bar\n"; 27 | test_shell sh(cfg); 28 | 29 | sh.line_available("FOO"); 30 | 31 | JUST_ASSERT_EQUAL("bar", sh.normal()); 32 | } 33 | 34 | JUST_TEST_CASE(test_line_with_builtin_macro_definitions) 35 | { 36 | config cfg = config::empty; 37 | cfg.builtin_macro_definitions = "#define FOO bar\n"; 38 | test_shell sh(cfg); 39 | 40 | sh.line_available("__LINE__"); 41 | 42 | JUST_ASSERT_EQUAL("1", sh.normal()); 43 | } 44 | 45 | JUST_TEST_CASE(test_displaying_errors_in_builtin_macro_definition) 46 | { 47 | config cfg = config::empty; 48 | cfg.builtin_macro_definitions = "#error foo"; 49 | 50 | test_shell sh(cfg); 51 | 52 | JUST_ASSERT(!sh.error().empty()); 53 | } 54 | 55 | JUST_TEST_CASE(test_redefining_protected_name_in_builtin_macros) 56 | { 57 | config cfg = config::empty; 58 | cfg.builtin_macro_definitions = "#define __STDC__ 13\n"; 59 | 60 | test_shell sh(cfg); 61 | 62 | JUST_ASSERT(sh.error().empty()); 63 | } 64 | 65 | JUST_TEST_CASE( 66 | test_redefining_protected_name_in_builtin_macros_with_spaces 67 | ) 68 | { 69 | config cfg = config::empty; 70 | cfg.builtin_macro_definitions = " # define __STDC__ 13\n"; 71 | 72 | test_shell sh(cfg); 73 | 74 | JUST_ASSERT(sh.error().empty()); 75 | } 76 | 77 | JUST_TEST_CASE(test_including_standard_headers_with_default_config) 78 | { 79 | config cfg = config::default_config; 80 | cfg.enable_warnings = false; 81 | 82 | test_shell sh(cfg); 83 | 84 | sh.line_available("#include "); 85 | 86 | // Comparing to empty string produces a more informative error message when 87 | // the assertion fails 88 | JUST_ASSERT_EQUAL("", sh.error()); 89 | } 90 | 91 | JUST_TEST_CASE(test_history_is_stored) 92 | { 93 | std::vector history; 94 | test_shell sh(history); 95 | 96 | sh.line_available("int"); 97 | 98 | JUST_ASSERT_EQUAL(1u, history.size()); 99 | JUST_ASSERT_EQUAL("int", history.front()); 100 | } 101 | 102 | JUST_TEST_CASE(test_empty_line_is_not_stored_in_history) 103 | { 104 | std::vector history; 105 | test_shell sh(history); 106 | 107 | sh.line_available(""); 108 | 109 | JUST_ASSERT_EQUAL(0u, history.size()); 110 | } 111 | 112 | JUST_TEST_CASE( 113 | test_the_same_thing_following_each_other_is_not_added_to_history_twice 114 | ) 115 | { 116 | std::vector history; 117 | test_shell sh(history); 118 | 119 | sh.line_available("int"); 120 | sh.line_available("int"); 121 | 122 | JUST_ASSERT_EQUAL(1u, history.size()); 123 | JUST_ASSERT_EQUAL("int", history.front()); 124 | } 125 | 126 | -------------------------------------------------------------------------------- /lib/core/indenter.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace preshell; 26 | 27 | namespace 28 | { 29 | bool is_whitespace(char c_) 30 | { 31 | return c_ == ' ' || c_ == '\t' || c_ == '\v' || c_ == '\r' || c_ == '\n'; 32 | } 33 | 34 | void word_wrap( 35 | const std::string& s_, 36 | unsigned int width_, 37 | std::vector& out_ 38 | ) 39 | { 40 | for (std::string s(s_); !s.empty();) 41 | { 42 | if (s.length() > width_) 43 | { 44 | int i = width_; 45 | while (i != -1 && !is_whitespace(s[i])) 46 | { 47 | --i; 48 | } 49 | 50 | if (i == -1) 51 | { 52 | out_.push_back(std::string(s.begin(), s.begin() + width_)); 53 | s = std::string(s.begin() + width_, s.end()); 54 | } 55 | else 56 | { 57 | out_.push_back(std::string(s.begin(), s.begin() + i)); 58 | s = std::string(s.begin() + (i + 1), s.end()); 59 | } 60 | } 61 | else 62 | { 63 | out_.push_back(s); 64 | s = std::string(); 65 | } 66 | } 67 | } 68 | } 69 | 70 | indenter::indenter(const get_width_t& get_width_, const output_t& out_) : 71 | _get_width(get_width_), 72 | _out(out_) 73 | {} 74 | 75 | indenter::indenter(const indenter& base_, const output_t& out_) : 76 | _get_width(base_._get_width), 77 | _out(out_) 78 | {} 79 | 80 | indenter::~indenter() 81 | { 82 | assert(_buff.str() == ""); 83 | } 84 | 85 | indenter& indenter::raw(const std::string& s_) 86 | { 87 | _buff << s_ << "\n"; 88 | return *this; 89 | } 90 | 91 | indenter& indenter::empty_line() 92 | { 93 | return raw(""); 94 | } 95 | 96 | indenter& indenter::left_align(const std::string& s_) 97 | { 98 | return left_align(s_, "", ""); 99 | } 100 | 101 | indenter& indenter::left_align( 102 | const std::string& s_, 103 | const std::string& line_prefix_ 104 | ) 105 | { 106 | return left_align(s_, line_prefix_, line_prefix_); 107 | } 108 | 109 | indenter& indenter::left_align( 110 | const std::string& s_, 111 | const std::string& line_prefix_, 112 | const std::string& first_line_prefix_ 113 | ) 114 | { 115 | assert(first_line_prefix_.length() == line_prefix_.length()); 116 | 117 | std::vector lines; 118 | word_wrap(s_, _get_width() - line_prefix_.length(), lines); 119 | bool first = true; 120 | BOOST_FOREACH(const std::string& line, lines) 121 | { 122 | _buff << (first ? first_line_prefix_ : line_prefix_) << line << "\n"; 123 | first = false; 124 | } 125 | return *this; 126 | } 127 | 128 | void indenter::flush() 129 | { 130 | _out(_buff.str()); 131 | _buff.str(std::string()); 132 | } 133 | 134 | -------------------------------------------------------------------------------- /tools/gen_toc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # (See accompanying file LICENSE_1_0.txt or copy at 6 | # http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | import re 9 | import sys 10 | import optparse 11 | 12 | def anchor(c, toc_type): 13 | if c == ' ': 14 | return '-' 15 | elif c == '.': 16 | if toc_type == 'pandoc_html': 17 | return '.' 18 | else: 19 | return '' 20 | elif c in [':', '`', '?', '(', ')']: 21 | return '' 22 | else: 23 | return c.lower() 24 | 25 | def index_anchor(a, n): 26 | return '%s-%d' % (a, n) 27 | 28 | class Header: 29 | def __init__(self, s, prev_anchors, toc_type): 30 | [pre, title] = s.split(' ', 1) 31 | self.depth = len(pre) - 1 32 | self.title = title.strip() 33 | self.anchor = ''.join([anchor(c, toc_type) for c in self.title]) 34 | if self.anchor in prev_anchors: 35 | i = 1 36 | while index_anchor(self.anchor, i) in prev_anchors: 37 | i = i + 1 38 | self.anchor = index_anchor(self.anchor, i) 39 | 40 | def toc_line(self, indent): 41 | return \ 42 | '%s- [%s](#%s)' % (' ' * (self.depth+indent), self.title, self.anchor) 43 | 44 | def is_header(s): 45 | return re.compile('^[#]+ ').search(s) 46 | 47 | def collect_headers(doc, toc_type): 48 | header_lines = [s for s in doc if is_header(s)] 49 | headers = [] 50 | anchors = [] 51 | for s in header_lines: 52 | h = Header(s, anchors, toc_type) 53 | headers.append(h) 54 | anchors.append(h.anchor) 55 | return headers 56 | 57 | def generate_toc(headers, toc_title): 58 | return \ 59 | '\n'.join( \ 60 | [h.toc_line(-1) for h in headers if h.depth > 0 and h.title != toc_title]\ 61 | ) + '\n' 62 | 63 | def generate_doc(f, doc, toc_title, toc_type): 64 | in_toc = False 65 | for s in doc: 66 | if in_toc: 67 | if is_header(s): 68 | f.write(s) 69 | in_toc = False 70 | else: 71 | if is_header(s) and Header(s, [], toc_type).title == toc_title: 72 | in_toc = True 73 | if toc_type != 'none': 74 | f.write(s + '\n') 75 | f.write(generate_toc(collect_headers(doc, toc_type), toc_title)) 76 | f.write('\n') 77 | else: 78 | f.write(s) 79 | 80 | def main(): 81 | accepted_types = ['github', 'pandoc_html', 'none'] 82 | parser = optparse.OptionParser() 83 | parser.add_option( 84 | '-i', '--input', 85 | action='store', 86 | dest='input', 87 | help='The file to process' 88 | ) 89 | parser.add_option( 90 | '-o', '--output', 91 | action='store', 92 | dest='output', 93 | help='The file to write the result into. It defaults to the input file. To write output to stdout use: -' 94 | ) 95 | parser.add_option( 96 | '-t', '--type', 97 | action='store', 98 | dest='type', 99 | default='github', 100 | help='Type of TOC. Possible values: %s. Default: %s' 101 | % (', '.join(accepted_types), 'github') 102 | ) 103 | 104 | (options, args) = parser.parse_args() 105 | if options.output == None: 106 | options.output = options.input 107 | 108 | if options.input == None: 109 | parser.error('No input file specified') 110 | elif not options.type in accepted_types: 111 | parser.error('Invalid type: %s' % (options.type)) 112 | else: 113 | toc_title = 'Table of contents' 114 | doc = open(options.input, 'r').readlines() 115 | if options.output == '-': 116 | f = sys.stdout 117 | else: 118 | f = open(options.output, 'w') 119 | generate_doc(f, doc, toc_title, options.type) 120 | 121 | if __name__ == '__main__': 122 | main() 123 | 124 | -------------------------------------------------------------------------------- /lib/core/wave_context.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | using namespace preshell; 23 | 24 | namespace 25 | { 26 | template 27 | macro_map get_macros_impl( 28 | It begin_, 29 | It end_, 30 | const wave_context& context_ 31 | ) 32 | { 33 | macro_map result; 34 | for (; begin_ != end_; ++begin_) 35 | { 36 | result.insert( 37 | std::make_pair( 38 | std::string(begin_->c_str()), 39 | macro_definition(*begin_, context_) 40 | ) 41 | ); 42 | } 43 | // Wave doesn't seem to export it 44 | if (result.find("__LINE__") == result.end()) 45 | { 46 | std::ostringstream s; 47 | s << context_.get_main_pos().get_line(); 48 | const file_position p("", 1, 1); 49 | result.insert( 50 | std::make_pair( 51 | std::string("__LINE__"), 52 | macro_definition( 53 | p, 54 | std::list( 55 | 1, 56 | token(boost::wave::T_INTLIT, s.str().c_str(), p) 57 | ) 58 | ) 59 | ) 60 | ); 61 | } 62 | if (result.find("__FILE__") == result.end()) 63 | { 64 | const file_position p("", 1, 1); 65 | std::ostringstream s; 66 | s << "\""; 67 | string_escape(context_.get_main_pos().get_file().c_str(), s); 68 | s << "\""; 69 | result.insert( 70 | std::make_pair( 71 | std::string("__FILE__"), 72 | macro_definition( 73 | p, 74 | std::list( 75 | 1, 76 | token(boost::wave::T_STRINGLIT, s.str().c_str(), p) 77 | ) 78 | ) 79 | ) 80 | ); 81 | } 82 | return result; 83 | } 84 | } 85 | 86 | wave_context_ptr preshell::create_context( 87 | const std::string& input_, 88 | std::list& if_states_, 89 | const config& config_, 90 | indenter& info_indenter_, 91 | indenter& error_indenter_, 92 | const bool& log_macro_definitions_, 93 | const std::list& history_, 94 | const bool& enable_save_history_, 95 | bool& replay_history_ 96 | ) 97 | { 98 | wave_context_ptr 99 | context( 100 | new wave_context( 101 | input_.begin(), 102 | input_.end(), 103 | "", 104 | preshell_preprocessing_hooks( 105 | if_states_, 106 | info_indenter_, 107 | error_indenter_, 108 | log_macro_definitions_, 109 | history_, 110 | enable_save_history_, 111 | replay_history_ 112 | ) 113 | ) 114 | ); 115 | context->set_language( 116 | boost::wave::language_support( 117 | boost::wave::support_cpp | boost::wave::support_c99 118 | ) 119 | ); 120 | 121 | BOOST_FOREACH(const std::string& s, config_.include_path) 122 | { 123 | context->add_include_path(s.c_str()); 124 | } 125 | 126 | BOOST_FOREACH(const std::string& s, config_.sysinclude_path) 127 | { 128 | context->add_sysinclude_path(s.c_str()); 129 | } 130 | 131 | return context; 132 | } 133 | 134 | macro_map preshell::get_macros(const wave_context& context_) 135 | { 136 | return get_macros_impl( 137 | context_.macro_names_begin(), 138 | context_.macro_names_end(), 139 | context_ 140 | ); 141 | } 142 | 143 | 144 | -------------------------------------------------------------------------------- /lib/core/context.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | using namespace preshell; 29 | 30 | std::ostream& preshell::operator<<(std::ostream& o_, const macro_map& m_) 31 | { 32 | o_ << "macro_map{" << std::endl; 33 | 34 | for (macro_map::const_iterator i = m_.begin(), e = m_.end(); i != e; ++i) 35 | { 36 | o_ << " {" << i->first << ", " << i->second << "}" << std::endl; 37 | } 38 | 39 | return o_ << "}"; 40 | } 41 | 42 | bool context::tokens_skipped() const 43 | { 44 | using std::list; 45 | 46 | for ( 47 | list::const_iterator i = if_states.begin(), e = if_states.end(); 48 | i != e; 49 | ++i 50 | ) 51 | { 52 | if (*i == if0_then || *i == if1_else) 53 | { 54 | return true; 55 | } 56 | } 57 | return false; 58 | } 59 | 60 | namespace 61 | { 62 | file_position unknown_position() 63 | { 64 | return file_position("", 1, 1); 65 | } 66 | 67 | std::string to_string(int n_) 68 | { 69 | std::ostringstream s; 70 | s << n_; 71 | return s.str(); 72 | } 73 | 74 | template 75 | void add_macro( 76 | const std::string& name_, 77 | const std::string& value_, 78 | macro_map& m_ 79 | ) 80 | { 81 | using std::list; 82 | 83 | const file_position pos("", 1, 1); 84 | 85 | m_[name_] = 86 | macro_definition( 87 | unknown_position(), 88 | list(1, token(Id, value_.c_str(), pos)) 89 | ); 90 | } 91 | 92 | template 93 | void add_macro(const std::string& name_, macro_map& m_) 94 | { 95 | add_macro(name_, to_string(Value), m_); 96 | } 97 | 98 | void add_macro_with_empty_body(const std::string& name_, macro_map& m_) 99 | { 100 | m_[name_] = macro_definition(unknown_position(), std::list()); 101 | } 102 | } 103 | 104 | context context::initial( 105 | const config& config_, 106 | const std::vector& macros_, 107 | indenter& info_indenter_, 108 | indenter& error_indenter_, 109 | const std::list& history_ 110 | ) 111 | { 112 | std::list if_states; 113 | const bool log_macro_definitions = false; 114 | const bool enable_save_history = false; 115 | bool replay_history = false; 116 | 117 | wave_context_ptr wctx = 118 | create_context( 119 | "", 120 | if_states, 121 | config_, 122 | info_indenter_, 123 | error_indenter_, 124 | log_macro_definitions, 125 | history_, 126 | enable_save_history, 127 | replay_history 128 | ); 129 | BOOST_FOREACH(const std::string& i, macros_) 130 | { 131 | wctx->add_macro_definition(i); 132 | } 133 | macro_map m = get_macros(*wctx); 134 | add_macro_with_empty_body("__PRESHELL", m); 135 | add_macro("__PRESHELL_MAJOR", m); 136 | add_macro("__PRESHELL_MINOR", m); 137 | add_macro("__PRESHELL_PATCH", m); 138 | add_macro( 139 | "help", 140 | "\"Getting help: #pragma wave preshell_help\"", 141 | m 142 | ); 143 | 144 | assert(!replay_history); 145 | 146 | return context(m, "", 1); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /include/preshell/preshell.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_PRESHELL_HPP 2 | #define PRESHELL_PRESHELL_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace preshell 33 | { 34 | result_ptr precompile( 35 | const std::string& input_, 36 | const context& context_, 37 | const config& config_, 38 | indenter& indenter_, 39 | const std::list& history_ 40 | ); 41 | void cancel(); 42 | 43 | bool continuation_needed(const std::string& s_, const config& config_); 44 | 45 | // Utility 46 | void string_escape(const std::string& s_, std::ostream& out_); 47 | std::string string_escape(const std::string& s_); 48 | 49 | std::string remove_protected_macro_definitions(const std::string& s_); 50 | 51 | bool recoverable(const boost::wave::cpp_exception& e_); 52 | std::string format_error(const boost::wave::cpp_exception& e_); 53 | 54 | template 55 | bool wave_iterators_equal(It a_, It b_, Cont* warnings_) 56 | { 57 | using boost::wave::cpp_exception; 58 | try 59 | { 60 | return a_ == b_; 61 | } 62 | catch (const cpp_exception& e) 63 | { 64 | if (recoverable(e)) 65 | { 66 | if (warnings_) 67 | { 68 | warnings_->push_back(format_error(e)); 69 | } 70 | return false; 71 | } 72 | else 73 | { 74 | throw; 75 | } 76 | } 77 | } 78 | 79 | template 80 | std::string join( 81 | It begin_, 82 | It end_, 83 | Cont* warnings_, 84 | const bool& cancel_preprocessing_, 85 | bool suppress_empty_lines_ 86 | ) 87 | { 88 | using boost::wave::cpp_exception; 89 | 90 | std::ostringstream s; 91 | try 92 | { 93 | std::ostringstream current_line; 94 | It i = begin_; 95 | while ( 96 | !cancel_preprocessing_ && !wave_iterators_equal(i, end_, warnings_) 97 | ) 98 | { 99 | if (IS_CATEGORY(*i, boost::wave::EOLTokenType)) 100 | { 101 | const std::string l = current_line.str(); 102 | current_line.str(std::string()); 103 | if (!(l.empty() && suppress_empty_lines_)) 104 | { 105 | s << l << i->get_value(); 106 | } 107 | } 108 | else if (*i == boost::wave::T_CPPCOMMENT) 109 | { 110 | s << current_line.str() << i->get_value(); 111 | current_line.str(std::string()); 112 | } 113 | else 114 | { 115 | current_line << i->get_value(); 116 | } 117 | try 118 | { 119 | ++i; 120 | } 121 | catch (const cpp_exception& e) 122 | { 123 | if (recoverable(e)) 124 | { 125 | if (warnings_) 126 | { 127 | warnings_->push_back(format_error(e)); 128 | } 129 | } 130 | else 131 | { 132 | throw; 133 | } 134 | } 135 | } 136 | const std::string l = current_line.str(); 137 | if (!l.empty()) 138 | { 139 | s << l; 140 | } 141 | } 142 | catch (const cpp_exception& e) 143 | { 144 | if ( 145 | e.get_errorcode() 146 | != boost::wave::preprocess_exception::missing_matching_endif 147 | ) 148 | { 149 | throw; 150 | } 151 | } 152 | return s.str(); 153 | } 154 | 155 | } 156 | 157 | #endif 158 | 159 | -------------------------------------------------------------------------------- /lib/core/preshell_preprocessing_hooks.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | using namespace preshell; 27 | 28 | preshell_preprocessing_hooks::preshell_preprocessing_hooks( 29 | std::list& if_states_, 30 | indenter& info_indenter_, 31 | indenter& error_indenter_, 32 | const bool& log_macro_definitions_, 33 | const std::list& history_, 34 | const bool& enable_save_history_, 35 | bool& replay_history_ 36 | ) : 37 | _if_states(if_states_), 38 | _info_indenter(info_indenter_), 39 | _error_indenter(error_indenter_), 40 | _log_macro_definitions(log_macro_definitions_), 41 | _history(history_), 42 | _enable_save_history(enable_save_history_), 43 | _replay_history(replay_history_) 44 | {} 45 | 46 | void preshell_preprocessing_hooks::display_help() 47 | { 48 | _info_indenter 49 | .left_align("Usage:") 50 | .empty_line() 51 | .left_align( 52 | "The preprocessor shell preprocesses the code you give it and displays" 53 | " the preprocessed result. The shell provides command history and" 54 | " tab-completion for preprocessor directives and macro names. Some" 55 | " pragmas are also provided to inspect or update the state of the" 56 | " preprocessor." 57 | ) 58 | .empty_line() 59 | .left_align( 60 | "When the last character of a line is '\\', the shell does not process " 61 | " it, but waits for the next line and processes the two lines together." 62 | ) 63 | .empty_line() 64 | .left_align( 65 | "Any preprocessing can be interrupted by pressing Ctrl-C, which gives" 66 | " you the prompt back." 67 | ) 68 | .empty_line() 69 | .left_align("You can quit by pressing Ctrl-D.") 70 | .empty_line() 71 | .left_align("Available pragmas:") 72 | .empty_line() 73 | .left_align("#pragma wave macros", " ") 74 | .left_align("Displays the list of defined macros.", " ") 75 | .empty_line() 76 | .left_align("#pragma wave macro_names", " ") 77 | .left_align("Displays the list of defined macro names.", " ") 78 | .empty_line() 79 | .left_align("#pragma wave preshell_help", " ") 80 | .left_align("Displays this help.", " ") 81 | .empty_line() 82 | .left_align("#pragma wave save_history()", " ") 83 | .left_align("Saves the shell's history into a file.", " "); 84 | if (!_enable_save_history) 85 | { 86 | _info_indenter 87 | .left_align( 88 | "This feature is currently disabled. You can enable it by using the " 89 | "-H command-line option.", 90 | " " 91 | ); 92 | } 93 | _info_indenter 94 | .empty_line() 95 | .left_align("#pragma wave quit", " ") 96 | .left_align("Terminates the preprocessor.", " ") 97 | .flush() 98 | ; 99 | } 100 | 101 | void preshell_preprocessing_hooks::display_macro_names( 102 | const std::set& names_ 103 | ) 104 | { 105 | _info_indenter 106 | .raw(boost::algorithm::join(names_, "\n")) 107 | .flush(); 108 | } 109 | 110 | void preshell_preprocessing_hooks::save_history( 111 | const std::string& filename_ 112 | ) 113 | { 114 | std::ofstream f(filename_.c_str()); 115 | if (f) 116 | { 117 | f 118 | << "// Created using Preprocessor shell " << version() << std::endl 119 | << std::endl; 120 | BOOST_FOREACH(const std::string& l, _history) 121 | { 122 | f << l << std::endl; 123 | if (!f) 124 | { 125 | _error_indenter 126 | .left_align("Error writing to file " + filename_) 127 | .flush(); 128 | return; 129 | } 130 | } 131 | } 132 | else 133 | { 134 | _error_indenter 135 | .left_align("Error opening file " + filename_) 136 | .flush(); 137 | } 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /app/readline_shell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include "readline_shell.hpp" 18 | #include "interrupt_handler_override.hpp" 19 | #include "override_guard.hpp" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | #include 37 | 38 | namespace 39 | { 40 | bool starts_with(const std::string& prefix_, const std::string& s_) 41 | { 42 | if (prefix_.length() <= s_.length()) 43 | { 44 | for (int i = 0, e = prefix_.length(); i != e; ++i) 45 | { 46 | if (prefix_[i] != s_[i]) 47 | { 48 | return false; 49 | } 50 | } 51 | return true; 52 | } 53 | else 54 | { 55 | return false; 56 | } 57 | } 58 | 59 | void display(just::console::color c_, const std::string& s_) 60 | { 61 | if (s_ != "") 62 | { 63 | just::console::text_color(c_); 64 | std::cout << s_; 65 | just::console::reset(); 66 | std::cout << std::endl; 67 | } 68 | } 69 | } 70 | 71 | readline_shell* readline_shell::_instance = 0; 72 | 73 | readline_shell::~readline_shell() 74 | { 75 | assert(_instance); 76 | _instance = 0; 77 | } 78 | 79 | readline_shell::readline_shell( 80 | const preshell::config& config_, 81 | const std::vector& macros_ 82 | ) : 83 | shell(config_, macros_) 84 | { 85 | assert(!_instance); 86 | _instance = this; 87 | 88 | display_initialisation_diagnostic(); 89 | } 90 | 91 | void readline_shell::add_history(const std::string& s_) 92 | { 93 | ::add_history(s_.c_str()); 94 | } 95 | 96 | void readline_shell::run() 97 | { 98 | using boost::bind; 99 | 100 | override_guard 101 | ovr1(rl_attempted_completion_function, tab_completion); 102 | interrupt_handler_override ovr2(bind(&readline_shell::cancel_operation,this)); 103 | 104 | while (char* l = readline(prompt().c_str())) 105 | { 106 | const std::string line(l); 107 | free(l); 108 | line_available(line); 109 | } 110 | } 111 | 112 | char* readline_shell::tab_generator(const char* text_, int state_) 113 | { 114 | using std::copy; 115 | 116 | assert(_instance); 117 | 118 | static std::list values; 119 | static std::list::const_iterator pos; 120 | 121 | if (!state_) // init 122 | { 123 | const std::string text(text_); 124 | values.clear(); 125 | for ( 126 | preshell::macro_map::const_iterator 127 | i = _instance->context().macros.begin(), 128 | e = _instance->context().macros.end(); 129 | i != e; 130 | ++i 131 | ) 132 | { 133 | const std::string s = 134 | i->second.has_parameters() ? i->first + "(" : i->first; 135 | if (starts_with(text_, s)) 136 | { 137 | values.push_back(s); 138 | } 139 | } 140 | for ( 141 | std::vector::const_iterator 142 | i = directives.begin(), 143 | e = directives.end(); 144 | i != e; 145 | ++i 146 | ) 147 | { 148 | if (starts_with(text_, *i)) 149 | { 150 | values.push_back(*i); 151 | } 152 | } 153 | pos = values.begin(); 154 | } 155 | 156 | if (pos == values.end()) 157 | { 158 | return 0; 159 | } 160 | else 161 | { 162 | char* s = new char[pos->length() + 1]; 163 | copy(pos->begin(), pos->end(), s); 164 | s[pos->length()] = 0; 165 | ++pos; 166 | return s; 167 | } 168 | } 169 | 170 | char** readline_shell::tab_completion(const char* text_, int start_, int end_) 171 | { 172 | return rl_completion_matches(const_cast(text_), &tab_generator); 173 | } 174 | 175 | void readline_shell::display_normal(const std::string& s_) const 176 | { 177 | if (s_ != "") 178 | { 179 | std::cout << s_ << std::endl; 180 | } 181 | } 182 | 183 | void readline_shell::display_info(const std::string& s_) const 184 | { 185 | display(just::console::color::bright_cyan, s_); 186 | } 187 | 188 | void readline_shell::display_error(const std::string& s_) const 189 | { 190 | display(just::console::color::bright_red, s_); 191 | } 192 | 193 | unsigned int readline_shell::width() const 194 | { 195 | struct winsize w; 196 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 197 | return w.ws_col; 198 | } 199 | 200 | 201 | -------------------------------------------------------------------------------- /lib/core/parse_config.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2014, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | 32 | #include 33 | 34 | using namespace preshell; 35 | 36 | namespace 37 | { 38 | void append( 39 | std::vector& v1_, 40 | const std::vector& v2_ 41 | ) 42 | { 43 | v1_.insert(v1_.end(), v2_.begin(), v2_.end()); 44 | } 45 | 46 | std::string run(const std::string& cmd_) 47 | { 48 | const std::vector cmd = boost::assign::list_of 49 | ("/bin/sh")("-c")(cmd_); 50 | const just::process::output o = just::process::run(cmd, ""); 51 | return o.standard_output() + o.standard_error(); 52 | } 53 | 54 | std::vector get_gcc_default_sysinclude( 55 | const std::string& gcc_path_ 56 | ) 57 | { 58 | using boost::algorithm::trim_left_copy; 59 | using boost::algorithm::split; 60 | using boost::is_any_of; 61 | using boost::starts_with; 62 | 63 | using std::vector; 64 | using std::string; 65 | 66 | const string s = run(gcc_path_ + " -v -xc++ -"); 67 | 68 | vector lines; 69 | split(lines, s, is_any_of("\n")); 70 | 71 | vector result; 72 | bool in_sysinclude = false; 73 | BOOST_FOREACH(const string& line, lines) 74 | { 75 | if (in_sysinclude) 76 | { 77 | if (starts_with(line, " ")) 78 | { 79 | result.push_back(trim_left_copy(line)); 80 | } 81 | else 82 | { 83 | return result; 84 | } 85 | } 86 | else if (starts_with(line, "#include <")) 87 | { 88 | in_sysinclude = true; 89 | } 90 | } 91 | 92 | return result; 93 | } 94 | 95 | std::string get_gcc_builtin_macros(const std::string& gcc_path_) 96 | { 97 | return run(gcc_path_ + "-dM -E -"); 98 | } 99 | } 100 | 101 | parse_config_result preshell::parse_config( 102 | config& cfg_, 103 | std::vector& macros_, 104 | std::vector& preprocess_, 105 | 106 | int argc_, 107 | const char* argv_[], 108 | 109 | std::ostream* out_, 110 | std::ostream* err_ 111 | ) 112 | { 113 | using boost::program_options::options_description; 114 | using boost::program_options::variables_map; 115 | using boost::program_options::store; 116 | using boost::program_options::notify; 117 | using boost::program_options::parse_command_line; 118 | using boost::program_options::value; 119 | 120 | using std::vector; 121 | using std::string; 122 | 123 | vector include_path; 124 | vector sysinclude_path; 125 | string gcc; 126 | 127 | options_description desc("Options"); 128 | desc.add_options() 129 | ("help", "Display help") 130 | ("sysinclude,I", 131 | value(&sysinclude_path), 132 | "Additional system include directory") 133 | ("include,i", value(&include_path), "Additional include directory") 134 | ("define,D", value(¯os_), "Define macro (format: name[=[value]])") 135 | ("preprocess,p", value(&preprocess_), "Preprocess code at startup") 136 | ("logdef,l", "Log macro definitions and undefinitions") 137 | ("no-warning,w", "Disable warning messages") 138 | ("suppress-empty-lines,e", "Suppress empty lines in output") 139 | ("gcc,g", 140 | value(&gcc), 141 | "Use the default sysinclude path of that gcc or clang binary") 142 | ("save_history,H", "Enable #pragma wave save_history(...)") 143 | ; 144 | 145 | try 146 | { 147 | variables_map vm; 148 | store(parse_command_line(argc_, argv_, desc), vm); 149 | notify(vm); 150 | 151 | if (vm.count("help")) 152 | { 153 | if (*out_) 154 | { 155 | *out_ << desc << std::endl; 156 | } 157 | return exit_without_error; 158 | } 159 | else 160 | { 161 | if (!gcc.empty()) 162 | { 163 | cfg_.sysinclude_path = get_gcc_default_sysinclude(gcc); 164 | cfg_.builtin_macro_definitions = get_gcc_builtin_macros(gcc); 165 | } 166 | append(cfg_.sysinclude_path, sysinclude_path); 167 | append(cfg_.include_path, include_path); 168 | cfg_.log_macro_definitions = vm.count("logdef") || vm.count("l"); 169 | cfg_.enable_warnings = !(vm.count("no-warning") || vm.count("w")); 170 | cfg_.suppress_empty_lines_in_output = 171 | vm.count("suppress-empty-lines") || vm.count("e"); 172 | cfg_.enable_save_history = vm.count("save_history") || vm.count("H"); 173 | 174 | return run_shell; 175 | } 176 | } 177 | catch (const boost::program_options::error& e_) 178 | { 179 | if (err_) 180 | { 181 | *err_ << e_.what() << "\n\n" << desc << std::endl; 182 | } 183 | return exit_with_error; 184 | } 185 | catch (const std::exception& e_) 186 | { 187 | if (err_) 188 | { 189 | *err_ << e_.what() << std::endl; 190 | } 191 | return exit_with_error; 192 | } 193 | } 194 | 195 | 196 | -------------------------------------------------------------------------------- /include/just/console.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JUST_CONSOLE_HPP 2 | #define JUST_CONSOLE_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2014. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifdef _WIN32 10 | # include 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | namespace just 17 | { 18 | namespace console 19 | { 20 | namespace impl 21 | { 22 | template 23 | class color 24 | { 25 | public: 26 | bool operator==(color c_) const { return _id == c_._id; } 27 | bool operator!=(color c_) const { return !(*this == c_); } 28 | 29 | static const color black; 30 | static const color red; 31 | static const color bright_red; 32 | static const color green; 33 | static const color bright_green; 34 | static const color yellow; 35 | static const color bright_yellow; 36 | static const color blue; 37 | static const color bright_blue; 38 | static const color magenta; 39 | static const color bright_magenta; 40 | static const color cyan; 41 | static const color bright_cyan; 42 | static const color gray; 43 | static const color white; 44 | private: 45 | color(int id_) : _id(id_) {} 46 | 47 | int _id; 48 | }; 49 | 50 | template const color color::black(0); 51 | template const color color::red(1); 52 | template const color color::bright_red(2); 53 | template const color color::green(3); 54 | template const color color::bright_green(4); 55 | template const color color::yellow(5); 56 | template const color color::bright_yellow(6); 57 | template const color color::blue(7); 58 | template const color color::bright_blue(8); 59 | template const color color::magenta(9); 60 | template const color color::bright_magenta(10); 61 | template const color color::cyan(11); 62 | template const color color::bright_cyan(12); 63 | template const color color::gray(13); 64 | template const color color::white(14); 65 | 66 | template 67 | std::ostream& operator<<(std::ostream& o_, color c_) 68 | { 69 | if (c_ == color::black) { o_ << "black"; } 70 | else if (c_ == color::red) { o_ << "red"; } 71 | else if (c_ == color::green) { o_ << "green"; } 72 | else if (c_ == color::yellow) { o_ << "yellow"; } 73 | else if (c_ == color::blue) { o_ << "blue"; } 74 | else if (c_ == color::magenta) { o_ << "magenta"; } 75 | else if (c_ == color::cyan) { o_ << "cyan"; } 76 | else if (c_ == color::gray) { o_ << "gray"; } 77 | else if (c_ == color::bright_red) { o_ << "bright_red"; } 78 | else if (c_ == color::bright_green) { o_ << "bright_green"; } 79 | else if (c_ == color::bright_yellow) { o_ << "bright_yellow"; } 80 | else if (c_ == color::bright_blue) { o_ << "bright_blue"; } 81 | else if (c_ == color::bright_magenta) { o_ << "bright_magenta"; } 82 | else if (c_ == color::bright_cyan) { o_ << "bright_cyan"; } 83 | else if (c_ == color::white) { o_ << "white"; } 84 | else { assert(!"Invalid color value"); } 85 | 86 | return o_; 87 | } 88 | } 89 | 90 | typedef impl::color color; 91 | 92 | #ifdef _WIN32 93 | inline void text_color(color c_) 94 | { 95 | WORD c = 0; 96 | if (c_ == color::black) 97 | { 98 | c = 0; 99 | } 100 | else if (c_ == color::red) 101 | { 102 | c = FOREGROUND_RED; 103 | } 104 | else if (c_ == color::green) 105 | { 106 | c = FOREGROUND_GREEN; 107 | } 108 | else if (c_ == color::yellow) 109 | { 110 | c = FOREGROUND_RED | FOREGROUND_GREEN; 111 | } 112 | else if (c_ == color::blue) 113 | { 114 | c = FOREGROUND_BLUE; 115 | } 116 | else if (c_ == color::magenta) 117 | { 118 | c = FOREGROUND_RED | FOREGROUND_BLUE; 119 | } 120 | else if (c_ == color::cyan) 121 | { 122 | c = FOREGROUND_GREEN | FOREGROUND_BLUE; 123 | } 124 | else if (c_ == color::gray) 125 | { 126 | c = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 127 | } 128 | else if (c_ == color::bright_red) 129 | { 130 | c = FOREGROUND_INTENSITY | FOREGROUND_RED; 131 | } 132 | else if (c_ == color::bright_green) 133 | { 134 | c = FOREGROUND_INTENSITY | FOREGROUND_GREEN; 135 | } 136 | else if (c_ == color::bright_yellow) 137 | { 138 | c = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN; 139 | } 140 | else if (c_ == color::bright_blue) 141 | { 142 | c = FOREGROUND_INTENSITY | FOREGROUND_BLUE; 143 | } 144 | else if (c_ == color::bright_magenta) 145 | { 146 | c = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE; 147 | } 148 | else if (c_ == color::bright_cyan) 149 | { 150 | c = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE; 151 | } 152 | else if (c_ == color::white) 153 | { 154 | c = 155 | FOREGROUND_INTENSITY 156 | | FOREGROUND_RED 157 | | FOREGROUND_GREEN 158 | | FOREGROUND_BLUE; 159 | } 160 | else 161 | { 162 | assert(!"Invalid color value"); 163 | } 164 | 165 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); 166 | } 167 | 168 | inline void reset() 169 | { 170 | text_color(color::gray); 171 | } 172 | 173 | #else 174 | inline void text_color(color c_) 175 | { 176 | if (c_ == color::black) { std::cout << "\033[30m"; } 177 | else if (c_ == color::red) { std::cout << "\033[31m"; } 178 | else if (c_ == color::green) { std::cout << "\033[32m"; } 179 | else if (c_ == color::yellow) { std::cout << "\033[33m"; } 180 | else if (c_ == color::blue) { std::cout << "\033[34m"; } 181 | else if (c_ == color::magenta) { std::cout << "\033[35m"; } 182 | else if (c_ == color::cyan) { std::cout << "\033[36m"; } 183 | else if (c_ == color::gray) { std::cout << "\033[37m"; } 184 | else if (c_ == color::bright_red) { std::cout << "\033[1;31m"; } 185 | else if (c_ == color::bright_green) { std::cout << "\033[1;32m"; } 186 | else if (c_ == color::bright_yellow) { std::cout << "\033[1;33m"; } 187 | else if (c_ == color::bright_blue) { std::cout << "\033[1;34m"; } 188 | else if (c_ == color::bright_magenta) { std::cout << "\033[1;35m"; } 189 | else if (c_ == color::bright_cyan) { std::cout << "\033[1;36m"; } 190 | else if (c_ == color::white) { std::cout << "\033[1;37m"; } 191 | else { assert(!"Invalid color value"); } 192 | } 193 | 194 | inline void reset() 195 | { 196 | std::cout << "\033[0m"; 197 | } 198 | #endif 199 | } 200 | } 201 | 202 | #endif 203 | 204 | -------------------------------------------------------------------------------- /lib/core/shell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | using namespace preshell; 30 | 31 | namespace 32 | { 33 | const char clang_compatibility_macros[] = 34 | "#ifndef __has_builtin\n" 35 | "# define __has_builtin(x) 0\n" 36 | "#endif\n" 37 | 38 | "#ifndef __has_feature\n" 39 | "# define __has_feature(x) 0\n" 40 | "#endif\n" 41 | 42 | "#ifndef __has_extension\n" 43 | "# define __has_extension __has_feature\n" 44 | "#endif\n" 45 | 46 | "#ifndef __has_attribute\n" 47 | "# define __has_attribute(x) 0\n" 48 | "#endif\n" 49 | 50 | "#ifndef __has_include\n" 51 | "# define __has_include(x) 0\n" 52 | "#endif\n" 53 | 54 | "#ifndef __has_include_next\n" 55 | "# define __has_include_next(x) 0\n" 56 | "#endif\n" 57 | 58 | "#ifndef __has_warning\n" 59 | "# define __has_warning(x) 0\n" 60 | "#endif\n" 61 | ; 62 | } 63 | 64 | shell::shell( 65 | const preshell::config& config_, 66 | const std::vector& macros_ 67 | ) : 68 | _info_indenter( 69 | boost::bind(&shell::width, static_cast(this)), 70 | boost::bind(&shell::display_info, static_cast(this), _1) 71 | ), 72 | _error_indenter( 73 | boost::bind(&shell::width, static_cast(this)), 74 | boost::bind(&shell::display_error, static_cast(this), _1) 75 | ), 76 | _config(config_), 77 | _context( 78 | new result( 79 | context::initial( 80 | config_, 81 | macros_, 82 | _info_indenter, 83 | _error_indenter, 84 | _history 85 | ) 86 | ) 87 | ), 88 | _initial_context(_context->pp_context) 89 | { 90 | indenter ignore(&always<80>, throw_away); 91 | 92 | _context = 93 | precompile( 94 | clang_compatibility_macros, 95 | _context->pp_context, 96 | _config, 97 | ignore, 98 | _history 99 | ); 100 | 101 | precompile_input(clang_compatibility_macros); 102 | 103 | if (!_config.builtin_macro_definitions.empty()) 104 | { 105 | _context = 106 | precompile( 107 | remove_protected_macro_definitions(_config.builtin_macro_definitions), 108 | _context->pp_context, 109 | _config, 110 | ignore, 111 | _history 112 | ); 113 | } 114 | 115 | _context->pp_context.line = 1; 116 | } 117 | 118 | shell::~shell() {} 119 | 120 | const std::vector shell::directives = 121 | boost::assign::list_of 122 | ("#define") 123 | ("#elif") 124 | ("#else") 125 | ("#endif") 126 | ("#error") 127 | ("#if") 128 | ("#ifdef") 129 | ("#ifndef") 130 | ("#include") 131 | ("#line") 132 | ("#pragma") 133 | ("#undef") 134 | 135 | ("_Pragma(\"") // Supported by Wave 136 | ; 137 | 138 | void shell::cancel_operation() 139 | { 140 | preshell::cancel(); 141 | } 142 | 143 | const preshell::config& shell::config() const 144 | { 145 | return _config; 146 | } 147 | 148 | const preshell::context& shell::context() const 149 | { 150 | return _context->pp_context; 151 | } 152 | 153 | void shell::display_splash() const 154 | { 155 | using boost::bind; 156 | 157 | const std::string version_desc = 158 | #include "version_desc.hpp" 159 | ; 160 | 161 | indenter 162 | ind(bind(&shell::width, this), bind(&shell::display_normal, this, _1)); 163 | 164 | ind 165 | .raw("/*") 166 | .left_align("Preprocessor shell " + preshell::version(), " * "); 167 | 168 | if (!version_desc.empty()) 169 | { 170 | ind.left_align(version_desc, " * "); 171 | } 172 | 173 | ind 174 | .raw(" * ") 175 | .left_align( 176 | "Preshell Copyright (C) 2013 Abel Sinkovics (abel@sinkovics.hu)", 177 | " * " 178 | ) 179 | .left_align( 180 | "This program comes with ABSOLUTELY NO WARRANTY. This is free software," 181 | " and you are welcome to redistribute it under certain conditions;" 182 | " for details visit .", 183 | " * " 184 | ) 185 | .raw(" * ") 186 | .left_align("Based on", " * ") 187 | .left_align( 188 | preshell::wave_version(), 189 | " * ", 190 | " * Boost.Wave " 191 | ) 192 | .left_align( 193 | preshell::readline_version(), 194 | " * ", 195 | " * Readline " 196 | ) 197 | .raw(" * ") 198 | .left_align("Getting help: #pragma wave preshell_help", " * ") 199 | .raw(" */") 200 | .flush(); 201 | } 202 | 203 | void shell::line_available(const std::string& s_) 204 | { 205 | if (!s_.empty()) 206 | { 207 | if (s_ != _prev_line) 208 | { 209 | add_history(s_); 210 | _prev_line = s_; 211 | } 212 | if (!is_pragma_usage(s_)) 213 | { 214 | _history.push_back(s_); 215 | } 216 | } 217 | 218 | process_line(s_); 219 | } 220 | 221 | std::string shell::prompt() const 222 | { 223 | return 224 | _buffer == "" ? 225 | (_context->pp_context.tokens_skipped() ? "#if 0 ...> " : "> ") : 226 | ">>>> "; 227 | } 228 | 229 | void shell::precompile_input(const std::string& s_) 230 | { 231 | _context = 232 | precompile(s_, _context->pp_context, _config, _info_indenter, _history); 233 | } 234 | 235 | void shell::display_output_if_available() const 236 | { 237 | if (_context->output != "") 238 | { 239 | display_normal(_context->output); 240 | } 241 | } 242 | 243 | void shell::display_info_if_available() const 244 | { 245 | if (_context->info != "") 246 | { 247 | display_info(_context->info); 248 | } 249 | } 250 | 251 | void shell::display_error_if_available() const 252 | { 253 | if (_context->error != "") 254 | { 255 | display_error(_context->error); 256 | } 257 | } 258 | 259 | void shell::display_initialisation_diagnostic() const 260 | { 261 | display_error_if_available(); 262 | } 263 | 264 | const std::list& shell::history() const 265 | { 266 | return _history; 267 | } 268 | 269 | void shell::replay_history() 270 | { 271 | _context->pp_context = _initial_context; 272 | BOOST_FOREACH(const std::string& s, _history) 273 | { 274 | process_line(s); 275 | } 276 | } 277 | 278 | template 279 | void shell::process_line(const std::string& s_) 280 | { 281 | _buffer += (_buffer == "" ? "" : "\n") + s_; 282 | 283 | if (!preshell::continuation_needed(_buffer, _config)) 284 | { 285 | assert(!_context->replay_history); 286 | 287 | _context = 288 | precompile( 289 | _buffer, 290 | _context->pp_context, 291 | _config, 292 | _info_indenter, 293 | _history 294 | ); 295 | _buffer = ""; 296 | 297 | if (EnableOutput) 298 | { 299 | display_output_if_available(); 300 | display_info_if_available(); 301 | display_error_if_available(); 302 | } 303 | 304 | if (_context->replay_history) 305 | { 306 | _context->replay_history = false; 307 | replay_history(); 308 | } 309 | } 310 | } 311 | 312 | -------------------------------------------------------------------------------- /lib/core/preshell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace preshell; 32 | 33 | namespace 34 | { 35 | bool cancel_preprocessing = false; 36 | 37 | template 38 | bool protected_name(const String& s) 39 | { 40 | return 41 | s == "__DATE__" 42 | || s == "__STDC__" 43 | || s == "__TIME__" 44 | || s == "__LINE__" 45 | || s == "__FILE__" 46 | || s == "__cplusplus"; 47 | } 48 | 49 | template 50 | void delete_macros(It begin_, It end_, Context& context_) 51 | { 52 | for (; begin_ != end_; ++begin_) 53 | { 54 | if (!protected_name(*begin_)) 55 | { 56 | context_.remove_macro_definition(*begin_, true); 57 | } 58 | } 59 | } 60 | 61 | template 62 | void delete_all_macros(Context& context_) 63 | { 64 | const std::vector 65 | v(context_.macro_names_begin(), context_.macro_names_end()); 66 | delete_macros(v.begin(), v.end(), context_); 67 | } 68 | 69 | template 70 | void add_macros(It begin_, It end_, Context& context_) 71 | { 72 | for (; begin_ != end_; ++begin_) 73 | { 74 | if (!protected_name(begin_->first)) 75 | { 76 | std::vector parameters(begin_->second.parameters()); 77 | typename Context::token_sequence_type 78 | definition( 79 | begin_->second.definition().begin(), 80 | begin_->second.definition().end() 81 | ); 82 | 83 | const token::string_type name(begin_->first.c_str()); 84 | 85 | context_.add_macro_definition( 86 | name, 87 | begin_->second.position(), 88 | begin_->second.has_parameters(), 89 | parameters, 90 | definition, 91 | false 92 | ); 93 | } 94 | } 95 | } 96 | 97 | std::string to_directive(if_state s_) 98 | { 99 | switch (s_) 100 | { 101 | case if0_then: return "#if 0\n"; 102 | case if0_else: return "#if 0\n#else\n"; 103 | case if1_then: return "#if 1\n"; 104 | case if1_else: return "#if 1\n#else\n"; 105 | } 106 | return ""; // Keep gcc happy 107 | } 108 | 109 | template 110 | std::string prepare_input( 111 | const std::string& input_, 112 | It begin_, 113 | It end_, 114 | const std::string& file_, 115 | int line_ 116 | ) 117 | { 118 | std::ostringstream s; 119 | for (; begin_ != end_; ++begin_) 120 | { 121 | s << to_directive(*begin_); 122 | } 123 | s << "#line " << line_ << " \""; 124 | string_escape(file_, s); 125 | s << "\"\n" << input_ << '\n'; 126 | return s.str(); 127 | } 128 | 129 | std::string join_with_new_line(const std::string& a_, const std::string& b_) 130 | { 131 | return !(a_.empty() || b_.empty()) ? a_ + "\n" + b_ : a_ + b_; 132 | } 133 | } 134 | 135 | namespace 136 | { 137 | void display_on_stream(std::ostream* out_, std::string s_) 138 | { 139 | *out_ << s_; 140 | } 141 | } 142 | 143 | result_ptr preshell::precompile( 144 | const std::string& input_, 145 | const context& context_, 146 | const config& config_, 147 | indenter& indenter_, 148 | const std::list& history_ 149 | ) 150 | { 151 | using boost::bind; 152 | 153 | cancel_preprocessing = false; 154 | result_ptr r = result_ptr(new result()); 155 | 156 | try 157 | { 158 | const std::string 159 | input( 160 | prepare_input( 161 | input_, 162 | context_.if_states.begin(), 163 | context_.if_states.end(), 164 | context_.filename, 165 | context_.line 166 | ) 167 | ); 168 | 169 | std::ostringstream info; 170 | indenter info_indenter(indenter_, bind(display_on_stream, &info, _1)); 171 | 172 | std::ostringstream error; 173 | indenter error_indenter(indenter_, bind(display_on_stream, &error, _1)); 174 | 175 | bool log_macro_definitions = false; 176 | bool enable_save_history = false; 177 | 178 | wave_context_ptr context = 179 | create_context( 180 | input, 181 | r->pp_context.if_states, 182 | config_, 183 | info_indenter, 184 | error_indenter, 185 | log_macro_definitions, 186 | history_, 187 | enable_save_history, 188 | r->replay_history 189 | ); 190 | delete_all_macros(*context); 191 | add_macros(context_.macros.begin(), context_.macros.end(), *context); 192 | 193 | log_macro_definitions = config_.log_macro_definitions; 194 | enable_save_history = config_.enable_save_history; 195 | 196 | std::list warning_list; 197 | const std::string 198 | output = join( 199 | context->begin(), 200 | context->end(), 201 | config_.enable_warnings ? &warning_list : 0, 202 | cancel_preprocessing, 203 | config_.suppress_empty_lines_in_output 204 | ); 205 | const std::string warnings = boost::algorithm::join(warning_list, "\n"); 206 | 207 | if (cancel_preprocessing) 208 | { 209 | r->error = 210 | (warnings.empty() ? "" : warnings + "\n") + "Operation cancelled"; 211 | } 212 | else 213 | { 214 | // The last character of the output is the extra new line 215 | r->output = 216 | output.empty() ? "" : std::string(output.begin(), output.end() - 1); 217 | r->info = info.str(); 218 | r->error = join_with_new_line(error.str(), warnings); 219 | r->pp_context.macros = get_macros(*context); 220 | r->pp_context.filename = context->get_main_pos().get_file().c_str(); 221 | r->pp_context.line = context->get_main_pos().get_line(); 222 | } 223 | } 224 | catch (const boost::wave::cpp_exception& e) 225 | { 226 | r->error = format_error(e); 227 | r->pp_context = context_; 228 | } 229 | catch (const std::exception& e) 230 | { 231 | r->error = e.what(); 232 | r->pp_context = context_; 233 | } 234 | catch (...) 235 | { 236 | r->error = "An unknown exception was thrown"; 237 | r->pp_context = context_; 238 | } 239 | 240 | return r; 241 | } 242 | 243 | void preshell::cancel() 244 | { 245 | cancel_preprocessing = true; 246 | } 247 | 248 | bool preshell::continuation_needed(const std::string& s_, const config& config_) 249 | { 250 | return s_ != "" && s_[s_.length() - 1] == '\\'; 251 | } 252 | 253 | void preshell::string_escape(const std::string& s_, std::ostream& out_) 254 | { 255 | for (std::string::const_iterator i = s_.begin(), e = s_.end(); i != e; ++i) 256 | { 257 | switch (*i) 258 | { 259 | case '\\': 260 | case '"': 261 | case '\'': 262 | out_ << '\\'; 263 | break; 264 | default: 265 | break; 266 | } 267 | out_ << *i; 268 | } 269 | } 270 | 271 | std::string preshell::string_escape(const std::string& s_) 272 | { 273 | std::ostringstream s; 274 | string_escape(s_, s); 275 | return s.str(); 276 | } 277 | 278 | std::string preshell::remove_protected_macro_definitions(const std::string& s_) 279 | { 280 | std::istringstream is(s_); 281 | std::ostringstream os; 282 | for (std::string line; std::getline(is, line);) 283 | { 284 | std::istringstream il(line); 285 | std::string s; 286 | if ( 287 | !( 288 | il >> s && (s == "#define" || (s == "#" && il >> s && s == "define")) 289 | && il >> s && protected_name(s) 290 | ) 291 | ) 292 | { 293 | os << line << std::endl; 294 | } 295 | } 296 | return os.str(); 297 | } 298 | 299 | bool preshell::recoverable(const boost::wave::cpp_exception& e_) 300 | { 301 | return 302 | e_.is_recoverable() && 303 | e_.get_errorcode() 304 | != boost::wave::preprocess_exception::missing_matching_endif; 305 | } 306 | 307 | std::string preshell::format_error(const boost::wave::cpp_exception& e_) 308 | { 309 | std::ostringstream s; 310 | s 311 | << e_.file_name() << ":" << e_.line_no() << ":" << e_.column_no() << ": " 312 | << e_.description(); 313 | return s.str(); 314 | } 315 | 316 | 317 | -------------------------------------------------------------------------------- /include/just/test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JUST_TEST_HPP 2 | #define JUST_TEST_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2014. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace just 16 | { 17 | namespace test 18 | { 19 | /* 20 | * assertion_failed 21 | */ 22 | class assertion_failed 23 | { 24 | public: 25 | assertion_failed( 26 | const std::string& msg_, 27 | const std::string& filename_, 28 | int line_ 29 | ) : 30 | _msg(msg_), 31 | _filename(filename_), 32 | _line(line_) 33 | {} 34 | 35 | const std::string& msg() const 36 | { 37 | return _msg; 38 | } 39 | 40 | const std::string& filename() const 41 | { 42 | return _filename; 43 | } 44 | 45 | int line() const 46 | { 47 | return _line; 48 | } 49 | private: 50 | std::string _msg; 51 | std::string _filename; 52 | int _line; 53 | }; 54 | 55 | /* 56 | * test_case 57 | */ 58 | class test_case 59 | { 60 | public: 61 | test_case(const std::string& name_, void (*f_)()) : 62 | _f(f_), 63 | _name(name_) 64 | {} 65 | 66 | bool run(std::ostream& report_) const 67 | { 68 | report_ << "Running " << _name << std::endl; 69 | try 70 | { 71 | _f(); 72 | return true; 73 | } 74 | catch (const assertion_failed& e_) 75 | { 76 | report_ 77 | << " Assertion failed" << std::endl 78 | << " " << e_.msg() << std::endl 79 | << " Test case: " << _name << std::endl 80 | << " Location: " << e_.filename() << ":" << e_.line() <::const_iterator 119 | i = _cases.begin(), 120 | e = _cases.end(); 121 | i != e; 122 | ++i 123 | ) 124 | { 125 | if (!i->run(report_)) 126 | { 127 | ++failures; 128 | } 129 | } 130 | report_ << _cases.size() << " test cases finished." << std::endl; 131 | if (failures > 0) 132 | { 133 | report_ << " " << failures << " failed." << std::endl; 134 | } 135 | else 136 | { 137 | report_ << " All succeeded." << std::endl; 138 | } 139 | return failures == 0; 140 | } 141 | private: 142 | std::vector _cases; 143 | }; 144 | 145 | /* 146 | * singleton 147 | */ 148 | // This assumes, that pointers are 0 initialised at startup 149 | // It never destroys the object 150 | template 151 | class singleton 152 | { 153 | public: 154 | static T& get() 155 | { 156 | if (!_t) 157 | { 158 | _t = new T(); 159 | } 160 | return *_t; 161 | } 162 | private: 163 | static T* _t; 164 | }; 165 | 166 | template 167 | T* singleton::_t = 0; 168 | 169 | /* 170 | * run 171 | */ 172 | inline int run(int argc_, char* argv_[]) 173 | { 174 | return singleton::get().run(std::cerr) ? 0 : 1; 175 | } 176 | 177 | /* 178 | * assert_msg 179 | */ 180 | class assert_msg 181 | { 182 | public: 183 | assert_msg(const std::string& filename_, int line_) : 184 | _filename(filename_), 185 | _line(line_) 186 | {} 187 | 188 | void operator()(const std::string& msg_, bool v_) const 189 | { 190 | if (!v_) 191 | { 192 | throw assertion_failed(msg_, _filename, _line); 193 | } 194 | } 195 | 196 | void operator()(bool v_) const 197 | { 198 | operator()("Assertion failed", v_); 199 | } 200 | private: 201 | std::string _filename; 202 | int _line; 203 | }; 204 | 205 | /* 206 | * assert operations 207 | */ 208 | #ifdef JUST_TEST_ASSERT_OP 209 | # error JUST_TEST_ASSERT_OP already defined 210 | #endif 211 | #define JUST_TEST_ASSERT_OP(name, pred) \ 212 | class name \ 213 | { \ 214 | public: \ 215 | name(const std::string& filename_, int line_) : \ 216 | _assert(filename_, line_) \ 217 | {} \ 218 | \ 219 | template \ 220 | void operator()(A a_, B b_) const \ 221 | { \ 222 | std::ostringstream s; \ 223 | s << "Expected: " << a_ << " " #pred " " << b_; \ 224 | _assert(s.str(), a_ pred b_); \ 225 | } \ 226 | private: \ 227 | assert_msg _assert; \ 228 | } 229 | 230 | JUST_TEST_ASSERT_OP(assert_equal, ==); 231 | JUST_TEST_ASSERT_OP(assert_not_equal, !=); 232 | JUST_TEST_ASSERT_OP(assert_less, <); 233 | JUST_TEST_ASSERT_OP(assert_less_equal, <=); 234 | JUST_TEST_ASSERT_OP(assert_greater, >); 235 | JUST_TEST_ASSERT_OP(assert_greater_equal, >=); 236 | 237 | #undef JUST_TEST_ASSERT_OP 238 | } 239 | } 240 | 241 | /* 242 | * Test case definition 243 | */ 244 | #ifdef JUST_TEST_CASE 245 | # error JUST_TEST_CASE already defined 246 | #endif 247 | #define JUST_TEST_CASE(name) \ 248 | void just_test_test_case_f_ ## name(); \ 249 | \ 250 | struct just_test_test_case_ ## name \ 251 | { \ 252 | just_test_test_case_ ## name() \ 253 | { \ 254 | ::just::test::singleton< ::just::test::test_manager >::get().add( \ 255 | #name, &just_test_test_case_f_ ## name \ 256 | ); \ 257 | } \ 258 | }; \ 259 | just_test_test_case_ ## name just_test_test_case_instance_ ## name; \ 260 | \ 261 | void just_test_test_case_f_ ## name() 262 | 263 | /* 264 | * Assertion macros 265 | */ 266 | #ifdef JUST_ASSERT 267 | # error JUST_ASSERT already defined 268 | #endif 269 | #define JUST_ASSERT ::just::test::assert_msg(__FILE__, __LINE__) 270 | 271 | #ifdef JUST_ASSERT_EQUAL 272 | # error JUST_ASSERT_EQUAL already defined 273 | #endif 274 | #define JUST_ASSERT_EQUAL ::just::test::assert_equal(__FILE__, __LINE__) 275 | 276 | #ifdef JUST_ASSERT_NOT_EQUAL 277 | # error JUST_ASSERT_NOT_EQUAL already defined 278 | #endif 279 | #define JUST_ASSERT_NOT_EQUAL ::just::test::assert_not_equal(__FILE__, __LINE__) 280 | 281 | #ifdef JUST_ASSERT_LESS 282 | # error JUST_ASSERT_LESS already defined 283 | #endif 284 | #define JUST_ASSERT_LESS ::just::test::assert_less(__FILE__, __LINE__) 285 | 286 | #ifdef JUST_ASSERT_LESS_EQUAL 287 | # error JUST_ASSERT_LESS_EQUAL already defined 288 | #endif 289 | #define JUST_ASSERT_LESS_EQUAL \ 290 | ::just::test::assert_less_equal(__FILE__, __LINE__) 291 | 292 | #ifdef JUST_ASSERT_GREATER 293 | # error JUST_ASSERT_GREATER already defined 294 | #endif 295 | #define JUST_ASSERT_GREATER ::just::test::assert_greater(__FILE__, __LINE__) 296 | 297 | #ifdef JUST_ASSERT_GREATER_EQUAL 298 | # error JUST_ASSERT_GREATER_EQUAL already defined 299 | #endif 300 | #define JUST_ASSERT_GREATER_EQUAL \ 301 | ::just::test::assert_greater_equal(__FILE__, __LINE__) 302 | 303 | #ifdef JUST_ASSERT_THROWS 304 | # error JUST_ASSERT_THROWS 305 | #endif 306 | #define JUST_ASSERT_THROWS(exc, expr) \ 307 | { \ 308 | bool __just_test_expression_threw = false; \ 309 | try \ 310 | { \ 311 | (expr); \ 312 | } \ 313 | catch (const exc&) \ 314 | { \ 315 | __just_test_expression_threw = true; \ 316 | } \ 317 | catch (...) \ 318 | { \ 319 | throw ::just::test::assertion_failed( \ 320 | "Expected to throw " #exc " but threw something else.", \ 321 | __FILE__, \ 322 | __LINE__ \ 323 | ); \ 324 | } \ 325 | if (!__just_test_expression_threw) \ 326 | { \ 327 | throw \ 328 | ::just::test::assertion_failed( \ 329 | "Expected to throw " #exc, \ 330 | __FILE__, \ 331 | __LINE__ \ 332 | ); \ 333 | } \ 334 | } 335 | 336 | #ifdef JUST_ASSERT_THROWS_SOMETHING 337 | # error JUST_ASSERT_THROWS_SOMETHING 338 | #endif 339 | #define JUST_ASSERT_THROWS_SOMETHING(expr) \ 340 | { \ 341 | bool __just_test_expression_threw = false; \ 342 | try \ 343 | { \ 344 | (expr); \ 345 | } \ 346 | catch (...) { \ 347 | __just_test_expression_threw = true; \ 348 | } \ 349 | if (!__just_test_expression_threw) \ 350 | { \ 351 | throw \ 352 | ::just::test::assertion_failed( \ 353 | "Expected to throw ", \ 354 | __FILE__, \ 355 | __LINE__ \ 356 | ); \ 357 | } \ 358 | } 359 | 360 | /* 361 | * Macro for defining a main function 362 | */ 363 | #ifdef JUST_TEST_DEFINE_MAIN 364 | # error JUST_TEST_DEFINE_MAIN already defined 365 | #endif 366 | #define JUST_TEST_DEFINE_MAIN \ 367 | int main(int argc_, char* argv_[]) \ 368 | { \ 369 | return ::just::test::run(argc_, argv_); \ 370 | } 371 | 372 | #endif 373 | 374 | -------------------------------------------------------------------------------- /include/preshell/preshell_preprocessing_hooks.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PRESHELL_PRESHELL_PREPROCESSING_HOOKS_HPP 2 | #define PRESHELL_PRESHELL_PREPROCESSING_HOOKS_HPP 3 | 4 | // Preshell - Interactive C/C++ preprocessor shell 5 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | namespace preshell 41 | { 42 | class preshell_preprocessing_hooks : 43 | public boost::wave::context_policies::default_preprocessing_hooks 44 | { 45 | private: 46 | void save_history(const std::string& filename_); 47 | public: 48 | preshell_preprocessing_hooks( 49 | std::list& if_states_, 50 | indenter& info_indenter_, 51 | indenter& error_indenter_, 52 | const bool& log_macro_definitions_, 53 | const std::list& history_, 54 | const bool& enable_save_history_, 55 | bool& replay_history_ 56 | ); 57 | 58 | template 59 | bool interpret_pragma( 60 | const Context& ctx_, 61 | Container& pending_, 62 | const typename Context::token_type& option_, 63 | const Container& values_, 64 | const typename Context::token_type& pragma_token_ 65 | ) 66 | { 67 | if (option_.get_value() == "quit") 68 | { 69 | exit(0); 70 | } 71 | else if (option_.get_value() == "preshell_help") 72 | { 73 | display_help(); 74 | return true; 75 | } 76 | else if (option_.get_value() == "macro_names") 77 | { 78 | display_macro_names(macro_names(ctx_)); 79 | return true; 80 | } 81 | else if (option_.get_value() == "macros") 82 | { 83 | using boost::algorithm::join; 84 | using boost::adaptors::transformed; 85 | using boost::bind; 86 | 87 | _info_indenter 88 | .raw( 89 | join( 90 | macro_names(ctx_) 91 | | transformed(bind(macro_definition, &ctx_, _1)), 92 | "\n" 93 | ) 94 | ) 95 | .flush(); 96 | return true; 97 | } 98 | else if (option_.get_value() == "save_history") 99 | { 100 | if (values_.empty()) 101 | { 102 | _error_indenter 103 | .left_align("Usage: save_history()") 104 | .flush(); 105 | } 106 | else if (!_enable_save_history) 107 | { 108 | _error_indenter.left_align( 109 | "Saving history is disabled. You can enable it by the " 110 | "-H command line argument." 111 | ).flush(); 112 | } 113 | else 114 | { 115 | save_history(token_list_to_string(values_.begin(), values_.end())); 116 | } 117 | return true; 118 | } 119 | else if (option_.get_value() == "replay_history") 120 | { 121 | _replay_history = true; 122 | return true; 123 | } 124 | 125 | return false; 126 | } 127 | 128 | template 129 | bool evaluated_conditional_expression( 130 | const Context& ctx_, 131 | const Token& directive_, 132 | const Container& expression_, 133 | bool expression_value_ 134 | ) 135 | { 136 | if (directive_ == boost::wave::T_PP_ELIF && !_if_states.empty()) 137 | { 138 | _if_states.back() = expression_value_ ? if1_then : if0_then; 139 | } 140 | else 141 | { 142 | _if_states.push_back(expression_value_ ? if1_then : if0_then); 143 | } 144 | 145 | return 146 | base::evaluated_conditional_expression( 147 | ctx_, 148 | directive_, 149 | expression_, 150 | expression_value_ 151 | ); 152 | } 153 | 154 | template 155 | bool found_directive(const Context& ctx_, const Token& directive_) 156 | { 157 | if ( 158 | directive_ == boost::wave::T_PP_ELIF 159 | && !_if_states.empty() 160 | && _if_states.back() == if1_then 161 | ) 162 | { 163 | _if_states.back() = if1_else; 164 | } 165 | return base::found_directive(ctx_, directive_); 166 | } 167 | 168 | template 169 | const Token& generated_token(const Context& ctx_, const Token& token_) 170 | { 171 | process_token(token_); 172 | return base::generated_token(ctx_, token_); 173 | } 174 | 175 | template 176 | void skipped_token(const Context& ctx_, const Token& token_) 177 | { 178 | process_token(token_); 179 | } 180 | 181 | template 182 | void defined_macro( 183 | const Context& ctx_, 184 | const Token& macro_name_, 185 | bool is_functionlike_, 186 | const Parameters& parameters_, 187 | const Definition& definition_, 188 | bool is_predefined_ 189 | ) 190 | { 191 | if (_log_macro_definitions) 192 | { 193 | std::ostringstream s; 194 | s 195 | << macro_name_.get_position().get_file() 196 | << ":" << macro_name_.get_position().get_line() 197 | << ":" << macro_name_.get_position().get_column() 198 | << ": #define " << macro_name_.get_value(); 199 | _info_indenter.raw(s.str()).flush(); 200 | } 201 | } 202 | 203 | template 204 | void undefined_macro(const Context& ctx_, const Token& macro_name_) 205 | { 206 | if (_log_macro_definitions) 207 | { 208 | std::ostringstream s; 209 | s 210 | << macro_name_.get_position().get_file() 211 | << ":" << macro_name_.get_position().get_line() 212 | << ":" << macro_name_.get_position().get_column() 213 | << ": #undef " << macro_name_.get_value(); 214 | _info_indenter.raw(s.str()).flush(); 215 | } 216 | } 217 | 218 | private: 219 | typedef boost::wave::context_policies::default_preprocessing_hooks base; 220 | 221 | std::list& _if_states; 222 | indenter& _info_indenter; 223 | indenter& _error_indenter; 224 | const bool& _log_macro_definitions; 225 | const std::list& _history; 226 | const bool& _enable_save_history; 227 | bool& _replay_history; 228 | 229 | template 230 | void process_token(const Token& token_) 231 | { 232 | if (token_ == boost::wave::T_PP_ENDIF) 233 | { 234 | if (!_if_states.empty()) 235 | { 236 | _if_states.pop_back(); 237 | } 238 | } 239 | else if (token_ == boost::wave::T_PP_ELSE && !_if_states.empty()) 240 | { 241 | switch (_if_states.back()) 242 | { 243 | case if0_then: _if_states.back() = if0_else; break; 244 | case if1_then: _if_states.back() = if1_else; break; 245 | default: 246 | // ignore 247 | break; 248 | } 249 | } 250 | } 251 | 252 | template 253 | static std::string macro_definition( 254 | const Context* ctx_, 255 | const std::string& name_ 256 | ) 257 | { 258 | std::ostringstream os; 259 | 260 | if (name_ == "__FILE__") 261 | { 262 | os << "__FILE__ \""; 263 | string_escape(ctx_->get_main_pos().get_file().c_str(), os); 264 | os << "\""; 265 | } 266 | else if (name_ == "__LINE__") 267 | { 268 | os << "__LINE__ " << ctx_->get_main_pos().get_line(); 269 | } 270 | else 271 | { 272 | using boost::algorithm::join; 273 | using boost::adaptors::transformed; 274 | using boost::bind; 275 | 276 | bool is_function_style; 277 | bool is_predefined; 278 | typename Context::position_type position; 279 | std::vector parameters; 280 | typename Context::token_sequence_type definition; 281 | 282 | ctx_->get_macro_definition( 283 | name_, 284 | is_function_style, 285 | is_predefined, 286 | position, 287 | parameters, 288 | definition 289 | ); 290 | 291 | os << name_; 292 | if (is_function_style) 293 | { 294 | os 295 | << "(" 296 | << 297 | join( 298 | parameters 299 | | transformed(bind(&Context::token_type::get_value, _1)), 300 | ", " 301 | ) 302 | << ")"; 303 | } 304 | os 305 | << " " 306 | << 307 | join( 308 | definition 309 | | transformed(bind(&Context::token_type::get_value, _1)), 310 | "" 311 | ); 312 | } 313 | 314 | return os.str(); 315 | } 316 | 317 | template 318 | static std::set macro_names(const Context& ctx_) 319 | { 320 | using boost::bind; 321 | using boost::make_transform_iterator; 322 | 323 | std::set 324 | names( 325 | make_transform_iterator( 326 | ctx_.macro_names_begin(), 327 | bind(&Context::string_type::c_str, _1) 328 | ), 329 | make_transform_iterator( 330 | ctx_.macro_names_end(), 331 | bind(&Context::string_type::c_str, _1) 332 | ) 333 | ); 334 | names.insert("__LINE__"); 335 | names.insert("__FILE__"); 336 | return names; 337 | } 338 | 339 | void display_help(); 340 | void display_macro_names(const std::set& names_); 341 | }; 342 | } 343 | 344 | #endif 345 | 346 | -------------------------------------------------------------------------------- /include/just/process.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JUST_PROCESS_HPP 2 | #define JUST_PROCESS_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2014. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifdef _WIN32 10 | # include 11 | #else 12 | # include 13 | # include 14 | # include 15 | # include 16 | # include 17 | # include 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace just 30 | { 31 | namespace process 32 | { 33 | namespace impl 34 | { 35 | /* 36 | * noncopyable 37 | */ 38 | class noncopyable 39 | { 40 | public: 41 | noncopyable() {} 42 | 43 | private: 44 | noncopyable(const noncopyable&); 45 | noncopyable& operator=(const noncopyable&); 46 | }; 47 | 48 | /* 49 | * util 50 | */ 51 | inline const char* c_str(const std::string& s_) 52 | { 53 | return s_.c_str(); 54 | } 55 | 56 | #ifdef _WIN32 57 | inline HANDLE make_inheritable_copy(HANDLE h_) 58 | { 59 | const HANDLE cp = GetCurrentProcess(); 60 | HANDLE r = NULL; 61 | if (DuplicateHandle(cp, h_, cp, &r, 0, TRUE, DUPLICATE_SAME_ACCESS)) 62 | { 63 | return r; 64 | } 65 | else 66 | { 67 | return NULL; 68 | } 69 | } 70 | #endif 71 | 72 | /* 73 | * fd_t 74 | */ 75 | #ifdef _WIN32 76 | typedef HANDLE fd_t; 77 | #else 78 | typedef int fd_t; 79 | #endif 80 | 81 | inline fd_t invalid_fd() 82 | { 83 | #ifdef _WIN32 84 | return NULL; 85 | #else 86 | return -1; 87 | #endif 88 | } 89 | 90 | /* 91 | * input_file 92 | */ 93 | class input_file : noncopyable 94 | { 95 | public: 96 | explicit input_file(fd_t fd_) : _fd(fd_), _eof(false) {} 97 | 98 | ~input_file() 99 | { 100 | if (_fd != invalid_fd()) 101 | { 102 | close(); 103 | } 104 | } 105 | 106 | #ifdef _WIN32 107 | DWORD read(char* buf_, size_t count_) 108 | { 109 | DWORD len = 0; 110 | if (ReadFile(_fd, buf_, count_, &len, NULL)) 111 | { 112 | _eof = (len == 0); 113 | return len; 114 | } 115 | else 116 | { 117 | _eof = true; 118 | return 0; 119 | } 120 | } 121 | #endif 122 | 123 | #ifndef _WIN32 124 | ssize_t read(char* buf_, size_t count_) 125 | { 126 | const ssize_t len = ::read(_fd, buf_, count_); 127 | _eof = (len == 0); 128 | return len; 129 | } 130 | #endif 131 | bool eof() const { return _eof; } 132 | 133 | std::string read() 134 | { 135 | std::ostringstream s; 136 | static const int buffsize = 1024; 137 | char buff[buffsize]; 138 | while (!eof()) 139 | { 140 | s.write(buff, read(buff, buffsize)); 141 | } 142 | return s.str(); 143 | } 144 | 145 | void close() 146 | { 147 | assert(_fd != invalid_fd()); 148 | #ifdef _WIN32 149 | CloseHandle(_fd); 150 | #else 151 | ::close(_fd); 152 | #endif 153 | _fd = invalid_fd(); 154 | } 155 | 156 | #ifndef _WIN32 157 | void use_as(int fd_) 158 | { 159 | dup2(_fd, fd_); 160 | } 161 | #endif 162 | 163 | #ifdef _WIN32 164 | fd_t copy_handle() const { return make_inheritable_copy(_fd); } 165 | #endif 166 | private: 167 | fd_t _fd; 168 | bool _eof; 169 | }; 170 | 171 | /* 172 | * output_file 173 | */ 174 | class output_file : noncopyable 175 | { 176 | public: 177 | explicit output_file(fd_t fd_) : _fd(fd_) {} 178 | 179 | ~output_file() 180 | { 181 | if (_fd != invalid_fd()) 182 | { 183 | close(); 184 | } 185 | } 186 | 187 | #ifdef _WIN32 188 | DWORD write(const char* buff_, size_t count_) 189 | { 190 | DWORD len = 0; 191 | if (WriteFile(_fd, buff_, count_, &len, NULL)) 192 | { 193 | return len; 194 | } 195 | else 196 | { 197 | return 0; 198 | } 199 | } 200 | #endif 201 | 202 | #ifndef _WIN32 203 | ssize_t write(const char* buff_, size_t count_) 204 | { 205 | return ::write(_fd, buff_, count_); 206 | } 207 | #endif 208 | 209 | #ifdef _WIN32 210 | DWORD write(const std::string& s_) 211 | #else 212 | ssize_t write(const std::string& s_) 213 | #endif 214 | { 215 | return write(s_.c_str(), s_.length()); 216 | } 217 | 218 | void close() 219 | { 220 | assert(_fd != invalid_fd()); 221 | #ifdef _WIN32 222 | CloseHandle(_fd); 223 | #else 224 | ::close(_fd); 225 | #endif 226 | _fd = invalid_fd(); 227 | } 228 | 229 | #ifndef _WIN32 230 | void use_as(int fd_) 231 | { 232 | dup2(_fd, fd_); 233 | } 234 | #endif 235 | 236 | #ifndef _WIN32 237 | void close_on_exec() 238 | { 239 | fcntl(_fd, F_SETFD, fcntl(_fd, F_GETFD) | FD_CLOEXEC); 240 | } 241 | #endif 242 | 243 | #ifdef _WIN32 244 | fd_t copy_handle() const { return make_inheritable_copy(_fd); } 245 | #endif 246 | private: 247 | fd_t _fd; 248 | }; 249 | 250 | /* 251 | * pipe 252 | */ 253 | class pipe 254 | { 255 | public: 256 | class fds 257 | { 258 | public: 259 | #ifdef _WIN32 260 | fds() 261 | { 262 | CreatePipe(_fd, _fd + 1, NULL, 0); 263 | } 264 | #endif 265 | 266 | #ifndef _WIN32 267 | fds() 268 | { 269 | ::pipe(_fd); 270 | } 271 | #endif 272 | 273 | fd_t read_fd() const { return _fd[0]; } 274 | fd_t write_fd() const { return _fd[1]; } 275 | private: 276 | fd_t _fd[2]; 277 | }; 278 | 279 | input_file input; 280 | output_file output; 281 | 282 | pipe(fds fds_ = fds()) : 283 | input(fds_.read_fd()), 284 | output(fds_.write_fd()) 285 | {} 286 | }; 287 | 288 | /* 289 | * join 290 | */ 291 | template 292 | std::string join(InputIt begin_, InputIt end_) 293 | { 294 | std::ostringstream s; 295 | bool first = true; 296 | for (InputIt i = begin_; i != end_; ++i) 297 | { 298 | if (first) 299 | { 300 | first = false; 301 | } 302 | else 303 | { 304 | s << ' '; 305 | } 306 | s << *i; 307 | } 308 | return s.str(); 309 | } 310 | } 311 | 312 | /* 313 | * exception 314 | */ 315 | struct exception : std::runtime_error 316 | { 317 | explicit exception(const std::string& reason_) : 318 | std::runtime_error(reason_) 319 | {} 320 | }; 321 | 322 | /* 323 | * output 324 | */ 325 | class output 326 | { 327 | public: 328 | output(const std::string& stdout_, const std::string& stderr_) : 329 | _out(stdout_), 330 | _err(stderr_) 331 | {} 332 | 333 | const std::string& standard_output() const { return _out; } 334 | const std::string& standard_error() const { return _err; } 335 | private: 336 | std::string _out; 337 | std::string _err; 338 | }; 339 | 340 | /* 341 | * run 342 | */ 343 | #ifdef _WIN32 344 | template 345 | output run(const Seq& cmd_, const std::string& input_) 346 | { 347 | const std::string cmds = impl::join(cmd_.begin(), cmd_.end()); 348 | std::vector cmd(cmds.begin(), cmds.end()); 349 | cmd.push_back(0); 350 | 351 | impl::pipe standard_input; 352 | impl::pipe standard_output; 353 | impl::pipe standard_error; 354 | 355 | STARTUPINFO si; 356 | si.cb = sizeof(si); 357 | si.lpReserved = NULL; 358 | si.lpDesktop = NULL; 359 | si.lpTitle = NULL; 360 | si.dwX = 0; 361 | si.dwY = 0; 362 | si.dwXSize = 0; 363 | si.dwYSize = 0; 364 | si.dwXCountChars = 0; 365 | si.dwYCountChars = 0; 366 | si.dwFillAttribute = 0; 367 | si.dwFlags = STARTF_USESTDHANDLES; 368 | si.wShowWindow = 0; 369 | si.cbReserved2 = 0; 370 | si.lpReserved2 = NULL; 371 | si.hStdInput = standard_input.input.copy_handle(); 372 | si.hStdOutput = standard_output.output.copy_handle(); 373 | si.hStdError = standard_error.output.copy_handle(); 374 | 375 | PROCESS_INFORMATION pi; 376 | pi.hProcess = 0; 377 | pi.hThread = 0; 378 | pi.dwProcessId = 0; 379 | pi.dwThreadId = 0; 380 | 381 | if ( 382 | CreateProcess(NULL, &cmd[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) 383 | ) 384 | { 385 | CloseHandle(si.hStdInput); 386 | CloseHandle(si.hStdOutput); 387 | CloseHandle(si.hStdError); 388 | 389 | standard_input.input.close(); 390 | standard_input.output.write(input_); 391 | standard_input.output.close(); 392 | 393 | standard_output.output.close(); 394 | standard_error.output.close(); 395 | 396 | WaitForSingleObject(pi.hProcess, INFINITE); 397 | 398 | return 399 | output(standard_output.input.read(), standard_error.input.read()); 400 | } 401 | else 402 | { 403 | const DWORD e = GetLastError(); 404 | std::ostringstream s; 405 | s << "Error " << e; 406 | { 407 | char buff[1024]; 408 | if ( 409 | FormatMessage( 410 | FORMAT_MESSAGE_FROM_SYSTEM, 411 | NULL, 412 | e, 413 | 0, 414 | buff, 415 | sizeof(buff), 416 | 0 417 | ) 418 | ) 419 | { 420 | s << ": " << buff; 421 | } 422 | } 423 | throw exception(s.str()); 424 | } 425 | } 426 | #endif 427 | 428 | #ifndef _WIN32 429 | template 430 | output run(const Seq& cmd_, const std::string& input_) 431 | { 432 | using std::vector; 433 | using std::transform; 434 | using std::string; 435 | 436 | assert(!cmd_.empty()); 437 | 438 | vector cmd(cmd_.size() + 1, 0); 439 | transform(cmd_.begin(), cmd_.end(), cmd.begin(), impl::c_str); 440 | 441 | impl::pipe standard_input; 442 | impl::pipe standard_output; 443 | impl::pipe standard_error; 444 | impl::pipe error_reporting; 445 | 446 | const pid_t pid = fork(); 447 | switch (pid) 448 | { 449 | case -1: 450 | return output("", ""); 451 | case 0: // in child 452 | standard_input.output.close(); 453 | standard_output.input.close(); 454 | standard_error.input.close(); 455 | error_reporting.input.close(); 456 | error_reporting.output.close_on_exec(); 457 | 458 | standard_input.input.use_as(STDIN_FILENO); 459 | standard_output.output.use_as(STDOUT_FILENO); 460 | standard_error.output.use_as(STDERR_FILENO); 461 | 462 | execv(cmd[0], const_cast(&cmd[0])); 463 | { 464 | const int err = errno; 465 | std::ostringstream s; 466 | s << "Error running"; 467 | for ( 468 | typename Seq::const_iterator i = cmd_.begin(), e = cmd_.end(); 469 | i != e; 470 | ++i 471 | ) 472 | { 473 | s << ' ' << *i; 474 | } 475 | s << ": " << strerror(err); 476 | error_reporting.output.write(s.str()); 477 | } 478 | _exit(0); 479 | default: // in parent 480 | standard_input.input.close(); 481 | standard_input.output.write(input_); 482 | standard_input.output.close(); 483 | 484 | standard_output.output.close(); 485 | standard_error.output.close(); 486 | 487 | error_reporting.output.close(); 488 | 489 | int status; 490 | waitpid(pid, &status, 0); 491 | 492 | const std::string err = error_reporting.input.read(); 493 | if (err.empty()) 494 | { 495 | return 496 | output(standard_output.input.read(), standard_error.input.read()); 497 | } 498 | else 499 | { 500 | throw exception(err); 501 | } 502 | } 503 | } 504 | #endif 505 | } 506 | } 507 | 508 | #endif 509 | 510 | -------------------------------------------------------------------------------- /test/preshell.cpp: -------------------------------------------------------------------------------- 1 | // Preshell - Interactive C/C++ preprocessor shell 2 | // Copyright (C) 2013, Abel Sinkovics (abel@sinkovics.hu) 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | #include "test_util.hpp" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace preshell; 31 | 32 | namespace 33 | { 34 | std::list split(const std::string& s_) 35 | { 36 | std::list l; 37 | return boost::algorithm::split(l, s_, boost::is_any_of("\n")); 38 | } 39 | 40 | bool contains(const std::string& s_, const std::list& l_) 41 | { 42 | return std::find(l_.begin(), l_.end(), s_) != l_.end(); 43 | } 44 | 45 | indenter ind(always<80>, should_not_be_called); 46 | } 47 | 48 | JUST_TEST_CASE(test_empty_input) 49 | { 50 | std::list history; 51 | result_ptr r = precompile("", context(), config::empty, ind, history); 52 | JUST_ASSERT_EQUAL("", r->output); 53 | } 54 | 55 | JUST_TEST_CASE(test_one_character_input) 56 | { 57 | std::list history; 58 | result_ptr r = precompile("a", context(), config::empty, ind, history); 59 | JUST_ASSERT_EQUAL("a", r->output); 60 | } 61 | 62 | JUST_TEST_CASE(test_macro_call) 63 | { 64 | std::list history; 65 | const file_position position("", 1, 1); 66 | std::list def; 67 | def.push_back(token(boost::wave::T_IDENTIFIER, "foo", position)); 68 | 69 | context ctx; 70 | ctx.macros["M"] = macro_definition(position, def); 71 | result_ptr r = precompile("M", ctx, config::empty, ind, history); 72 | JUST_ASSERT_EQUAL("foo", r->output); 73 | } 74 | 75 | JUST_TEST_CASE(test_macro_definition) 76 | { 77 | std::list history; 78 | const file_position position("", 1, 9); 79 | std::list def; 80 | def.push_back(token(boost::wave::T_IDENTIFIER, "foo", position)); 81 | 82 | macro_map m; 83 | m["M"] = macro_definition(position, def); 84 | 85 | result_ptr r = 86 | precompile("#define M foo", context(), config::empty, ind, history); 87 | JUST_ASSERT_EQUAL("", r->output); 88 | JUST_ASSERT_EQUAL(m["M"], r->pp_context.macros["M"]); 89 | } 90 | 91 | JUST_TEST_CASE(test_macro_deletion) 92 | { 93 | std::list history; 94 | const file_position position("", 1, 9); 95 | std::list def; 96 | def.push_back(token(boost::wave::T_IDENTIFIER, "foo", position)); 97 | 98 | context ctx; 99 | ctx.macros["M"] = macro_definition(position, def); 100 | 101 | result_ptr r = precompile("#undef M", ctx, config::empty, ind, history); 102 | JUST_ASSERT_EQUAL("", r->output); 103 | JUST_ASSERT(r->pp_context.macros.find("M") == r->pp_context.macros.end()); 104 | } 105 | 106 | JUST_TEST_CASE(test_overriding_predefined_macro) 107 | { 108 | std::list history; 109 | const file_position position("", 1, 1); 110 | std::list def; 111 | def.push_back(token(boost::wave::T_IDENTIFIER, "foo", position)); 112 | 113 | context ctx; 114 | ctx.macros["__DATE__"] = macro_definition(position, def); 115 | result_ptr r = precompile("hello", ctx, config::empty, ind, history); 116 | JUST_ASSERT_EQUAL("hello", r->output); 117 | } 118 | 119 | JUST_TEST_CASE(test_missing_endif) 120 | { 121 | std::list history; 122 | const file_position position("", 2, 9); 123 | std::list def; 124 | def.push_back(token(boost::wave::T_IDENTIFIER, "foo", position)); 125 | 126 | macro_map m; 127 | m["M"] = macro_definition(position, def); 128 | 129 | result_ptr r = 130 | precompile("#if 1\n#define M foo", context(), config::empty, ind, history); 131 | JUST_ASSERT_EQUAL("", r->output); 132 | JUST_ASSERT_EQUAL(m["M"], r->pp_context.macros["M"]); 133 | } 134 | 135 | JUST_TEST_CASE(test_in_if_0) 136 | { 137 | std::list history; 138 | result_ptr r1 = precompile("#if 0", context(), config::empty, ind, history); 139 | JUST_ASSERT_EQUAL("", r1->output); 140 | result_ptr r2 = 141 | precompile("hello", r1->pp_context, config::empty, ind, history); 142 | JUST_ASSERT_EQUAL("", r2->output); 143 | result_ptr r3 = 144 | precompile("#else", r2->pp_context, config::empty, ind, history); 145 | JUST_ASSERT_EQUAL("", r3->output); 146 | result_ptr r4 = 147 | precompile("foo", r3->pp_context, config::empty, ind, history); 148 | JUST_ASSERT_EQUAL("foo", r4->output); 149 | result_ptr r5 = 150 | precompile("#endif", r4->pp_context, config::empty, ind, history); 151 | JUST_ASSERT_EQUAL("", r5->output); 152 | result_ptr r6 = 153 | precompile("world", r5->pp_context, config::empty, ind, history); 154 | JUST_ASSERT_EQUAL("world", r6->output); 155 | } 156 | 157 | JUST_TEST_CASE(test_in_if_1) 158 | { 159 | std::list history; 160 | result_ptr r1 = precompile("#if 1", context(), config::empty, ind, history); 161 | JUST_ASSERT_EQUAL("", r1->output); 162 | result_ptr r2 = 163 | precompile("hello", r1->pp_context, config::empty, ind, history); 164 | JUST_ASSERT_EQUAL("hello", r2->output); 165 | result_ptr r3 = 166 | precompile("#else", r2->pp_context, config::empty, ind, history); 167 | JUST_ASSERT_EQUAL("", r3->output); 168 | result_ptr r4 = 169 | precompile("foo", r3->pp_context, config::empty, ind, history); 170 | JUST_ASSERT_EQUAL("", r4->output); 171 | result_ptr r5 = 172 | precompile("#endif", r4->pp_context, config::empty, ind, history); 173 | JUST_ASSERT_EQUAL("", r5->output); 174 | result_ptr r6 = 175 | precompile("world", r5->pp_context, config::empty, ind, history); 176 | JUST_ASSERT_EQUAL("world", r6->output); 177 | } 178 | 179 | JUST_TEST_CASE(test_if1_elif1) 180 | { 181 | std::list history; 182 | result_ptr r1 = precompile("#if 1", context(), config::empty, ind, history); 183 | JUST_ASSERT_EQUAL("", r1->output); 184 | result_ptr r2 = 185 | precompile("hello", r1->pp_context, config::empty, ind, history); 186 | JUST_ASSERT_EQUAL("hello", r2->output); 187 | result_ptr r3 = 188 | precompile("#elif 1", r2->pp_context, config::empty, ind, history); 189 | JUST_ASSERT_EQUAL("", r3->output); 190 | result_ptr r4 = 191 | precompile("foo", r3->pp_context, config::empty, ind, history); 192 | JUST_ASSERT_EQUAL("", r4->output); 193 | result_ptr r5 = 194 | precompile("#endif", r4->pp_context, config::empty, ind, history); 195 | JUST_ASSERT_EQUAL("", r5->output); 196 | } 197 | 198 | JUST_TEST_CASE(test_if1_elif0) 199 | { 200 | std::list history; 201 | result_ptr r1 = precompile("#if 1", context(), config::empty, ind, history); 202 | JUST_ASSERT_EQUAL("", r1->output); 203 | result_ptr r2 = 204 | precompile("hello", r1->pp_context, config::empty, ind, history); 205 | JUST_ASSERT_EQUAL("hello", r2->output); 206 | result_ptr r3 = 207 | precompile("#elif 0", r2->pp_context, config::empty, ind, history); 208 | JUST_ASSERT_EQUAL("", r3->output); 209 | result_ptr r4 = 210 | precompile("foo", r3->pp_context, config::empty, ind, history); 211 | JUST_ASSERT_EQUAL("", r4->output); 212 | result_ptr r5 = 213 | precompile("#endif", r4->pp_context, config::empty, ind, history); 214 | JUST_ASSERT_EQUAL("", r5->output); 215 | } 216 | 217 | JUST_TEST_CASE(test_if0_elif1) 218 | { 219 | std::list history; 220 | result_ptr r1 = precompile("#if 0", context(), config::empty, ind, history); 221 | JUST_ASSERT_EQUAL("", r1->output); 222 | result_ptr r2 = 223 | precompile("hello", r1->pp_context, config::empty, ind, history); 224 | JUST_ASSERT_EQUAL("", r2->output); 225 | result_ptr r3 = 226 | precompile("#elif 1", r2->pp_context, config::empty, ind, history); 227 | JUST_ASSERT_EQUAL("", r3->output); 228 | result_ptr r4 = 229 | precompile("foo", r3->pp_context, config::empty, ind, history); 230 | JUST_ASSERT_EQUAL("foo", r4->output); 231 | result_ptr r5 = 232 | precompile("#endif", r4->pp_context, config::empty, ind, history); 233 | JUST_ASSERT_EQUAL("", r5->output); 234 | } 235 | 236 | JUST_TEST_CASE(test_if0_elif0) 237 | { 238 | std::list history; 239 | result_ptr r1 = precompile("#if 0", context(), config::empty, ind, history); 240 | JUST_ASSERT_EQUAL("", r1->output); 241 | result_ptr r2 = 242 | precompile("hello", r1->pp_context, config::empty, ind, history); 243 | JUST_ASSERT_EQUAL("", r2->output); 244 | result_ptr r3 = 245 | precompile("#elif 0", r2->pp_context, config::empty, ind, history); 246 | JUST_ASSERT_EQUAL("", r3->output); 247 | result_ptr r4 = 248 | precompile("foo", r3->pp_context, config::empty, ind, history); 249 | JUST_ASSERT_EQUAL("", r4->output); 250 | result_ptr r5 = 251 | precompile("#endif", r4->pp_context, config::empty, ind, history); 252 | JUST_ASSERT_EQUAL("", r5->output); 253 | } 254 | 255 | JUST_TEST_CASE(test_if0_elif0_else) 256 | { 257 | std::list history; 258 | result_ptr r1 = precompile("#if 0", context(), config::empty, ind, history); 259 | JUST_ASSERT_EQUAL("", r1->output); 260 | result_ptr r2 = 261 | precompile("hello", r1->pp_context, config::empty, ind, history); 262 | JUST_ASSERT_EQUAL("", r2->output); 263 | result_ptr r3 = 264 | precompile("#elif 0", r2->pp_context, config::empty, ind, history); 265 | JUST_ASSERT_EQUAL("", r3->output); 266 | result_ptr r4 = 267 | precompile("foo", r3->pp_context, config::empty, ind, history); 268 | JUST_ASSERT_EQUAL("", r4->output); 269 | result_ptr r5 = 270 | precompile("#else", r4->pp_context, config::empty, ind, history); 271 | JUST_ASSERT_EQUAL("", r5->output); 272 | result_ptr r6 = 273 | precompile("bar", r5->pp_context, config::empty, ind, history); 274 | JUST_ASSERT_EQUAL("bar", r6->output); 275 | result_ptr r7 = 276 | precompile("#endif", r6->pp_context, config::empty, ind, history); 277 | JUST_ASSERT_EQUAL("", r7->output); 278 | } 279 | 280 | JUST_TEST_CASE(test_error) 281 | { 282 | std::list history; 283 | result_ptr r = 284 | precompile("#error foo", context(), config::empty, ind, history); 285 | JUST_ASSERT_EQUAL("", r->output); 286 | JUST_ASSERT("" != r->error); 287 | } 288 | 289 | JUST_TEST_CASE(test_line) 290 | { 291 | std::list history; 292 | result_ptr r1 = 293 | precompile("__LINE__", context(), config::empty, ind, history); 294 | JUST_ASSERT_EQUAL("1", r1->output); 295 | JUST_ASSERT_EQUAL(2, r1->pp_context.line); 296 | JUST_ASSERT_EQUAL(1u, r1->pp_context.macros["__LINE__"].definition().size()); 297 | JUST_ASSERT_EQUAL( 298 | "2", 299 | r1->pp_context.macros["__LINE__"].definition().front().get_value() 300 | ); 301 | 302 | result_ptr r2 = 303 | precompile("__LINE__", r1->pp_context, config::empty, ind, history); 304 | JUST_ASSERT_EQUAL("2", r2->output); 305 | JUST_ASSERT_EQUAL(3, r2->pp_context.line); 306 | JUST_ASSERT_EQUAL(1u, r2->pp_context.macros["__LINE__"].definition().size()); 307 | JUST_ASSERT_EQUAL( 308 | "3", 309 | r2->pp_context.macros["__LINE__"].definition().front().get_value() 310 | ); 311 | 312 | result_ptr r3 = 313 | precompile( 314 | "#pragma wave macro_names", 315 | r2->pp_context, 316 | config::empty, 317 | ind, 318 | history 319 | ); 320 | JUST_ASSERT(contains("__LINE__", split(r3->info))); 321 | 322 | result_ptr r4 = 323 | precompile( 324 | "#pragma wave macros", 325 | r3->pp_context, 326 | config::empty, 327 | ind, 328 | history 329 | ); 330 | JUST_ASSERT(contains("__LINE__ 4", split(r4->info))); 331 | } 332 | 333 | JUST_TEST_CASE(test_file) 334 | { 335 | std::list history; 336 | result_ptr r1 = 337 | precompile("__FILE__", context(), config::empty, ind, history); 338 | JUST_ASSERT_EQUAL("\"\"", r1->output); 339 | JUST_ASSERT_EQUAL("", r1->pp_context.filename); 340 | JUST_ASSERT_EQUAL(1u, r1->pp_context.macros["__FILE__"].definition().size()); 341 | JUST_ASSERT_EQUAL( 342 | "\"\"", 343 | r1->pp_context.macros["__FILE__"].definition().front().get_value() 344 | ); 345 | 346 | result_ptr r2 = 347 | precompile("__FILE__", r1->pp_context, config::empty, ind, history); 348 | JUST_ASSERT_EQUAL("\"\"", r2->output); 349 | JUST_ASSERT_EQUAL("", r2->pp_context.filename); 350 | JUST_ASSERT_EQUAL(1u, r2->pp_context.macros["__FILE__"].definition().size()); 351 | JUST_ASSERT_EQUAL( 352 | "\"\"", 353 | r2->pp_context.macros["__FILE__"].definition().front().get_value() 354 | ); 355 | 356 | result_ptr r3 = 357 | precompile( 358 | "#pragma wave macro_names", 359 | r2->pp_context, 360 | config::empty, 361 | ind, 362 | history 363 | ); 364 | JUST_ASSERT(contains("__FILE__", split(r3->info))); 365 | 366 | result_ptr r4 = 367 | precompile( 368 | "#pragma wave macros", 369 | r3->pp_context, 370 | config::empty, 371 | ind, 372 | history 373 | ); 374 | JUST_ASSERT(contains("__FILE__ \"\"", split(r4->info))); 375 | } 376 | 377 | JUST_TEST_CASE(test_line_override) 378 | { 379 | std::list history; 380 | result_ptr r1 = 381 | precompile("#line 13 \"foo.cpp\"", context(), config::empty, ind, history); 382 | JUST_ASSERT_EQUAL("", r1->output); 383 | JUST_ASSERT_EQUAL(13, r1->pp_context.line); 384 | JUST_ASSERT_EQUAL(1u, r1->pp_context.macros["__LINE__"].definition().size()); 385 | JUST_ASSERT_EQUAL( 386 | "13", 387 | r1->pp_context.macros["__LINE__"].definition().front().get_value() 388 | ); 389 | JUST_ASSERT_EQUAL("foo.cpp", r1->pp_context.filename); 390 | JUST_ASSERT_EQUAL(1u, r1->pp_context.macros["__FILE__"].definition().size()); 391 | JUST_ASSERT_EQUAL( 392 | "\"foo.cpp\"", 393 | r1->pp_context.macros["__FILE__"].definition().front().get_value() 394 | ); 395 | 396 | result_ptr r2 = 397 | precompile("__LINE__", r1->pp_context, config::empty, ind, history); 398 | JUST_ASSERT_EQUAL("13", r2->output); 399 | JUST_ASSERT_EQUAL(14, r2->pp_context.line); 400 | JUST_ASSERT_EQUAL(1u, r2->pp_context.macros["__LINE__"].definition().size()); 401 | JUST_ASSERT_EQUAL( 402 | "14", 403 | r2->pp_context.macros["__LINE__"].definition().front().get_value() 404 | ); 405 | JUST_ASSERT_EQUAL("foo.cpp", r2->pp_context.filename); 406 | JUST_ASSERT_EQUAL(1u, r2->pp_context.macros["__FILE__"].definition().size()); 407 | JUST_ASSERT_EQUAL( 408 | "\"foo.cpp\"", 409 | r2->pp_context.macros["__FILE__"].definition().front().get_value() 410 | ); 411 | 412 | result_ptr r3 = 413 | precompile("__FILE__", r2->pp_context, config::empty, ind, history); 414 | JUST_ASSERT_EQUAL("\"foo.cpp\"", r3->output); 415 | 416 | result_ptr r4 = 417 | precompile( 418 | "#pragma wave macro_names", 419 | r3->pp_context, 420 | config::empty, 421 | ind, 422 | history 423 | ); 424 | JUST_ASSERT(contains("__FILE__", split(r4->info))); 425 | JUST_ASSERT(contains("__LINE__", split(r4->info))); 426 | 427 | result_ptr r5 = 428 | precompile( 429 | "#pragma wave macros", 430 | r4->pp_context, 431 | config::empty, 432 | ind, 433 | history 434 | ); 435 | JUST_ASSERT(contains("__FILE__ \"foo.cpp\"", split(r5->info))); 436 | JUST_ASSERT(contains("__LINE__ 16", split(r5->info))); 437 | } 438 | 439 | JUST_TEST_CASE(test_input_processed_after_warning) 440 | { 441 | std::list history; 442 | const config cfg = config::empty; 443 | 444 | result_ptr r1 = 445 | precompile( 446 | "#define a 1\n" 447 | "#define a 2\n" 448 | "#define b 3\n", 449 | context(), 450 | cfg, 451 | ind, 452 | history 453 | ); 454 | 455 | result_ptr r2 = precompile("b", r1->pp_context, cfg, ind, history); 456 | 457 | JUST_ASSERT_EQUAL("3", r2->output); 458 | } 459 | 460 | JUST_TEST_CASE(test_disable_warnings) 461 | { 462 | std::list history; 463 | config cfg = config::empty; 464 | cfg.enable_warnings = false; 465 | 466 | result_ptr r = 467 | precompile( 468 | "#define a 1\n" 469 | "#define a 2\n", 470 | context(), 471 | cfg, 472 | ind, 473 | history 474 | ); 475 | 476 | JUST_ASSERT_EQUAL("", r->error); 477 | } 478 | 479 | --------------------------------------------------------------------------------