#include "Shell.h" using namespace std; // The following is a hack so that we can write clean // code and still use readline. Fix but only if you grok. COMMAND *cur; shell_state *rl_sh; short int cli_flags; string cli_script_path = ""; short int remote_function_retval = 0; void set_rfr(short int val) { D("remote function retval set to: "<< val); remote_function_retval = val; } char *dupstr( char *s ) { char *r = (char *)malloc(strlen(s)+1); strcpy(r,s); return(r); } char** cmd_completer(const char *text, int start, int end) { char **matches = (char **)NULL; #ifdef HAVE_READLINE_COMPLETION /* If this word is at the start of the line, then it is a command to complete. Otherwise it is the name of a file in the current directory. */ if (start == 0) { rl_completion_append_character = ' '; matches = rl_completion_matches(text, cmd_generator); } else if ( rl_sh->shell_mode == SHELL_NORMAL ) { matches = rl_completion_matches(text, rl_remote_complete); } #endif return matches; } char* cmd_generator(const char *text, int state) { static int list_index, len; char *name; if (!state) { list_index = 0; len = strlen(text); } while (name = cur[list_index].name ) { list_index++; if (strncmp (name, text, len) == 0) return (dupstr(name)); } return ((char *) NULL); } void initialize_readline() { /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = "iPHUC"; // Tell the completer that we want a crack first. rl_attempted_completion_function = cmd_completer; D("rl_attempted_completion_function registered"); /* readline signal handling */ rl_catch_signals = 1; D("rl_catch_signals set"); #ifdef HAVE_READLINE_COMPLETION rl_catch_sigwinch = 1; rl_set_signals(); D("rl_catch_sigwinch set"); #endif } int exec_line( char *line, struct shell_state *sh ) { int t, count; size_t i; COMMAND *command = (COMMAND *)NULL; string word; string *args = new string[MAX_ARGUMENTS]; //strip leading white i = 0; while ( line[i] && line[i] == ' ' ) i++; word = line + i; //run through *line and find spaces //string::size_type index; t = 0; count = 0; for( i = 0 ; i < word.length() && count < (MAX_ARGUMENTS - 1); i++) { if( word[i] == '\\' ) { if ( (i+1) < word.length() ) { i++; args[count] = args[count] + word[i]; } continue; } if( word[i] == ' ' ) { count++; while ( (i+1) < word.length() && word[i+1] == ' ' ) i++; continue; } args[count] = args[count] + word[i]; } // check to see if it's a valid command invocation for (i = 0; sh->command_array[i].name; i++) { if ( args[0].compare( sh->command_array[i].name ) == 0) command = &sh->command_array[i]; } if (!command) { cout << "shell: No such command '" << args[0] << "'" << endl; return -1; } // call function return ((*(command->func)) (args, sh)); delete[] args; } bool dirExists(afc_connection *hAFC, char *path) { afc_directory *hAFCDir; int retval = AFCDirectoryOpen(hAFC, path, &hAFCDir); if(retval == 0) return true; else return false; //Should close dir, but AFCDirectoryClose crashes //AFCDirectoryClose(hAFC, &hAFCDir); } void processRelativePath(string *basePath, string *cdPath) { // NOTE: we assume that basePath is an absolute path. // we also assume that cdPath is NOT an absolute path. // This function is meant to chdir basePath to cdPath string::size_type index = 0; string::size_type length = cdPath->length(); string::size_type bp_end = basePath->length() - 1; if (*cdPath == "" || *cdPath == "." ) return; while( index < length ) { //strip leading '/' while( index < length && cdPath->at(index) == PATH_DELIMITER_CHAR ) index++; //stay in bounds if( index >= length ) break; //Test for 'special' paths if( cdPath->at(index) == '.' ) { index++; if( index >= length ) { } else if( cdPath->at(index) != '.' ) { //its a '.' if ( cdPath->at(index) != PATH_DELIMITER_CHAR ) index--; //break; } else { index++; if( index < length && cdPath->at(index) != PATH_DELIMITER_CHAR ) { // the file name starts with '..' index -= 2; } else { if( *basePath == PATH_DELIMITER_STRING ) { // cant go back on root dir } else { //do a '..' on basePath //remove initial '/' if( basePath->at(bp_end) == PATH_DELIMITER_CHAR ) bp_end--; //remove until you get to another '/' while( bp_end > 0 && basePath->at(bp_end) != PATH_DELIMITER_CHAR ) bp_end--; if( bp_end > 0 ) { *basePath = basePath->substr(0,bp_end); bp_end = basePath->length() - 1; } else { *basePath = PATH_DELIMITER_STRING; bp_end = 0; } } } } } if( basePath->at(bp_end) != PATH_DELIMITER_CHAR ) { *basePath += PATH_DELIMITER_CHAR; bp_end++; } while( index < length && cdPath->at(index) != PATH_DELIMITER_CHAR ) { if( cdPath->at(index) != '\\' ) { *basePath += cdPath->at(index); bp_end++; index++; } else { index++; } } } //if there is a trailing / on the end of cdPath, add it //as long as there isn't already one at the end of basePath if( basePath->at(basePath->length() - 1) != PATH_DELIMITER_CHAR && cdPath->at(cdPath->length() - 1 ) == PATH_DELIMITER_CHAR ) *basePath += PATH_DELIMITER_CHAR; } char *rl_remote_complete(const char *text, int state) { static struct afc_directory *directory = (afc_directory *)NULL; static char *current_directory; static string *temp; char *retval = (char *)NULL; char *ret = (char *)NULL; string::size_type pcount; string t = text; string partial; // this is a new query, reset everything if( !state ) { if (directory != (afc_directory *)NULL) { AFCDirectoryClose(0, directory); if( current_directory) free(current_directory); } //make an absolute path if(temp) delete temp; if( strlen(text) == 0 ) { //assume "." if no relative path is supplied temp = new string( rl_sh->remote_path ); } else { if( text[0] != PATH_DELIMITER_CHAR ) { //if it's not absolute, append it to the //current rl_sh->remote_path temp = new string( rl_sh->remote_path ); processRelativePath(temp, &t); } else { //is already absolute temp = new string( text ); } //here we must remove any incomplete filenames //at the end of temp, so we truncate to the //right-most PATH_DELIMITER_CHAR string::size_type index = temp->rfind(PATH_DELIMITER_CHAR); if (index != 0 && index != string::npos) { if( index != (temp->length()-1) ) { *temp = temp->substr(0,index); *temp += PATH_DELIMITER_STRING; } } else { *temp = PATH_DELIMITER_STRING; } } current_directory = (char *)malloc(temp->length()); current_directory = savestring(temp->c_str()); AFCDirectoryOpen(rl_sh->conn, current_directory, &directory); } pcount = t.rfind(PATH_DELIMITER_CHAR); if( pcount == t.length() ) { AFCDirectoryRead(0, directory, &retval); } else { if( pcount != string::npos ) partial = t.substr(pcount+1); else partial = t; // read the next value from the dir retval = (char*)NULL; while(directory) { char *tempptr; AFCDirectoryRead(0, directory, &tempptr); if( !tempptr ) break; if( !strncmp(tempptr, partial.c_str(), partial.length() ) ) { retval = tempptr; break; } } } if( !retval ) { // clean up if (directory) { AFCDirectoryClose(0, directory); directory = (afc_directory *)NULL; if( current_directory ) free(current_directory); } return (char *)NULL; } else { int len = 1 + strlen(retval) + temp->length(); ret = (char *)malloc( len ); strcpy(ret, temp->c_str() ); strncat(ret, retval, (len - temp->length()) ); struct afc_directory *tmp = (afc_directory*)NULL; if ( !AFCDirectoryOpen(rl_sh->conn, ret, &tmp) ) rl_completion_append_character = PATH_DELIMITER_CHAR; else rl_completion_append_character = ' '; if( tmp ) AFCDirectoryClose(0, tmp); return (ret); } } int run_script(string *path, struct shell_state *sh) { int retval; string command; ifVerbose cout << "shell: Opening script '" << *path << "'" << endl; fstream f( (const char*)path->c_str(), ios::in ); D("script file open."); if(!f.is_open()) { ifNotQuiet cout << "shell: could not open script '" << *path << "'" << endl; return -1; } ifNotQuiet cout << "shell: Reading script." << endl; while( !f.eof() ) { getline( f, command ); D(command); ifVerbose cout << command << endl; retval = exec_line((char *)command.c_str(), sh); if( remote_function_retval ) { ifNotQuiet cout << "Function returned a non-zero value. Halting script." << endl; return SHELL_CONTINUE; } switch( retval ) { case SHELL_CONTINUE: break; case SHELL_TERMINATE: D("run caught SHELL_TERMINATE"); if (sh->shell_mode == SHELL_NORMAL) AFCConnectionClose(sh->conn); f.close(); D("script file closed"); return retval; // AMDeviceStopSession(dev); case SHELL_WAIT: D("run caught SHELL_WAIT"); if (sh->shell_mode == SHELL_NORMAL) AFCConnectionClose(sh->conn); f.close(); D("script file closed"); return retval; // AMDeviceStopSession(dev); default: D("run caught unknown return value"); ifNotQuiet cout << "shell: Function returned unknown error: " << retval << endl; f.close(); D("script file closed"); return retval; } } f.close(); D("script file closed"); return retval; } int shell(struct shell_state *sh) { char *line; string prompt; int retval; ifVerbose cout << "shell: Entering loop." << endl; //initialize readline cur = sh->command_array; //hack rl_sh = sh; //hack D("initializing readline"); initialize_readline(); // If -s script is specified, run it and exit if ( cli_flags & OPT_SCRIPT ) { D("script: " << cli_script_path); return run_script(&cli_script_path, sh); } // oneshot? if ( cli_flags & OPT_ONESHOT ) { D("oneshot: " << cli_script_path); int ret = exec_line((char *)cli_script_path.c_str(), sh); D("command exited: "<< ret); exit(ret); } while (1) { prompt = sh->prompt_string + sh->remote_path+ ": "; line = readline(prompt.c_str()); if (line == (char *)NULL) { ifNotQuiet cout << "shell: Readline error. Probably memory allocation related." << endl; return SHELL_TERMINATE; } else if (line[0] != '\0') { D(line); add_history(line); retval = exec_line(line, sh); } else { //blank line == help D("blank line == help"); line = (char *)malloc(sizeof(char)*5); strcpy(line, "help"); retval = exec_line(line, sh); } switch( retval ) { case SHELL_CONTINUE: free(line); break; case SHELL_TERMINATE: D("caught SHELL_TERMINATE"); free(line); if (sh->shell_mode == SHELL_NORMAL) AFCConnectionClose(sh->conn); // AMDeviceStopSession(dev); return retval; case SHELL_WAIT: D("caught SHELl_WAIT"); free(line); if (sh->shell_mode == SHELL_NORMAL) AFCConnectionClose(sh->conn); // AMDeviceStopSession(dev); return retval; default: D("shell caught unknown return value"); free(line); ifNotQuiet cout << "shell: Function returned unknown error: " << retval << endl; return retval; } } } int sh_run( string *args, struct shell_state *sh) { string path; int retval; if( args[1] == "" ) { ifNotQuiet cout << "run: please specify a script to run" << endl; return SHELL_CONTINUE; } else if ( args[1].at(0) == '/' ) { // assume an abs path path = args[1]; D("absoulte path: " << path ); } else { // assume we want something relative to local_path path = sh->local_path; processRelativePath(&path, &args[1]); args[1] = path; D("relative path: " << path ); } ifVerbose cout << "run: Running script '" << path << "'" << endl; retval = run_script(&path, sh); ifVerbose cout << "run: Script exited with status: " << retval << endl; return SHELL_CONTINUE; } int sh_help(string *args, struct shell_state *sh) { COMMAND *command = (COMMAND *)NULL; if( args[1] == "" ) { cout << "help: Enter a command to display its documentation string." << endl; cout << "Commands accessable in this mode: " << endl; for (int i = 0; sh->command_array[i].doc; i++) { cout << sh->command_array[i].doc << endl; } return SHELL_CONTINUE; } for (int i = 0; sh->command_array[i].name; i++) { if ( args[1].compare( sh->command_array[i].name ) == 0 ) { command = &sh->command_array[i]; break; } } //cout << "help: info for '" << args[1] << "': " << endl; if(command) cout << command->doc << endl; else cout << "help: could not find command '" << args[1] << "'" << endl; return SHELL_CONTINUE; } void setcliflags( short int flags ) { cli_flags = flags; D("Set cli_flags: " << flags); } short int getcliflags() { return cli_flags; } void setscriptpath( char *path ) { cli_script_path = path; } /* -*- mode:c; indent-tabs-mode:nil; c-basic-offset:2; tab-width:2; */