Subversion Repositories HomeAutomation

Rev

Rev 1609 | Rev 1642 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*
  2.  *
  3.  *  Copyright (C) 2010  Mattias Runge
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License along
  16.  *  with this program; if not, write to the Free Software Foundation, Inc.,
  17.  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  *
  19.  */
  20.  
  21. #include <string>
  22. #include <vector>
  23.  
  24. #include <signal.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <pwd.h>
  28.  
  29. #include <boost/lexical_cast.hpp>
  30. #include <boost/thread/mutex.hpp>
  31. #include <boost/thread/condition.hpp>
  32. #include <boost/thread/locks.hpp>
  33. #include <boost/algorithm/string.hpp>
  34.  
  35. #include "net/Manager.h"
  36. #include "net/Subscriber.h"
  37. #include "net/types.h"
  38.  
  39. #include "type/common.h"
  40.  
  41. #include <readline/readline.h>
  42. #include <readline/history.h>
  43.  
  44. using namespace atom;
  45.  
  46.  
  47. void Handler(int status);
  48. void CleanUp();
  49.  
  50. char* AutoCompleteGet(const char* text, int state);
  51. static char** AutoComplete(const char* text, int start, int end);
  52.  
  53. type::StringList autocomplete_list;
  54. char* buffer = NULL;
  55. bool waiting_for_autocomplete = false;
  56. boost::condition on_message_condition;
  57. boost::mutex guard_mutex;
  58. std::string prompt;
  59. int prompt_id = -1;
  60.  
  61. class ConsoleClient : public net::Subscriber
  62. {
  63. public:
  64.     typedef boost::shared_ptr<ConsoleClient> Pointer;
  65.    
  66.     ConsoleClient(std::string address, unsigned int port)
  67.     {
  68.         this->identifier_ = address + ":" + boost::lexical_cast<std::string>(port);
  69.         this->client_id_ = net::Manager::Instance()->Connect(net::PROTOCOL_TCP, address, port);
  70.     }
  71.    
  72.     virtual ~ConsoleClient()
  73.     {
  74.         net::Manager::Instance()->Disconnect(this->client_id_);
  75.  
  76.         this->io_service_.stop();
  77.     }
  78.    
  79.     void Send(std::string data)
  80.     {
  81.         net::Manager::Instance()->SendTo(this->client_id_, data);
  82.     }
  83.    
  84.     bool IsConnected()
  85.     {
  86.         return this->client_id_ != 0;
  87.     }
  88.    
  89.     std::string GetIdentifier()
  90.     {
  91.         return this->identifier_;
  92.     }
  93.    
  94. private:
  95.     net::ClientId client_id_;
  96.     std::string identifier_;
  97.    
  98.    
  99.     void SlotOnNewStateHandler(net::ClientId client_id, net::ServerId server_id, net::ClientState client_state)
  100.     {
  101.         if (client_state == net::CLIENT_STATE_CONNECTED)
  102.         {
  103.             printf("Connected to Atom Daemon!\n");
  104.         }
  105.         else
  106.         {
  107.             printf("\nDisconnected!\n");
  108.             this->client_id_ = 0;
  109.            
  110.             kill(getpid(), SIGTERM);
  111.         }
  112.     }
  113.    
  114.     void SlotOnNewDataHandler(net::ClientId client_id, net::ServerId server_id, type::Byteset data)
  115.     {
  116.         prompt_id = -1;
  117.        
  118.         std::string s(data.ToCharString());
  119.        
  120.         if (waiting_for_autocomplete)
  121.         {
  122.             autocomplete_list.clear();
  123.            
  124.             boost::algorithm::trim_if(s, boost::is_any_of("\n"));
  125.             boost::algorithm::trim_if(s, boost::is_any_of(" "));
  126.            
  127.             if (s != "")
  128.             {
  129.                 boost::algorithm::split(autocomplete_list, s, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
  130.             }
  131.         }
  132.         else
  133.         {
  134.             type::StringList lines;
  135.            
  136.             boost::algorithm::split(lines, s, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
  137.            
  138.             for (unsigned int n = 0; n < lines.size(); n++)
  139.             {
  140.                 type::StringList parts;
  141.                
  142.                 boost::algorithm::split(parts, lines[n], boost::is_any_of(";"), boost::algorithm::token_compress_off);
  143.                
  144.                 if (parts[0].length() == 1 && parts[0] == "P")
  145.                 {
  146.                     prompt_id = boost::lexical_cast<unsigned int>(parts[1]);
  147.                    
  148.                     prompt = "";
  149.                    
  150.                     for (unsigned int c = 2; c < parts.size(); c++)
  151.                     {
  152.                         prompt += parts[c] + " ";
  153.                     }
  154.                 }
  155.                 else
  156.                 {
  157.                     boost::algorithm::trim_if(lines[n], boost::is_any_of(" "));
  158.                    
  159.                     if (lines[n] != "")
  160.                     {
  161.                         printf("%s\n", lines[n].data());
  162.                     }
  163.                 }
  164.             }
  165.         }
  166.          
  167.         on_message_condition.notify_all();
  168.     }
  169. };
  170.  
  171. ConsoleClient::Pointer cc;
  172. struct termios original_flags;
  173. std::string history_filename;
  174.  
  175. int main(int argc, char **argv)
  176. {
  177.     signal(SIGTERM, Handler);
  178.     signal(SIGINT, Handler);
  179.     signal(SIGQUIT, Handler);
  180.     signal(SIGABRT, Handler);
  181.     signal(SIGPIPE, Handler);
  182.    
  183.     tcgetattr(fileno(stdin), &original_flags);
  184.        
  185.     printf("Atom Interactive Console, version 1.5.0 starting...\n");
  186.     printf("Written by Mattias Runge 2010.\n");
  187.     printf("Released under GPL version 2.\n");
  188.    
  189.     net::Manager::Create();
  190.    
  191.     std::string address = "localhost";
  192.     unsigned int port = 1202;
  193.    
  194.     printf("Connecting to %s:%d...", address.data(), port);
  195.    
  196.     try
  197.     {
  198.         cc = ConsoleClient::Pointer(new ConsoleClient(address, port));
  199.     }
  200.     catch (std::runtime_error& e)
  201.     {
  202.         printf("%s\n", e.what());
  203.         CleanUp();
  204.        
  205.         return EXIT_FAILURE;
  206.     }
  207.    
  208.     printf("success!\n");
  209.    
  210.     prompt = cc->GetIdentifier() + "] ";
  211.    
  212.     passwd* user_struct = getpwuid(getuid());
  213.    
  214.     history_filename = std::string(user_struct->pw_dir) + "/.atomic_history";
  215.    
  216.     read_history(history_filename.data());
  217.    
  218.     rl_attempted_completion_function = AutoComplete;
  219.    
  220.     while ((buffer = readline(prompt.data())) != NULL)
  221.     {
  222.         if (strlen(buffer) == 0)
  223.         {
  224.             continue;
  225.         }
  226.        
  227.         if (strcmp(buffer, "quit") == 0)
  228.         {
  229.             break;
  230.         }
  231.        
  232.         waiting_for_autocomplete = false;
  233.         prompt = cc->GetIdentifier() + "] ";
  234.        
  235.         if (prompt_id == -1)
  236.         {
  237.             cc->Send("E;" + std::string(buffer));
  238.             add_history(buffer);
  239.         }
  240.         else
  241.         {
  242.             cc->Send("R;" + boost::lexical_cast<std::string>(prompt_id) + ";" + std::string(buffer));
  243.             prompt_id = -1;
  244.         }
  245.        
  246.         boost::mutex::scoped_lock guard(guard_mutex);
  247.         on_message_condition.wait(guard);    
  248.     }
  249.    
  250.     CleanUp();
  251.    
  252.     return EXIT_SUCCESS;
  253. }
  254.  
  255. static char** AutoComplete(const char* text, int start, int end)
  256. {
  257.     unsigned int count = 0;
  258.    
  259.     //printf("\nAutoComplete:start=%d, end=%d\n", start, end);
  260.    
  261.     for (unsigned int n = 0; n < start; n++)
  262.     {
  263.         if (rl_line_buffer[n] == ' ')
  264.         {
  265.             count++;
  266.         }
  267.     }
  268.    
  269.     waiting_for_autocomplete = true;
  270.    
  271.  
  272.     std::string request = "A;"+ boost::lexical_cast<std::string>(count) + ";" + std::string(rl_line_buffer);
  273.     //printf("\nSending request:%s\n", request.data());
  274.    
  275.     cc->Send(request);
  276.    
  277.     boost::mutex::scoped_lock guard(guard_mutex);
  278.     on_message_condition.wait(guard);    
  279.    
  280.     if (autocomplete_list.size() == 0)
  281.     {
  282.         return NULL;
  283.     }
  284.    
  285.     //printf("processing response\n");
  286.    
  287.     return rl_completion_matches(text, &AutoCompleteGet);
  288. }
  289.  
  290. char* AutoCompleteGet(const char* text, int state)
  291. {
  292.     static int index = 0;
  293.     int length = strlen(text);
  294.     std::string name;
  295.    
  296.     //printf("run, %s, %d, %d\n", text, state, autocomplete_list.size());
  297.    
  298.     if (!state) // First run
  299.     {
  300.         index = 0;
  301.     }
  302.    
  303.     while (autocomplete_list.size() > index)
  304.     {
  305.         name = autocomplete_list[index];
  306.         //printf("testing name = %s\n", name.data());
  307.        
  308.         index++;
  309.        
  310.         if (strncmp(name.data(), text, length) == 0)
  311.         {
  312.             char *result = (char*)malloc(name.size() + 1);
  313.             strcpy(result, name.data());
  314.            
  315.             return result;
  316.         }
  317.     }
  318.    
  319.     return NULL;
  320. }
  321.  
  322. void CleanUp()
  323. {
  324.     printf("Cleaning up...\n");
  325.  
  326.     write_history(history_filename.data());
  327.    
  328.     cc.reset();
  329.    
  330.     net::Manager::Delete();
  331.    
  332.     if (buffer != NULL)
  333.     {
  334.         free(buffer);
  335.     }
  336.    
  337.     printf("Thank you for using Atom. Goodbye!\n");
  338.    
  339.     tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
  340. }
  341.  
  342. void Handler(int status)
  343. {
  344.     std::string signal_name = "Unknown";
  345.    
  346.     switch (status)
  347.     {
  348.         case SIGTERM:
  349.         {
  350.             signal_name = "Terminate";
  351.             break;
  352.         }  
  353.         case SIGINT:
  354.         {
  355.             signal_name = "Interupt";
  356.             break;
  357.         }  
  358.         case SIGQUIT:
  359.         {
  360.             signal_name = "Quit";
  361.             break;
  362.         }  
  363.         case SIGABRT:
  364.         {
  365.             signal_name = "Abort";
  366.             break;
  367.         }  
  368.         case SIGIO:
  369.         {
  370.             signal_name = "I/O";
  371.             break;
  372.         }  
  373.         case SIGPIPE:
  374.         {
  375.             signal_name = "Pipe";
  376.             break;
  377.         }
  378.     }
  379.    
  380.    
  381.     if (status != SIGPIPE)
  382.     {
  383.         CleanUp();
  384.         exit(0);
  385.         //on_message_condition.notify_all();
  386.     }
  387. }