changeset 1489:3e705c864019

[project @ 1995-09-28 05:38:26 by jwe]
author jwe
date Thu, 28 Sep 1995 05:38:26 +0000
parents 89c587478067
children 4d6c168ff235
files src/dirfns.cc src/error.cc src/error.h src/help.cc src/lex.l src/octave.cc src/parse.y src/pt-cmd.cc src/pt-cmd.h src/token.h src/variables.cc src/variables.h
diffstat 12 files changed, 377 insertions(+), 113 deletions(-) [+]
line wrap: on
line diff
--- a/src/dirfns.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/dirfns.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -507,7 +507,11 @@
 	}
       else
 	{
-	  DIR *dir = opendir (dirname);
+	  char *tmp = tilde_expand (dirname);
+
+	  DIR *dir = opendir (tmp);
+
+	  free (tmp);
 
 	  if (dir)
 	    {
@@ -579,10 +583,19 @@
 
       if (error_state)
 	gripe_wrong_type_arg ("mkdir", args(0));
-      else if (mkdir (dirname, 0777) < 0)
+      else
 	{
-	  status = -1;
-	  error ("%s", strerror (errno));
+	  char *tmp = tilde_expand (dirname);
+
+	  int mkdir_retval = mkdir (tmp, 0777);
+
+	  free (tmp);
+
+	  if (mkdir_retval < 0)
+	    {
+	      status = -1;
+	      error ("%s", strerror (errno));
+	    }
 	}
     }
   else
@@ -610,10 +623,19 @@
 
       if (error_state)
 	gripe_wrong_type_arg ("rmdir", args(0));
-      else if (rmdir (dirname) < 0)
+      else
 	{
-	  status = -1;
-	  error ("%s", strerror (errno));
+	  char *tmp = tilde_expand (dirname);
+
+	  int rmdir_retval = rmdir (tmp);
+
+	  free (tmp);
+
+	  if (rmdir_retval < 0)
+	    {
+	      status = -1;
+	      error ("%s", strerror (errno));
+	    }
 	}
     }
   else
--- a/src/error.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/error.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -40,8 +40,13 @@
 // Current error state.
 int error_state = 0;
 
-// XXX FIXME XXX
-int suppress_octave_error_messages = 0;
+// Tell the error handler whether to print messages, or just store
+// them for later.  Used for handling errors in eval() and
+// the `unwind_protect' statement.
+int buffer_error_messages;
+
+// The message buffer
+ostrstream *error_message_buffer = 0;
 
 static void
 verror (const char *name, const char *fmt, va_list args)
@@ -50,13 +55,6 @@
 
   int to_beep_or_not_to_beep = user_pref.beep_on_error && ! error_state;
 
-  if (to_beep_or_not_to_beep)
-    cerr << "\a";
-  if (name)
-    cerr << name << ": ";
-  cerr.vform (fmt, args);
-  cerr << endl;
-
   ostrstream output_buf;
 
   if (to_beep_or_not_to_beep)
@@ -68,7 +66,32 @@
 
   char *msg = output_buf.str ();
 
-  maybe_write_to_diary_file (msg);
+  if (buffer_error_messages)
+    {
+      char *ptr = msg;
+
+      if (! error_message_buffer)
+	{
+	  error_message_buffer = new ostrstream;
+
+	  // XXX FIXME XXX -- this is ugly, but it prevents
+	  //
+	  //   eval ("error (\"msg\")", "error (__error_text__)");
+	  //
+	  // from printing `error: ' twice.  Assumes that the NAME we
+	  // have been given doesn't contain `:'.
+
+	  ptr = strchr (msg, ':') + 2;
+	  ptr = ptr ? ptr : msg;	  
+	}
+
+      *error_message_buffer << ptr;
+    }
+  else
+    {
+      maybe_write_to_diary_file (msg);
+      cerr << msg;
+    }
 
   delete [] msg;
 }
@@ -82,32 +105,29 @@
 {
   if (error_state != -2)
     {
-      if (! suppress_octave_error_messages)
+      if (fmt)
 	{
-	  if (fmt)
+	  if (*fmt)
 	    {
-	      if (*fmt)
+	      int len = strlen (fmt);
+	      if (fmt[len - 1] == '\n')
 		{
-		  int len = strlen (fmt);
-		  if (fmt[len - 1] == '\n')
+		  if (len > 1)
 		    {
-		      if (len > 1)
-			{
-			  char *tmp_fmt = strsave (fmt);
-			  tmp_fmt[len - 1] = '\0';
-			  verror (name, tmp_fmt, args);
-			  delete [] tmp_fmt;
-			}
+		      char *tmp_fmt = strsave (fmt);
+		      tmp_fmt[len - 1] = '\0';
+		      verror (name, tmp_fmt, args);
+		      delete [] tmp_fmt;
+		    }
 
-		      error_state = -2;
-		    }
-		  else
-		    verror (name, fmt, args);
+		  error_state = -2;
 		}
+	      else
+		verror (name, fmt, args);
 	    }
-	  else
-	    panic ("error_1: invalid format");
 	}
+      else
+	panic ("error_1: invalid format");
 
       if (! error_state)
 	error_state = 1;
@@ -172,94 +192,79 @@
   abort ();
 }
 
-DEFUN ("error", Ferror, Serror, 10,
-  "error (MESSAGE): print MESSAGE and set the error state.\n\
-This should eventually take us up to the top level, possibly\n\
-printing traceback messages as we go.\n\
-\n\
-If MESSAGE ends in a newline character, traceback messages are not\n\
-printed.") 
+typedef void (*error_fun)(const char *, ...);
+
+extern Octave_object Fsprintf (const Octave_object&, int);
+
+static Octave_object
+handle_message (error_fun f, const char *msg, const Octave_object& args)
 {
   Octave_object retval;
 
-  const char *msg = "unspecified error";
-
   int nargin = args.length ();
 
-  if (nargin == 1 && args(0).is_defined ())
+  tree_constant arg = ((nargin > 1) ? Fsprintf (args, 1) : args) (0);
+
+  if (arg.is_defined ())
     {
-      if (args(0).is_string ())
+      if (arg.is_string ())
 	{
-	  msg = args(0).string_value ();
+	  msg = arg.string_value ();
 
 	  if (! msg)
 	    return retval;
 	}
-      else if (args(0).is_empty ())
+      else if (arg.is_empty ())
 	return retval;
     }
 
-  error (msg);
+// Ugh.
+
+  int len = strlen (msg);
+  if (msg[len - 1] == '\n')
+    {
+      if (len > 1)
+	{
+	  char *tmp_msg = strsave (msg);
+	  tmp_msg[len - 1] = '\0';
+	  f ("%s\n", tmp_msg);
+	  delete [] tmp_msg;
+	}
+    }
+  else
+    f ("%s", msg);
 
   return retval;
 }
 
-DEFUN ("warning", Fwarning, Swarning, 10,
-  "warning (MESSAGE): print a warning MESSAGE.\n\
+DEFUN ("error", Ferror, Serror, 10,
+  "error (FMT, ...): print message according to FMT and set error state.\n\
 \n\
-See also: error")
+This should eventually take us up to the top level, possibly\n\
+printing traceback messages as we go.\n\
+\n\
+If MESSAGE ends in a newline character, traceback messages are not\n\
+printed.\n\
+\n\
+See also: printf") 
 {
-  Octave_object retval;
-
-  const char *msg = "unspecified warning";
-
-  int nargin = args.length ();
+  return handle_message (error, "unspecified error", args);
+}
 
-  if (nargin == 1 && args(0).is_defined ())
-    {
-      if (args(0).is_string ())
-	{
-	  msg = args(0).string_value ();
-
-	  if (! msg || ! *msg)
-	    return retval;
-	}
-      else if (args(0).is_empty ())
-	return retval;
-    }
-
-  warning (msg);
-
-  return retval;
+DEFUN ("warning", Fwarning, Swarning, 10,
+  "warning (FMT, ...): print a warning message according to FMT.\n\
+\n\
+See also: error, printf")
+{
+  return handle_message (warning, "unspecified warning", args);
 }
 
 DEFUN ("usage", Fusage, Susage, 10,
-  "usage (MESSAGE): print a usage MESSAGE.\n\
+  "usage (FMT, ...): print a usage message according to FMT.\n\
 \n\
-See also: error")
+See also: error, printf")
 {
-  Octave_object retval;
-
-  const char *msg = "unknown";
-
-  int nargin = args.length ();
-
-  if (nargin == 1 && args(0).is_defined ())
-    {
-      if (args(0).is_string ())
-	{
-	  msg = args(0).string_value ();
-
-	  if (! msg || ! *msg)
-	    return retval;
-	}
-      else if (args(0).is_empty ())
-	return retval;
-    }
-
-  usage (msg);
-
-  return retval;
+  return handle_message (usage, "unknown", args);
 }
 
 /*
--- a/src/error.h	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/error.h	Thu Sep 28 05:38:26 1995 +0000
@@ -24,6 +24,8 @@
 #if !defined (octave_error_h)
 #define octave_error_h 1
 
+class ostrstream;
+
 #define panic_impossible() \
   panic ("impossible state reached in file `%s' at line %d", \
 	 __FILE__, __LINE__)
@@ -38,8 +40,13 @@
 // Current error state.
 extern int error_state;
 
-// XXX FIXME XXX
-extern int suppress_octave_error_messages;
+// Tell the error handler whether to print messages, or just store
+// them for later.  Used for handling errors in eval() and
+// the `unwind_protect' statement.
+extern int buffer_error_messages;
+
+// The message buffer
+extern ostrstream *error_message_buffer;
 
 #endif
 
--- a/src/help.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/help.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -195,6 +195,9 @@
   { "break",
     "Exit the innermost enclosing while or for loop.", },
 
+  { "catch",
+    "begin the cleanup part of a try-catch block", },
+
   { "continue",
     "Jump to the end of the innermost enclosing while or for loop.", },
 
@@ -207,6 +210,9 @@
   { "end",
     "Mark the end of any for, if, while, or function block.", },
 
+  { "end_try_catch",
+    "Mark the end of an try-catch block.", }, 
+
   { "end_unwind_protect",
     "Mark the end of an unwind_protect block.", }, 
 
@@ -243,6 +249,9 @@
   { "return",
     "Return from a function.", },
 
+  { "try",
+    "Begin a try-catch block.", }, 
+
   { "unwind_protect",
     "Begin an unwind_protect block.", }, 
 
--- a/src/lex.l	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/lex.l	Thu Sep 28 05:38:26 1995 +0000
@@ -1092,6 +1092,25 @@
       yylval.tok_val = new token (token::unwind_protect_end, l, c);
       token_stack.push (yylval.tok_val);
     }
+  else if (strcmp ("try", s) == 0)
+    {
+      promptflag--;
+      yylval.tok_val = new token (l, c);
+      token_stack.push (yylval.tok_val);
+      return TRY;
+    }
+  else if (strcmp ("catch", s) == 0)
+    {
+      yylval.tok_val = new token (l, c);
+      token_stack.push (yylval.tok_val);
+      return CATCH;
+    }
+  else if (strcmp ("end_try_catch", s) == 0)
+    {
+      end_found = 1;
+      yylval.tok_val = new token (token::try_catch_end, l, c);
+      token_stack.push (yylval.tok_val);
+    }
   else if (strcmp ("all_va_args", s) == 0)
     {
       yylval.tok_val = new token (l, c);
--- a/src/octave.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/octave.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -979,8 +979,8 @@
 
       if (nargin > 1)
 	{
-	  unwind_protect_int (suppress_octave_error_messages);
-	  suppress_octave_error_messages = 1;
+	  unwind_protect_int (buffer_error_messages);
+	  buffer_error_messages = 1;
 	}
 
       int parse_status = 0;
@@ -990,7 +990,16 @@
       if (nargin > 1 && (parse_status != 0 || error_state))
 	{
 	  error_state = 0;
+
+	  // Set up for letting the user print any messages from
+	  // errors that occurred in the first part of this eval().
+
+	  buffer_error_messages = 0;
+	  bind_global_error_variable ();
+	  add_unwind_protect (clear_global_error_variable, 0);
+
 	  eval_string (args(1), parse_status, nargout);
+
 	  retval = Octave_object ();
 	}
 
--- a/src/parse.y	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/parse.y	Thu Sep 28 05:38:26 1995 +0000
@@ -223,6 +223,7 @@
 %token <tok_val> TEXT STYLE
 %token <tok_val> FOR WHILE IF ELSEIF ELSE BREAK CONTINUE FUNC_RET
 %token <tok_val> UNWIND CLEANUP
+%token <tok_val> TRY CATCH
 %token <tok_val> GLOBAL
 %token <tok_val> TEXT_ID
 
@@ -612,6 +613,14 @@
 		    $$ = new tree_unwind_protect_command ($3, $6, $1->line (),
 							  $1->column ());
 		  }
+		| TRY optsep opt_list CATCH optsep opt_list END
+		  {
+		    if (check_end ($7, token::try_catch_end))
+		      ABORT_PARSE;
+
+		    $$ = new tree_try_catch_command ($3, $6, $1->line (),
+						     $1->column ());
+		  }
 		| WHILE expression optsep opt_list END
 		  {
 		    maybe_warn_assign_as_truth_value ($2);
@@ -1318,6 +1327,14 @@
 	  end_error ("if", ettype, l, c);
 	  break;
 
+	case token::try_catch_end:
+	  end_error ("try", ettype, l, c);
+	  break;
+
+	case token::unwind_protect_end:
+	  end_error ("unwind_protect", ettype, l, c);
+	  break;
+
 	case token::while_end:
 	  end_error ("while", ettype, l, c);
 	  break;
--- a/src/pt-cmd.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/pt-cmd.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -638,6 +638,123 @@
 
 // Simple exception handling.
 
+tree_try_catch_command::~tree_try_catch_command (void)
+{
+  delete try_code;
+  delete catch_code;
+}
+
+static void
+do_catch_code (void *ptr)
+{
+  tree_statement_list *list = (tree_statement_list *) ptr;
+
+  // Set up for letting the user print any messages from errors that
+  // occurred in the body of the try_catch statement.
+
+  buffer_error_messages = 0;
+  bind_global_error_variable ();
+  add_unwind_protect (clear_global_error_variable, 0);
+
+  // Similarly, if we have seen a return or break statement, allow all
+  // the catch code to run before returning or handling the break.
+  // We don't have to worry about continue statements because they can
+  // only occur in loops.
+
+  unwind_protect_int (returning);
+  returning = 0;
+
+  unwind_protect_int (breaking);
+  breaking = 0;
+
+  if (list)
+    list->eval (1);
+
+  // This is the one for breaking.  (The unwind_protects are popped
+  // off the stack in the reverse of the order they are pushed on).
+
+  // XXX FIXME XXX -- inside a try-catch, should break work like
+  // a return, or just jump to the end of the try_catch block?
+  // The following code makes it just jump to the end of the block.
+
+  run_unwind_protect ();
+  if (breaking)
+    breaking--;
+
+  // This is the one for returning.
+
+  if (returning)
+    discard_unwind_protect ();
+  else
+    run_unwind_protect ();
+
+  run_unwind_protect ();
+}
+
+void
+tree_try_catch_command::eval (void)
+{
+  begin_unwind_frame ("tree_try_catch::eval");
+
+  add_unwind_protect (do_catch_code, catch_code);
+
+  if (catch_code)
+    {
+      unwind_protect_int (buffer_error_messages);
+      buffer_error_messages = 1;
+    }
+
+  if (try_code)
+    try_code->eval (1);
+
+  if (catch_code && error_state)
+    {
+      error_state = 0;
+      run_unwind_frame ("tree_try_catch::eval");
+    }
+  else
+    {
+      error_state = 0;
+      discard_unwind_frame ("tree_try_catch::eval");
+    }
+}
+
+void
+tree_try_catch_command::print_code (ostream& os)
+{
+  print_code_indent (os);
+
+  os << "try_catch";
+
+  print_code_new_line (os);
+
+  if (try_code)
+    {
+      increment_indent_level ();
+      try_code->print_code (os);
+      decrement_indent_level ();
+    }
+
+  print_code_indent (os);
+
+  os << "catch_code";
+
+  print_code_new_line (os);
+
+  if (catch_code)
+    {
+      increment_indent_level ();
+      catch_code->print_code (os);
+      decrement_indent_level ();
+    }
+
+  print_code_indent (os);
+
+  os << "end_try_catch";
+}
+
+// Simple exception handling.
+
 tree_unwind_protect_command::~tree_unwind_protect_command (void)
 {
   delete unwind_protect_code;
@@ -702,23 +819,12 @@
 void
 tree_unwind_protect_command::eval (void)
 {
-  begin_unwind_frame ("tree_unwind_protect::eval");
-
   add_unwind_protect (do_unwind_protect_cleanup_code, cleanup_code);
 
-  if (cleanup_code)
-    {
-      unwind_protect_int (suppress_octave_error_messages);
-      suppress_octave_error_messages = 1;
-    }
-
   if (unwind_protect_code)
     unwind_protect_code->eval (1);
 
-  if (cleanup_code && error_state)
-    error_state = 0;
-
-  run_unwind_frame ("tree_unwind_protect::eval");
+  run_unwind_protect ();
 }
 
 void
--- a/src/pt-cmd.h	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/pt-cmd.h	Thu Sep 28 05:38:26 1995 +0000
@@ -47,6 +47,7 @@
 class tree_while_command;
 class tree_for_command;
 class tree_if_command;
+class tree_try_catch_command;
 class tree_unwind_protect_command;
 class tree_break_command;
 class tree_continue_command;
@@ -243,6 +244,38 @@
   tree_statement_list *cleanup_code;
 };
 
+// Simple exception handling.
+
+class
+tree_try_catch_command : public tree_command
+{
+public:
+  tree_try_catch_command (int l = -1, int c = -1) : tree_command (l, c)
+    {
+      try_code = 0;
+      catch_code = 0;
+    }
+
+  tree_try_catch_command (tree_statement_list *tc,
+			       tree_statement_list *cc,
+			       int l = -1, int c = -1)
+    : tree_command (l, c)
+      {
+	try_code = tc;
+	catch_code = cc;
+      }
+
+  ~tree_try_catch_command (void);
+
+  void eval (void);
+
+  void print_code (ostream& os);
+
+private:
+  tree_statement_list *try_code;
+  tree_statement_list *catch_code;
+};
+
 // Break.
 
 class
--- a/src/token.h	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/token.h	Thu Sep 28 05:38:26 1995 +0000
@@ -51,6 +51,7 @@
       function_end,
       if_end,
       while_end,
+      try_catch_end,
       unwind_protect_end,
     };
 
--- a/src/variables.cc	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/variables.cc	Thu Sep 28 05:38:26 1995 +0000
@@ -1479,6 +1479,31 @@
   tmp_ass.eval (print);
 }
 
+void
+bind_global_error_variable (void)
+{
+  *error_message_buffer << ends;
+
+  char *error_text = error_message_buffer->str ();
+
+  bind_builtin_variable ("__error_text__", error_text, 1);
+
+  delete [] error_text;
+
+  delete error_message_buffer;
+
+  error_message_buffer = 0;
+}
+
+void
+clear_global_error_variable (void *)
+{
+  delete error_message_buffer;
+  error_message_buffer = 0;
+
+  bind_builtin_variable ("__error_text__", "", 1);
+}
+
 // Give a global variable a definition.  This will insert the symbol
 // in the global table if necessary.
 
@@ -1605,6 +1630,13 @@
     "the string to append after successful command-line completion\n\
 attempts");
 
+  DEFCONST ("error_text", SBV_current_error_text, "", 0, 0,
+    "the text of error messages that would have been printed in the
+body of the most recent unwind_protect statement or the TRY part of\n\
+the most recent eval() command.  Outside of unwind_protect and\n\
+eval(), or if no error has ocurred within them, the value of\n\
+__error_text__ is guaranteed to be the empty string.");
+
   DEFVAR ("default_return_value", SBV_default_return_value, Matrix (),
 	  0, 0,
     "the default for value for unitialized variables returned from\n\
--- a/src/variables.h	Tue Sep 26 22:49:48 1995 +0000
+++ b/src/variables.h	Thu Sep 28 05:38:26 1995 +0000
@@ -108,6 +108,10 @@
 
 extern void bind_ans (const tree_constant& val, int print);
 
+extern void bind_global_error_variable (void);
+
+extern void clear_global_error_variable (void *);
+
 extern void bind_builtin_variable (const char *, tree_constant *,
 				   int protect = 0, int eternal = 0,
 				   sv_Function f = (sv_Function) 0,