Subversion Repositories HomeAutomation

Rev

Rev 1608 | Rev 1620 | 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.  
  59. class ConsoleClient : public net::Subscriber
  60. {
  61. public:
  62.     typedef boost::shared_ptr<ConsoleClient> Pointer;
  63.    
  64.     ConsoleClient(std::string address, unsigned int port)
  65.     {
  66.         this->client_id_ = net::Manager::Instance()->Connect(net::PROTOCOL_TCP, address, port);
  67.     }
  68.    
  69.     virtual ~ConsoleClient()
  70.     {
  71.         net::Manager::Instance()->Disconnect(this->client_id_);
  72.  
  73.         this->io_service_.stop();
  74.     }
  75.    
  76.     void Send(std::string data)
  77.     {
  78.         net::Manager::Instance()->SendTo(this->client_id_, data);
  79.     }
  80.    
  81.     bool IsConnected()
  82.     {
  83.         return this->client_id_ != 0;
  84.     }
  85.    
  86. private:
  87.     net::ClientId client_id_;
  88.    
  89.     void SlotOnNewStateHandler(net::ClientId client_id, net::ServerId server_id, net::ClientState client_state)
  90.     {
  91.         if (client_state == net::CLIENT_STATE_CONNECTED)
  92.         {
  93.             printf("Connected to Atom Daemon!\n");
  94.         }
  95.         else
  96.         {
  97.             printf("\nDisconnected!\n");
  98.             this->client_id_ = 0;
  99.            
  100.             kill(getpid(), SIGTERM);
  101.         }
  102.     }
  103.    
  104.     void SlotOnNewDataHandler(net::ClientId client_id, net::ServerId server_id, type::Byteset data)
  105.     {
  106.         std::string s(data.ToCharString());
  107.        
  108.         if (waiting_for_autocomplete)
  109.         {
  110.             autocomplete_list.clear();
  111.            
  112.             boost::algorithm::trim_if(s, boost::is_any_of("\n"));
  113.             boost::algorithm::trim_if(s, boost::is_any_of(" "));
  114.            
  115.             if (s != "")
  116.             {
  117.                 boost::algorithm::split(autocomplete_list, s, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
  118.             }
  119.         }
  120.         else
  121.         {
  122.             if (s[s.length()-1] != '\n')
  123.             {
  124.                 s += "\n";
  125.             }
  126.            
  127.             printf("%s", s.data());
  128.         }
  129.          
  130.         on_message_condition.notify_all();
  131.     }
  132. };
  133.  
  134. ConsoleClient::Pointer cc;
  135. struct termios original_flags;
  136. std::string history_filename;
  137.  
  138. int main(int argc, char **argv)
  139. {
  140.     signal(SIGTERM, Handler);
  141.     signal(SIGINT, Handler);
  142.     signal(SIGQUIT, Handler);
  143.     signal(SIGABRT, Handler);
  144.     signal(SIGPIPE, Handler);
  145.    
  146.     tcgetattr(fileno(stdin), &original_flags);
  147.        
  148.     printf("Atom Interactive Console, version 1.5.0 starting...\n");
  149.     printf("Written by Mattias Runge 2010.\n");
  150.     printf("Released under GPL version 2.\n");
  151.    
  152.     net::Manager::Create();
  153.    
  154.     std::string address = "localhost";
  155.     unsigned int port = 1202;
  156.    
  157.     printf("Connecting to %s:%d...", address.data(), port);
  158.    
  159.     try
  160.     {
  161.         cc = ConsoleClient::Pointer(new ConsoleClient(address, port));
  162.     }
  163.     catch (std::runtime_error& e)
  164.     {
  165.         printf("%s\n", e.what());
  166.         CleanUp();
  167.        
  168.         return EXIT_FAILURE;
  169.     }
  170.    
  171.     printf("success!\n");
  172.    
  173.     std::string prompt = address + ":" + boost::lexical_cast<std::string>(port) + "] ";
  174.    
  175.    
  176.     passwd* user_struct = getpwuid(getuid());
  177.    
  178.     history_filename = std::string(user_struct->pw_dir) + "/.atomic_history";
  179.    
  180.     read_history(history_filename.data());
  181.    
  182.     rl_attempted_completion_function = AutoComplete;
  183.    
  184.     while ((buffer = readline(prompt.data())) != NULL)
  185.     {
  186.         if (strlen(buffer) == 0)
  187.         {
  188.             continue;
  189.         }
  190.        
  191.         if (strcmp(buffer, "quit") == 0)
  192.         {
  193.             break;
  194.         }
  195.        
  196.         waiting_for_autocomplete = false;
  197.        
  198.         cc->Send("E;" + std::string(buffer));
  199.        
  200.         boost::mutex::scoped_lock guard(guard_mutex);
  201.         on_message_condition.wait(guard);    
  202.        
  203.         add_history(buffer);
  204.     }
  205.    
  206.     CleanUp();
  207.    
  208.     return EXIT_SUCCESS;
  209. }
  210.  
  211. static char** AutoComplete(const char* text, int start, int end)
  212. {
  213.     unsigned int count = 0;
  214.    
  215.     //printf("\nAutoComplete:start=%d, end=%d\n", start, end);
  216.    
  217.     for (unsigned int n = 0; n < start; n++)
  218.     {
  219.         if (rl_line_buffer[n] == ' ')
  220.         {
  221.             count++;
  222.         }
  223.     }
  224.    
  225.     waiting_for_autocomplete = true;
  226.    
  227.  
  228.     std::string request = "A;"+ boost::lexical_cast<std::string>(count) + ";" + std::string(rl_line_buffer);
  229.     //printf("\nSending request:%s\n", request.data());
  230.    
  231.     cc->Send(request);
  232.    
  233.     boost::mutex::scoped_lock guard(guard_mutex);
  234.     on_message_condition.wait(guard);    
  235.    
  236.     if (autocomplete_list.size() == 0)
  237.     {
  238.         return NULL;
  239.     }
  240.    
  241.     //printf("processing response\n");
  242.    
  243.     return rl_completion_matches(text, &AutoCompleteGet);
  244. }
  245.  
  246. char* AutoCompleteGet(const char* text, int state)
  247. {
  248.     static int index = 0;
  249.     int length = strlen(text);
  250.     std::string name;
  251.    
  252.     //printf("run, %s, %d, %d\n", text, state, autocomplete_list.size());
  253.    
  254.     if (!state) // First run
  255.     {
  256.         index = 0;
  257.     }
  258.    
  259.     while (autocomplete_list.size() > index)
  260.     {
  261.         name = autocomplete_list[index];
  262.         //printf("testing name = %s\n", name.data());
  263.        
  264.         index++;
  265.        
  266.         if (strncmp(name.data(), text, length) == 0)
  267.         {
  268.             char *result = (char*)malloc(name.size() + 1);
  269.             strcpy(result, name.data());
  270.            
  271.             return result;
  272.         }
  273.     }
  274.    
  275.     return NULL;
  276. }
  277.  
  278. void CleanUp()
  279. {
  280.     printf("Cleaning up...\n");
  281.  
  282.     write_history(history_filename.data());
  283.    
  284.     cc.reset();
  285.    
  286.     net::Manager::Delete();
  287.    
  288.     if (buffer != NULL)
  289.     {
  290.         free(buffer);
  291.     }
  292.    
  293.     printf("Thank you for using Atom. Goodbye!\n");
  294.    
  295.     tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
  296. }
  297.  
  298. void Handler(int status)
  299. {
  300.     std::string signal_name = "Unknown";
  301.    
  302.     switch (status)
  303.     {
  304.         case SIGTERM:
  305.         {
  306.             signal_name = "Terminate";
  307.             break;
  308.         }  
  309.         case SIGINT:
  310.         {
  311.             signal_name = "Interupt";
  312.             break;
  313.         }  
  314.         case SIGQUIT:
  315.         {
  316.             signal_name = "Quit";
  317.             break;
  318.         }  
  319.         case SIGABRT:
  320.         {
  321.             signal_name = "Abort";
  322.             break;
  323.         }  
  324.         case SIGIO:
  325.         {
  326.             signal_name = "I/O";
  327.             break;
  328.         }  
  329.         case SIGPIPE:
  330.         {
  331.             signal_name = "Pipe";
  332.             break;
  333.         }
  334.     }
  335.    
  336.    
  337.     if (status != SIGPIPE)
  338.     {
  339.         CleanUp();
  340.         exit(0);
  341.         //on_message_condition.notify_all();
  342.     }
  343. }