| /* |
| * Copyright (c) 2022 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include "InteractiveCommands.h" |
| |
| #include <platform/logging/LogV.h> |
| |
| #include <editline.h> |
| |
| constexpr const char * kInteractiveModePrompt = ">>> "; |
| constexpr const char * kInteractiveModeHistoryFilePath = "/tmp/darwin_framework_tool_history"; |
| constexpr const char * kInteractiveModeStopCommand = "quit()"; |
| |
| namespace { |
| |
| class RestartCommand : public CHIPCommandBridge { |
| public: |
| RestartCommand() |
| : CHIPCommandBridge("restart") |
| { |
| } |
| |
| CHIP_ERROR RunCommand() override |
| { |
| RestartCommissioners(); |
| return CHIP_NO_ERROR; |
| } |
| |
| chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(0); } |
| }; |
| |
| class StopCommand : public CHIPCommandBridge { |
| public: |
| StopCommand() |
| : CHIPCommandBridge("stop") |
| { |
| } |
| |
| CHIP_ERROR RunCommand() override |
| { |
| StopCommissioners(); |
| return CHIP_NO_ERROR; |
| } |
| |
| chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(0); } |
| }; |
| |
| void ClearLine() |
| { |
| printf("\r\x1B[0J"); // Move cursor to the beginning of the line and clear from cursor to end of the screen |
| } |
| |
| void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, const char * msg, va_list args) |
| { |
| ClearLine(); |
| chip::Logging::Platform::LogV(module, category, msg, args); |
| ClearLine(); |
| } |
| } // namespace |
| |
| char * GetCommand(const chip::Optional<char *> & mAdditionalPrompt, char * command) |
| { |
| if (command != nullptr) { |
| free(command); |
| command = nullptr; |
| } |
| |
| if (mAdditionalPrompt.HasValue()) { |
| ClearLine(); |
| printf("%s\n", mAdditionalPrompt.Value()); |
| ClearLine(); |
| } |
| command = readline(kInteractiveModePrompt); |
| |
| // Do not save empty lines |
| if (command != nullptr && *command) { |
| add_history(command); |
| write_history(kInteractiveModeHistoryFilePath); |
| } |
| |
| return command; |
| } |
| |
| el_status_t RestartFunction() |
| { |
| RestartCommand cmd; |
| cmd.RunCommand(); |
| return CSstay; |
| } |
| |
| el_status_t StopFunction() |
| { |
| StopCommand cmd; |
| cmd.RunCommand(); |
| return CSstay; |
| } |
| |
| CHIP_ERROR InteractiveStartCommand::RunCommand() |
| { |
| read_history(kInteractiveModeHistoryFilePath); |
| |
| // Logs needs to be redirected in order to refresh the screen appropriately when something |
| // is dumped to stdout while the user is typing a command. |
| chip::Logging::SetLogRedirectCallback(LoggingCallback); |
| |
| el_bind_key(CTL('^'), RestartFunction); |
| el_bind_key(CTL('_'), StopFunction); |
| |
| char * command = nullptr; |
| while (YES) { |
| command = GetCommand(mAdditionalPrompt, command); |
| if (command != nullptr && !ParseCommand(command)) { |
| break; |
| } |
| } |
| |
| if (command != nullptr) { |
| free(command); |
| command = nullptr; |
| } |
| |
| SetCommandExitStatus(CHIP_NO_ERROR); |
| return CHIP_NO_ERROR; |
| } |
| |
| bool InteractiveStartCommand::ParseCommand(char * command) |
| { |
| if (strcmp(command, kInteractiveModeStopCommand) == 0) { |
| ExecuteDeferredCleanups(); |
| return NO; |
| } |
| |
| ClearLine(); |
| mHandler->RunInteractive(command); |
| return YES; |
| } |