Changeset 2697


Ignore:
Timestamp:
06/06/2010 16:30:20 (2 years ago)
Author:
chris
Message:

Initial support for command and local file completion with readline.

Improve bbackupquery command-line help.

Add -E option to disable readline/editline as it causes problems with
entering international characters on some systems (see #73).

Location:
box/trunk/bin/bbackupquery
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • box/trunk/bin/bbackupquery/BackupQueries.cpp

    r2641 r2697  
    6767QueryCommandSpecification commands[] =  
    6868{ 
    69         { "quit", "" }, 
    70         { "exit", "" }, 
    71         { "list", "rodIFtTash", }, 
    72         { "pwd",  "" }, 
    73         { "cd",   "od" }, 
    74         { "lcd",  "" }, 
    75         { "sh",   "" }, 
    76         { "getobject", "" }, 
    77         { "get",  "i" }, 
    78         { "compare", "alcqAEQ" }, 
    79         { "restore", "drif" }, 
    80         { "help", "" }, 
    81         { "usage", "m" }, 
    82         { "undelete", "" }, 
    83         { "delete", "" }, 
    84         { NULL, NULL }  
     69        { "quit", "", Command_Quit }, 
     70        { "exit", "", Command_Quit }, 
     71        { "list", "rodIFtTash", Command_List }, 
     72        { "pwd",  "", Command_pwd }, 
     73        { "cd",   "od", Command_cd }, 
     74        { "lcd",  "", Command_lcd }, 
     75        { "sh",   "", Command_sh }, 
     76        { "getobject", "", Command_GetObject }, 
     77        { "get",  "i", Command_Get }, 
     78        { "compare", "alcqAEQ", Command_Compare }, 
     79        { "restore", "drif", Command_Restore }, 
     80        { "help", "", Command_Help }, 
     81        { "usage", "m", Command_Usage }, 
     82        { "undelete", "", Command_Undelete }, 
     83        { "delete", "", Command_Delete }, 
     84        { NULL, NULL, Command_Unknown }  
    8585}; 
    8686 
     
    125125} 
    126126 
     127BackupQueries::ParsedCommand 
     128BackupQueries::ParseCommand(const std::string& Command, bool isFromCommandLine) 
     129{ 
     130        ParsedCommand parsed; 
     131        parsed.completeCommand = Command; 
     132         
     133        // is the command a shell command? 
     134        if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') 
     135        { 
     136                // Yes, run shell command 
     137                parsed.cmdElements[0] = "sh"; 
     138                parsed.cmdElements[1] = Command.c_str() + 3; 
     139                return parsed; 
     140        } 
     141 
     142        // split command into components 
     143        const char *c = Command.c_str(); 
     144        bool inQuoted = false; 
     145        bool inOptions = false; 
     146         
     147        std::string s; 
     148        while(*c != 0) 
     149        { 
     150                // Terminating char? 
     151                if(*c == ((inQuoted)?'"':' ')) 
     152                { 
     153                        if(!s.empty()) parsed.cmdElements.push_back(s); 
     154                        s.resize(0); 
     155                        inQuoted = false; 
     156                        inOptions = false; 
     157                } 
     158                else 
     159                { 
     160                        // No. Start of quoted parameter? 
     161                        if(s.empty() && *c == '"') 
     162                        { 
     163                                inQuoted = true; 
     164                        } 
     165                        // Start of options? 
     166                        else if(s.empty() && *c == '-') 
     167                        { 
     168                                inOptions = true; 
     169                        } 
     170                        else 
     171                        { 
     172                                if(inOptions) 
     173                                { 
     174                                        // Option char 
     175                                        parsed.options += *c; 
     176                                } 
     177                                else 
     178                                { 
     179                                        // Normal string char 
     180                                        s += *c; 
     181                                } 
     182                        } 
     183                } 
     184         
     185                ++c; 
     186        } 
     187         
     188        if(!s.empty()) 
     189        { 
     190                parsed.cmdElements.push_back(s); 
     191        } 
     192         
     193        #ifdef WIN32 
     194        if(isFromCommandLine) 
     195        { 
     196                std::string converted; 
     197                 
     198                if(!ConvertEncoding(parsed.completeCommand, CP_ACP, converted,  
     199                        GetConsoleCP())) 
     200                { 
     201                        BOX_ERROR("Failed to convert encoding"); 
     202                        return; 
     203                } 
     204                 
     205                parsed.completeCommand = converted; 
     206                 
     207                for(std::vector<std::string>::iterator  
     208                        i  = parsed.cmdElements.begin(); 
     209                        i != parsed.cmdElements.end(); i++) 
     210                { 
     211                        if(!ConvertEncoding(*i, CP_ACP, converted,  
     212                                GetConsoleCP())) 
     213                        { 
     214                                BOX_ERROR("Failed to convert encoding"); 
     215                                return; 
     216                        } 
     217                         
     218                        *i = converted; 
     219                } 
     220        } 
     221        #endif 
     222         
     223        return parsed; 
     224} 
     225 
    127226// -------------------------------------------------------------------------- 
    128227// 
     
    133232// 
    134233// -------------------------------------------------------------------------- 
    135 void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) 
    136 { 
    137         // is the command a shell command? 
    138         if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') 
     234void BackupQueries::DoCommand(ParsedCommand& rCommand) 
     235{        
     236        // Check... 
     237        if(rCommand.cmdElements.size() < 1) 
     238        { 
     239                // blank command 
     240                return; 
     241        } 
     242 
     243        if(rCommand.cmdElements[0] == "sh" && rCommand.cmdElements.size() == 2) 
    139244        { 
    140245                // Yes, run shell command 
    141                 int result = ::system(Command + 3); 
     246                int result = ::system(rCommand.cmdElements[1].c_str()); 
    142247                if(result != 0) 
    143248                { 
     
    148253                return; 
    149254        } 
    150  
    151         // split command into components 
    152         std::vector<std::string> cmdElements; 
    153         std::string options; 
    154         { 
    155                 const char *c = Command; 
    156                 bool inQuoted = false; 
    157                 bool inOptions = false; 
    158                  
    159                 std::string s; 
    160                 while(*c != 0) 
    161                 { 
    162                         // Terminating char? 
    163                         if(*c == ((inQuoted)?'"':' ')) 
    164                         { 
    165                                 if(!s.empty()) cmdElements.push_back(s); 
    166                                 s.resize(0); 
    167                                 inQuoted = false; 
    168                                 inOptions = false; 
    169                         } 
    170                         else 
    171                         { 
    172                                 // No. Start of quoted parameter? 
    173                                 if(s.empty() && *c == '"') 
    174                                 { 
    175                                         inQuoted = true; 
    176                                 } 
    177                                 // Start of options? 
    178                                 else if(s.empty() && *c == '-') 
    179                                 { 
    180                                         inOptions = true; 
    181                                 } 
    182                                 else 
    183                                 { 
    184                                         if(inOptions) 
    185                                         { 
    186                                                 // Option char 
    187                                                 options += *c; 
    188                                         } 
    189                                         else 
    190                                         { 
    191                                                 // Normal string char 
    192                                                 s += *c; 
    193                                         } 
    194                                 } 
    195                         } 
    196                  
    197                         ++c; 
    198                 } 
    199                 if(!s.empty()) cmdElements.push_back(s); 
    200         } 
    201          
    202         #ifdef WIN32 
    203         if (isFromCommandLine) 
    204         { 
    205                 for (std::vector<std::string>::iterator  
    206                         i  = cmdElements.begin(); 
    207                         i != cmdElements.end(); i++) 
    208                 { 
    209                         std::string converted; 
    210                         if (!ConvertEncoding(*i, CP_ACP, converted,  
    211                                 GetConsoleCP())) 
    212                         { 
    213                                 BOX_ERROR("Failed to convert encoding"); 
    214                                 return; 
    215                         } 
    216                         *i = converted; 
    217                 } 
    218         } 
    219         #endif 
    220                  
    221         // Check... 
    222         if(cmdElements.size() < 1) 
    223         { 
    224                 // blank command 
    225                 return; 
    226         } 
    227  
     255                 
    228256        // Work out which command it is... 
    229257        int cmd = 0; 
    230         while(commands[cmd].name != 0 && ::strcmp(cmdElements[0].c_str(), commands[cmd].name) != 0) 
     258        while(commands[cmd].name != 0 &&  
     259                rCommand.cmdElements[0] != commands[cmd].name) 
    231260        { 
    232261                cmd++; 
    233262        } 
     263         
    234264        if(commands[cmd].name == 0) 
    235265        { 
     
    238268                for(a = 0; alias[a] != 0; ++a) 
    239269                { 
    240                         if(::strcmp(cmdElements[0].c_str(), alias[a]) == 0) 
     270                        if(rCommand.cmdElements[0] == alias[a]) 
    241271                        { 
    242272                                // Found an alias 
     
    245275                        } 
    246276                } 
    247          
     277        } 
     278                 
     279        if(commands[cmd].name == 0 || commands[cmd].type == Command_Unknown) 
     280        { 
    248281                // No such command 
    249                 if(alias[a] == 0) 
    250                 { 
    251                         BOX_ERROR("Unrecognised command: " << Command); 
    252                         return; 
    253                 } 
     282                BOX_ERROR("Unrecognised command: " << rCommand.cmdElements[0]); 
     283                return; 
    254284        } 
    255285 
    256286        // Arguments 
    257         std::vector<std::string> args(cmdElements.begin() + 1, cmdElements.end()); 
     287        std::vector<std::string> args(rCommand.cmdElements.begin() + 1, 
     288                rCommand.cmdElements.end()); 
    258289 
    259290        // Set up options 
     
    263294        { 
    264295                // options 
    265                 const char *c = options.c_str(); 
     296                const char *c = rCommand.options.c_str(); 
    266297                while(*c != 0) 
    267298                { 
     
    278309        } 
    279310 
    280         if(cmd != Command_Quit && cmd != Command_Exit) 
     311        if(commands[cmd].type != Command_Quit) 
    281312        { 
    282313                // If not a quit command, set the return code to zero 
     
    285316 
    286317        // Handle command 
    287         switch(cmd) 
     318        switch(commands[cmd].type) 
    288319        { 
    289320        case Command_Quit: 
    290         case Command_Exit: 
    291321                mQuitNow = true; 
    292322                break; 
     
    349379                 
    350380        default: 
    351                 BOX_ERROR("Unknown command: " << Command); 
     381                BOX_ERROR("Unknown command: " << rCommand.cmdElements[0]); 
    352382                break; 
    353383        } 
  • box/trunk/bin/bbackupquery/BackupQueries.h

    r2641 r2697  
    2121class ExcludeList; 
    2222 
    23 typedef struct 
    24 { 
    25         const char* name; 
    26         const char* opts; 
    27 } 
    28 QueryCommandSpecification; 
    29  
    30 // Data about commands 
    31 extern QueryCommandSpecification commands[]; 
    32  
    3323typedef enum 
    3424{ 
    35         Command_Quit = 0, 
    36         Command_Exit, 
     25        Command_Unknown = 0, 
     26        Command_Quit, 
    3727        Command_List, 
    3828        Command_pwd, 
     
    5040} 
    5141CommandType; 
     42 
     43typedef enum 
     44{ 
     45        Complete_End = 0, 
     46        Complete_RemoteDir, 
     47        Complete_RemoteFile, 
     48        Complete_LocalDir, 
     49        Complete_LocalFile, 
     50        Complete_LocationName, 
     51        Complete_RemoteIdInCurrentDir, 
     52} 
     53CompletionType; 
     54 
     55typedef struct 
     56{ 
     57        const char* name; 
     58        const char* opts; 
     59        CommandType type; 
     60} 
     61QueryCommandSpecification; 
     62 
     63// Data about commands 
     64extern QueryCommandSpecification commands[]; 
    5265 
    5366extern const char *alias[]; 
     
    7285        BackupQueries(const BackupQueries &); 
    7386public: 
    74  
    75         void DoCommand(const char *Command, bool isFromCommandLine); 
     87        struct ParsedCommand 
     88        { 
     89                std::vector<std::string> cmdElements; 
     90                std::string options; 
     91                std::string completeCommand; 
     92        }; 
     93 
     94        ParsedCommand ParseCommand(const std::string& Command, 
     95                bool isFromCommandLine); 
     96        void DoCommand(ParsedCommand& rCommand); 
     97        CompletionType* GetCompletionTypes(ParsedCommand& rCommand); 
    7698 
    7799        // Ready to stop? 
  • box/trunk/bin/bbackupquery/bbackupquery.cpp

    r2695 r2697  
    6161void PrintUsageAndExit() 
    6262{ 
    63         printf("Usage: bbackupquery [-q*|v*|V|W<level>] [-w] " 
     63        std::ostringstream out; 
     64        out <<  
     65                "Usage: bbackupquery [options] [command]...\n" 
     66                "\n" 
     67                "Options:\n" 
     68                "  -q         Run more quietly, reduce verbosity level by one, can repeat\n" 
     69                "  -Q         Run at minimum verbosity, log nothing\n" 
     70                "  -v         Run more verbosely, increase verbosity level by one, can repeat\n" 
     71                "  -V         Run at maximum verbosity, log everything\n" 
     72                "  -W <level> Set verbosity to error/warning/notice/info/trace/everything\n" 
     73                "  -w         Read/write mode, allow changes to store\n" 
    6474#ifdef WIN32 
    65         "[-u] " 
    66 #endif 
    67         "\n" 
    68         "\t[-c config_file] [-o log_file] [-O log_file_level]\n" 
    69         "\t[-l protocol_log_file] [commands]\n" 
    70         "\n" 
    71         "As many commands as you require.\n" 
    72         "If commands are multiple words, remember to enclose the command in quotes.\n" 
    73         "Remember to use the quit command unless you want to end up in interactive mode.\n"); 
     75                "  -u         Enable Unicode console, requires font change to Lucida Console\n" 
     76#else // !WIN32 
     77                "  -E         Disable interactive command editing, may fix entering intl chars\n" 
     78#endif 
     79                "  -c <file>  Use the specified configuration file. If -c is omitted, the last\n" 
     80                "             argument is the configuration file, or else the default \n" 
     81                "             [" << BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE <<  
     82                "]\n" 
     83                "  -o <file>  Write logging output to specified file as well as console\n" 
     84                "  -O <level> Set file verbosity to error/warning/notice/info/trace/everything\n" 
     85                "  -l <file>  Write protocol debugging logs to specified file\n" 
     86                "\n" 
     87                "Parameters: as many commands as you like. If commands are multiple words,\n" 
     88                "remember to enclose the command in quotes. Remember to use the quit command\n" 
     89                "unless you want to end up in interactive mode.\n"; 
     90        printf("%s", out.str().c_str()); 
    7491        exit(1); 
    7592} 
     
    121138} 
    122139 
     140#ifdef HAVE_RL_COMPLETION_MATCHES 
     141        #define RL_COMPLETION_MATCHES rl_completion_matches 
     142#elif defined HAVE_COMPLETION_MATCHES 
     143        #define RL_COMPLETION_MATCHES completion_matches 
     144#endif 
     145 
    123146char ** bbackupquery_completion(const char *text, int start, int end) 
    124147{ 
     
    131154         * directory. 
    132155         */ 
     156        #ifdef RL_COMPLETION_MATCHES 
    133157        if (start == 0) 
    134158        { 
    135                 #ifdef HAVE_RL_COMPLETION_MATCHES 
    136                         matches = rl_completion_matches(text, 
    137                                 command_generator); 
    138                 #elif defined HAVE_COMPLETION_MATCHES 
    139                         matches = completion_matches(text, command_generator); 
    140                 #endif 
    141         } 
     159                matches = RL_COMPLETION_MATCHES(text, command_generator); 
     160        } 
     161        #endif 
    142162 
    143163        return matches; 
     
    191211        const char* validOpts = "qvVwuc:l:o:O:W:"; 
    192212        bool unicodeConsole = false; 
    193 #else 
    194         const char* validOpts = "qvVwc:l:o:O:W:"; 
     213#elif defined HAVE_LIBREADLINE // && !WIN32 
     214        const char* validOpts = "qvVwEc:l:o:O:W:"; 
     215        bool useReadline = true; 
    195216#endif 
    196217 
     
    286307                case 'u': 
    287308                        unicodeConsole = true; 
     309                        break; 
     310#elif defined HAVE_LIBREADLINE // && !WIN32 
     311                case 'E': 
     312                        useReadline = false; 
    288313                        break; 
    289314#endif 
     
    383408        // 3. Make a protocol, and handshake 
    384409        if(!quiet) BOX_INFO("Handshake with store..."); 
    385         BackupProtocolClient connection(socket); 
     410        std::auto_ptr<BackupProtocolClient> 
     411                apConnection(new BackupProtocolClient(socket)); 
     412        BackupProtocolClient& connection(*(apConnection.get())); 
    386413        connection.Handshake(); 
    387414         
     
    403430        } 
    404431        // Login -- if this fails, the Protocol will exception 
    405         connection.QueryLogin(conf.GetKeyValueInt("AccountNumber"), 
     432        connection.QueryLogin(conf.GetKeyValueUint32("AccountNumber"), 
    406433                (readWrite)?0:(BackupProtocolClientLogin::Flags_ReadOnly)); 
    407434 
     
    417444                while(c < argc && !context.Stop()) 
    418445                { 
    419                         context.DoCommand(argv[c++], true); 
     446                        BackupQueries::ParsedCommand cmd( 
     447                                context.ParseCommand(argv[c++], true)); 
     448                        context.DoCommand(cmd); 
    420449                } 
    421450        } 
     
    424453 
    425454#ifdef HAVE_LIBREADLINE 
    426         // Must initialise the locale before using editline's readline(), 
    427         // otherwise cannot enter international characters. 
    428         if (setlocale(LC_ALL, "") == NULL) 
    429         { 
    430                 BOX_ERROR("Failed to initialise locale. International " 
    431                         "character support may not work."); 
    432         } 
    433  
    434 #ifdef HAVE_READLINE_HISTORY 
    435         using_history(); 
    436 #endif 
    437         /* Allow conditional parsing of the ~/.inputrc file. */ 
    438         rl_readline_name = "bbackupquery"; 
    439  
    440         /* Tell the completer that we want a crack first. */ 
    441         rl_attempted_completion_function = bbackupquery_completion; 
    442  
    443         char *last_cmd = 0; 
    444         while(!context.Stop()) 
    445         { 
    446                 char *command = readline("query > "); 
    447                 if(command == NULL) 
    448                 { 
    449                         // Ctrl-D pressed -- terminate now 
    450                         break; 
    451                 } 
    452                 context.DoCommand(command, false); 
    453                 if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) 
    454                 { 
    455                         free(command); 
    456                 } 
    457                 else 
    458                 { 
    459 #ifdef HAVE_READLINE_HISTORY 
    460                         add_history(command); 
     455        if (useReadline) 
     456        { 
    461457#else 
     458        if (false) 
     459        { 
     460#endif 
     461                // Must initialise the locale before using editline's 
     462                // readline(), otherwise cannot enter international characters. 
     463                if (setlocale(LC_ALL, "") == NULL) 
     464                { 
     465                        BOX_ERROR("Failed to initialise locale. International " 
     466                                "character support may not work."); 
     467                } 
     468 
     469                #ifdef HAVE_READLINE_HISTORY 
     470                        using_history(); 
     471                #endif 
     472 
     473                /* Allow conditional parsing of the ~/.inputrc file. */ 
     474                rl_readline_name = strdup("bbackupquery"); 
     475 
     476                /* Tell the completer that we want a crack first. */ 
     477                rl_attempted_completion_function = bbackupquery_completion; 
     478                 
     479                char *last_cmd = 0; 
     480                while(!context.Stop()) 
     481                { 
     482                        char *command = readline("query > "); 
     483                         
     484                        if(command == NULL) 
     485                        { 
     486                                // Ctrl-D pressed -- terminate now 
     487                                break; 
     488                        } 
     489                         
     490                        BackupQueries::ParsedCommand cmd( 
     491                                context.ParseCommand(command, false)); 
     492                        context.DoCommand(cmd); 
     493                         
     494                        if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) 
     495                        { 
     496                                free(command); 
     497                        } 
     498                        else 
     499                        { 
     500                                #ifdef HAVE_READLINE_HISTORY 
     501                                        add_history(command); 
     502                                #else 
     503                                        free(last_cmd); 
     504                                #endif 
     505                                last_cmd = command; 
     506                        } 
     507                } 
     508                #ifndef HAVE_READLINE_HISTORY 
    462509                        free(last_cmd); 
    463 #endif 
    464                         last_cmd = command; 
    465                 } 
    466         } 
    467 #ifndef HAVE_READLINE_HISTORY 
    468         free(last_cmd); 
    469         last_cmd = 0; 
    470 #endif 
    471 #else 
    472         // Version for platforms which don't have readline by default 
    473         if(fileno(stdin) >= 0) 
    474         { 
    475                 FdGetLine getLine(fileno(stdin)); 
    476                 while(!context.Stop()) 
    477                 { 
    478                         printf("query > "); 
    479                         fflush(stdout); 
    480                         std::string command(getLine.GetLine()); 
    481                         context.DoCommand(command.c_str(), false); 
    482                 } 
    483         } 
    484 #endif 
     510                        last_cmd = 0; 
     511                #endif 
     512        } 
     513        else // !HAVE_LIBREADLINE || !useReadline 
     514        { 
     515                // Version for platforms which don't have readline by default 
     516                if(fileno(stdin) >= 0) 
     517                { 
     518                        FdGetLine getLine(fileno(stdin)); 
     519                        while(!context.Stop()) 
     520                        { 
     521                                printf("query > "); 
     522                                fflush(stdout); 
     523                                std::string command(getLine.GetLine()); 
     524                                BackupQueries::ParsedCommand cmd( 
     525                                        context.ParseCommand(command, false)); 
     526                                context.DoCommand(cmd); 
     527                        } 
     528                } 
     529        } 
    485530         
    486531        // Done... stop nicely 
Note: See TracChangeset for help on using the changeset viewer.