Mercurial > hg > octave-lyh
view src/input.cc @ 581:bc813f5eb025
[project @ 1994-08-07 01:02:15 by jwe]
author | jwe |
---|---|
date | Sun, 07 Aug 1994 01:02:15 +0000 |
parents | 7ea224e713cd |
children | 14b2a186a5c0 |
line wrap: on
line source
// input.cc -*- C++ -*- /* Copyright (C) 1992, 1993, 1994 John W. Eaton This file is part of Octave. Octave is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Octave is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Octave; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ // Use the GNU readline library for command line editing and hisory. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <sys/types.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <time.h> #include <stdio.h> #include <stdlib.h> #include <iostream.h> #include <string.h> #include <assert.h> // This must come before anything that includes iostream.h... extern "C" { #include "readline/readline.h" #include "readline/history.h" extern void free_undo_list (); extern char *xmalloc (); // Yes, this sucks, but it avoids a conflict with another readline // function declared in iostream.h. #if 0 #define LINE_SIZE 8192 static int no_line_editing = 0; #endif char * gnu_readline (char *s) { #if 0 static int state = 0; static char *line_from_stdin = 0; if (no_line_editing) { if (! state) { line_from_stdin = (char *) malloc (LINE_SIZE); state = 1; } fputs ("octave> ", stdout); fgets (line_from_stdin, LINE_SIZE, stdin); return line_from_stdin; } else #endif return readline (s); } } #include "help.h" #include "error.h" #include "utils.h" #include "input.h" #include "pager.h" #include "parse.h" #include "dirfns.h" #include "octave.h" #include "variables.h" #include "tree-const.h" #include "octave-hist.h" #include "sighandlers.h" #include "user-prefs.h" #include "oct-obj.h" #include "defun.h" #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif // The size that strings change by. #ifndef DEFAULT_ARRAY_SIZE #define DEFAULT_ARRAY_SIZE 512 #endif // The growth rate for the prompt string. #ifndef PROMPT_GROWTH #define PROMPT_GROWTH 50 #endif // Global pointer for eval(). const char *current_eval_string = 0; // Nonzero means get input from current_eval_string. int get_input_from_eval_string = 0; // Nonzero means we're parsing a function file. int reading_fcn_file = 0; // Simple name of function file we are reading. char *curr_fcn_file_name = 0; // Nonzero means we're parsing a script file. int reading_script_file = 0; // If we are reading from an M-file, this is it. FILE *ff_instream = 0; // Nonzero means we are using readline. int using_readline = 1; // Nonzero means commands are echoed as they are executed (-x). int echo_input = 0; // Nonzero means this is an interactive shell. int interactive = 0; // Nonzero means the user forced this shell to be interactive (-i). int forced_interactive = 0; // Should we issue a prompt? int promptflag = 1; // The current line of input, from wherever. char *current_input_line = 0; // A line of input from readline. static char *octave_gets_line = 0; extern tree_constant eval_string (const char *string, int print, int ans_assign, int& parse_status); // Append SOURCE to TARGET at INDEX. SIZE is the current amount of // space allocated to TARGET. SOURCE can be NULL, in which case // nothing happens. Gets rid of SOURCE by free ()ing it. Returns // TARGET in case the location has changed. static char * sub_append_string (char *source, char *target, int *index, int *size) { if (source) { while ((int)strlen (source) >= (int)(*size - *index)) { char *tmp = new char [*size += DEFAULT_ARRAY_SIZE]; strcpy (tmp, target); delete [] target; target = tmp; } strcat (target, source); *index += strlen (source); delete [] source; } return target; } // Return the octal number parsed from STRING, or -1 to indicate that // the string contained a bad number. int read_octal (const char *string) { int result = 0; int digits = 0; while (*string && *string >= '0' && *string < '8') { digits++; result = (result * 8) + *string++ - '0'; } if (! digits || result > 0777 || *string) result = -1; return result; } // Return a string which will be printed as a prompt. The string may // contain special characters which are decoded as follows: // // \t the time // \d the date // \n CRLF // \s the name of the shell (program) // \w the current working directory // \W the last element of PWD // \u your username // \h the hostname // \# the command number of this command // \! the history number of this command // \$ a $ or a # if you are root // \<octal> character code in octal // \\ a backslash static char * decode_prompt_string (const char *string) { int result_size = PROMPT_GROWTH; int result_index = 0; char *result = new char [PROMPT_GROWTH]; int c; char *temp = 0; result[0] = 0; while (c = *string++) { if (c == '\\') { c = *string; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { char octal_string[4]; int n; strncpy (octal_string, string, 3); octal_string[3] = '\0'; n = read_octal (octal_string); temp = strsave ("\\"); if (n != -1) { string += 3; temp[0] = n; } c = 0; goto add_string; } case 't': case 'd': /* Make the current time/date into a string. */ { time_t the_time = time (0); char *ttemp = ctime (&the_time); temp = strsave (ttemp); if (c == 't') { strcpy (temp, temp + 11); temp[8] = '\0'; } else temp[10] = '\0'; goto add_string; } case 'n': if (! no_line_editing) temp = strsave ("\r\n"); else temp = strsave ("\n"); goto add_string; case 's': { temp = base_pathname (prog_name); temp = strsave (temp); goto add_string; } case 'w': case 'W': { char t_string[MAXPATHLEN]; #define EFFICIENT #ifdef EFFICIENT // Use the value of PWD because it is much more effecient. temp = user_pref.pwd; if (! temp) getcwd (t_string, MAXPATHLEN); else strcpy (t_string, temp); #else getcwd (t_string, MAXPATHLEN); #endif /* EFFICIENT */ if (c == 'W') { char *dir = strrchr (t_string, '/'); if (dir && dir != t_string) strcpy (t_string, dir + 1); temp = strsave (t_string); } else temp = strsave (polite_directory_format (t_string)); goto add_string; } case 'u': { temp = strsave (user_name); goto add_string; } case 'h': { char *t_string; temp = strsave (host_name); if (t_string = strchr (temp, '.')) *t_string = '\0'; goto add_string; } case '#': { char number_buffer[128]; sprintf (number_buffer, "%d", current_command_number); temp = strsave (number_buffer); goto add_string; } case '!': { char number_buffer[128]; int num = current_history_number (); if (num > 0) sprintf (number_buffer, "%d", num); else strcpy (number_buffer, "!"); temp = strsave (number_buffer); goto add_string; } case '$': temp = strsave (geteuid () == 0 ? "#" : "$"); goto add_string; case '\\': temp = strsave ("\\"); goto add_string; default: temp = strsave ("\\ "); temp[1] = c; add_string: if (c) string++; result = (char *)sub_append_string (temp, result, &result_index, &result_size); temp = 0; // Free ()'ed in sub_append_string (). result[result_index] = '\0'; break; } } else { while (3 + result_index > result_size) { char *tmp = new char [result_size += PROMPT_GROWTH]; strcpy (tmp, result); delete [] result; result = tmp; } result[result_index++] = c; result[result_index] = '\0'; } } #if 0 // I don't really think that this is a good idea. Do you? if (! find_variable ("NO_PROMPT_VARS")) { WORD_LIST *expand_string (), *list; char *string_list (); list = expand_string (result, 1); free (result); result = string_list (list); dispose_words (list); } #endif return result; } // Use GNU readline to get an input line and store it in the history // list. static char * octave_gets (void) { if (octave_gets_line) { free (octave_gets_line); octave_gets_line = 0; } if (interactive || forced_interactive) { char *ps = (promptflag > 0) ? user_pref.ps1 : user_pref.ps2; char *prompt = decode_prompt_string (ps); if (interactive) { pipe_handler_error_count = 0; flush_output_to_pager (); } maybe_write_to_diary_file (prompt); octave_gets_line = gnu_readline (prompt); delete [] prompt; } else octave_gets_line = gnu_readline (""); current_input_line = octave_gets_line; if (octave_gets_line && *octave_gets_line) { maybe_save_history (octave_gets_line); maybe_write_to_diary_file (octave_gets_line); if (echo_input) { if (! forced_interactive) cout << "+ "; cout << octave_gets_line << "\n"; } } maybe_write_to_diary_file ("\n"); return octave_gets_line; } // Read a line from the input stream. int octave_read (char *buf, int max_size) { int status = 0; static char *stashed_line = 0; if (get_input_from_eval_string) { int len = strlen (current_eval_string); if (len < max_size - 1) { strcpy (buf, current_eval_string); buf[len++] = '\n'; buf[len] = '\0'; // Paranoia. status = len; } else status = -1; if (stashed_line) delete [] stashed_line; stashed_line = strsave (buf); current_input_line = stashed_line; } else if (using_readline) { char *cp = octave_gets (); if (cp) { int len = strlen (cp); if (len >= max_size) status = -1; else { strcpy (buf, cp); buf[len++] = '\n'; buf[len] = '\0'; // Paranoia. status = len; } } current_input_line = cp; } else { FILE *curr_stream = rl_instream; if (reading_fcn_file || reading_script_file) curr_stream = ff_instream; assert (curr_stream); // Why is this required? buf[0] = '\0'; if (fgets (buf, max_size, curr_stream)) { int len = strlen (buf); if (len > max_size - 2) status = -1; else { if (buf[len-1] != '\n') { buf[len++] = '\n'; buf[len] = '\0'; } status = len; } } else status = 0; // Tell yylex that we found EOF. if (stashed_line) delete [] stashed_line; stashed_line = strsave (buf); current_input_line = stashed_line; if (echo_input && current_input_line && *current_input_line) { if (! forced_interactive) cout << "+ "; cout << current_input_line << "\n"; } } input_line_number++; return status; } // Fix things up so that input can come from file `name', printing a // warning if the file doesn't exist. FILE * get_input_from_file (char *name, int warn) { FILE *instream = 0; if (name && *name) instream = fopen (name, "r"); if (! instream && warn) warning ("%s: no such file or directory", name); if (reading_fcn_file || reading_script_file) ff_instream = instream; else rl_instream = instream; return instream; } // Fix things up so that input can come from the standard input. This // may need to become much more complicated, which is why it's in a // separate function. FILE * get_input_from_stdin (void) { rl_instream = stdin; return rl_instream; } static char * command_generator (char *text, int state) { static int len = 0; static int list_index = 0; static char **name_list = 0; if (state == 0) { list_index = 0; len = strlen (text); if (name_list) { char **ptr = name_list; while (ptr && *ptr) delete [] *ptr++; delete [] name_list; } name_list = make_name_list (); } char *name; while ((name = name_list[list_index]) != 0) { list_index++; if (strncmp (name, text, len) == 0) { char *buf = xmalloc (1 + strlen (name)); strcpy (buf, name); return buf; } } return 0; } static char ** command_completer (char *text, int start, int end) { char **matches = 0; matches = completion_matches (text, command_generator); return matches; } // The next two functions implement the equivalent of the K*rn shell // C-o operate-and-get-next-history-line editing command. Stolen from // the GNU Bourne Again SHell. // ?? static int saved_history_line_to_use = 0; // ?? static Function *old_rl_startup_hook = 0; static void set_saved_history (void) { HIST_ENTRY *h; if (saved_history_line_to_use) { if (history_set_pos (saved_history_line_to_use)) { h = current_history (); if (h) { rl_insert_text (h->line); // Get rid of any undo list created by the previous insert, so the // line won't totally be erased when the edits are undone (they will // be normally, because this is a history line -- cf. readline.c: // line 380 or so). if (rl_undo_list) { free_undo_list (); rl_undo_list = 0; } } } } saved_history_line_to_use = 0; rl_startup_hook = old_rl_startup_hook; } static void operate_and_get_next (int count, int c) { int where; extern int history_stifled, history_length, max_input_history; // Accept the current line. rl_newline (); // Find the current line, and find the next line to use. where = where_history (); if (history_stifled && (history_length >= max_input_history)) saved_history_line_to_use = where; else saved_history_line_to_use = where + 1; old_rl_startup_hook = rl_startup_hook; rl_startup_hook = (Function *) set_saved_history; } void initialize_readline (void) { // Allow conditional parsing of the ~/.inputrc file rl_readline_name = "Octave"; // Tell the completer that we want to try first. rl_attempted_completion_function = (CPPFunction *) command_completer; // Bind operate-and-get-next. rl_add_defun ("operate-and-get-next", (Function *) operate_and_get_next, CTRL ('O')); } static int match_sans_spaces (const char *standard, const char *test) { const char *tp = test; while (*tp == ' ' || *tp == '\t') tp++; const char *ep = test + strlen (test) - 1; while (*ep == ' ' || *ep == '\t') ep--; int len = ep - tp + 1; return (strncmp (standard, tp, len) == 0); } // If the user simply hits return, this will produce an empty matrix. static Octave_object get_user_input (const Octave_object& args, int nargout, int debug = 0) { tree_constant retval; int nargin = args.length (); int read_as_string = 0; if (nargin == 3) read_as_string++; char *prompt = "debug> "; if (nargin > 1) { if (args(1).is_string_type ()) prompt = args(1).string_value (); else { error ("input: unrecognized argument"); return retval; } } again: flush_output_to_pager (); char *input_buf = gnu_readline (prompt); if (input_buf) { maybe_save_history (input_buf); int len = strlen (input_buf); if (len < 1) { if (debug) goto again; else return retval; } if (match_sans_spaces ("exit", input_buf) || match_sans_spaces ("quit", input_buf) || match_sans_spaces ("return", input_buf)) return tree_constant (); else if (read_as_string) retval = input_buf; else { int parse_status = 0; retval = eval_string (input_buf, 0, 0, parse_status); if (retval.is_defined ()) { if (debug) retval.eval (1); } else retval = tree_constant (Matrix ()); } } else error ("input: reading user-input failed!"); if (debug) goto again; return retval; } DEFUN ("input", Finput, Sinput, 3, 1, "input (PROMPT [, S])\n\ \n\ Prompt user for input. If the second argument is present, return value as a string.") { Octave_object retval; int nargin = args.length (); if (nargin == 2 || nargin == 3) retval = get_user_input (args, nargout); else print_usage ("input"); return retval; } DEFUN ("keyboard", Fkeyboard, Skeyboard, 2, 1, "keyboard (PROMPT)\n\ \n\ maybe help in debugging function files") { Octave_object retval; int nargin = args.length (); if (nargin == 1 || nargin == 2) retval = get_user_input (args, nargout, 1); else print_usage ("keyboard"); return retval; } /* ;;; Local Variables: *** ;;; mode: C++ *** ;;; page-delimiter: "^/\\*" *** ;;; End: *** */